Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/facepunch/sbox-public/llms.txt

Use this file to discover all available pages before exploring further.

The s&box audio system lets you play sounds from code using Sound.Play(), attach looping or triggered sounds to GameObjects with SoundPointComponent and SoundBoxComponent, track and control playing sounds through SoundHandle, and organize output levels with Audio.Mixer. This page covers each of these APIs and how they fit together.

Quick start: playing a sound

The fastest way to play a sound from code is Sound.Play(). Pass a SoundEvent asset name or a SoundEvent reference.
// Play a sound event by name (non-positional)
SoundHandle handle = Sound.Play( "sounds/footstep.generic" );

// Play at a world-space position (3D spatialized)
SoundHandle handle = Sound.Play( "sounds/explosion.generic", WorldPosition );

// Play using a SoundEvent asset reference
[Property] SoundEvent HitSound;

void OnHit()
{
    Sound.Play( HitSound, WorldPosition );
}
Sound.Play returns SoundHandle.Empty on a headless/dedicated server. Always check handle.IsValid if you intend to modify the handle after playing.

SoundHandle

SoundHandle is a reference to a sound that is currently playing. Use it to update position, volume, pitch, or stop the sound.
SoundHandle _engine;

protected override void OnStart()
{
    _engine = Sound.Play( "sounds/engine.loop" );
    _engine.Position = WorldPosition;
    _engine.Volume   = 0.8f;
    _engine.Pitch    = 1.0f;
}

protected override void OnUpdate()
{
    if ( _engine.IsValid() )
    {
        // Keep position in sync with the vehicle
        _engine.Position = WorldPosition;

        // Pitch the sound by speed
        _engine.Pitch = 0.8f + (_speed / MaxSpeed) * 0.4f;
    }
}

protected override void OnDestroy()
{
    _engine?.Stop( fadeTime: 0.5f );
}

Key SoundHandle properties

PropertyTypeDescription
PositionVector3World-space position used for 3D spatialization.
VolumefloatPlayback volume. Range 0–1.
PitchfloatPlayback pitch. 1.0 is normal speed.
IsPlayingboolTrue while the sound has not been stopped.
PausedboolPause or resume playback without releasing the handle.
DistancefloatMaximum audible distance in world units (default 15000).
FalloffCurveDistance attenuation curve.
DistanceAttenuationboolWhether volume falls off with distance.
AirAbsorptionboolHigh frequencies attenuate at distance.
OcclusionboolGeometry occludes the sound.
OcclusionRadiusfloatSphere radius for partial occlusion.
TransmissionboolSound transmits through walls.
ListenLocalboolWhen true, plays as a 2D (UI) sound ignoring position.
SpacialBlendfloat0 = fully 2D, 1 = fully 3D.
TargetMixerMixerRoutes this sound to a specific mixer channel.
AmplitudefloatCurrent loudness level — useful for lip-sync or VU meters.
TimefloatCurrent playback position in seconds. Seek by assigning.

Stopping with a fade

handle.Stop( fadeTime: 1.0f ); // fade out over 1 second
handle.Stop();                  // stop immediately

Preloading

Preload a sound event before it is needed to avoid a stutter on first play:
protected override void OnStart()
{
    Sound.Preload( "sounds/gunshot.rifle" );
}

SoundPointComponent

SoundPointComponent is a scene component that plays a sound at the GameObject’s world position. It updates the position every frame automatically, so attaching it to a moving object keeps the audio in sync. Add it in the editor under Audio → Sound Point, or attach it in code:
var spc = GameObject.Components.Create<SoundPointComponent>();
spc.SoundEvent = ResourceLibrary.Get<SoundEvent>( "sounds/fire.loop" );
spc.PlayOnStart = true;
spc.Volume = 0.75f;

Properties

PropertyDescription
SoundEventThe sound event asset to play.
PlayOnStartStart playing automatically when the component is enabled.
StopOnNewStop the current sound before playing a new one when StartSound() is called.
VolumePlayback volume override (requires SoundOverride = true).
PitchPlayback pitch override (requires SoundOverride = true).
Force2dIgnore 3D position; play as a flat 2D sound.
RepeatReplay the sound after it finishes, with a random delay in [MinRepeatTime, MaxRepeatTime].
DistanceAttenuationOverrideOverride the event’s attenuation settings with Distance and Falloff.
OcclusionOverrideOverride occlusion settings.
TargetMixerRoute to a specific mixer channel.

Triggering manually

var spc = Components.Get<SoundPointComponent>();

// Play
spc.StartSound();

// Stop with a short fade
spc.StopSound();

Temporary effect

SoundPointComponent implements Component.ITemporaryEffect, so it can be used with pooled or short-lived GameObjects. The engine keeps the object alive until IsActive returns false (i.e., the sound finishes playing).

SoundBoxComponent

SoundBoxComponent plays a sound whose apparent position is always the closest point on a box volume to the listener. This makes ambient area sounds (caves, rooms, machinery) feel natural as the listener moves through the space.
var sbc = GameObject.Components.Create<SoundBoxComponent>();
sbc.SoundEvent = ResourceLibrary.Get<SoundEvent>( "sounds/ambient.cave" );
sbc.Scale = new Vector3( 400, 400, 200 ); // box dimensions
sbc.PlayOnStart = true;
The sound position (SndPos) is recalculated each update by finding the nearest listener and clamping it to the box surface. You can read SndPos to visualize where the audio source appears to come from.

AudioListener

By default the active camera provides the listener position for 3D audio. Add an AudioListener component to a different GameObject to override this — useful for third-person cameras, splitscreen, or top-down games where hearing should follow the character rather than the camera.
// The listener will follow the player character, not the camera
var listener = playerGameObject.Components.Create<AudioListener>();
listener.UseCameraDirection = true; // still use the camera's rotation for HRTF
Only one AudioListener needs to exist per scene. Multiple listeners are supported for split-screen setups — each scene tracks them internally.

Audio.Mixer

Mixers let you group sounds and control their collective volume, spatialization, and occlusion. The default mixer receives all sounds that do not specify a target. Child mixers feed into their parent, forming a tree.

Accessing named mixers

Mixers are defined in the project’s audio settings. Retrieve them by name:
using Sandbox.Audio;

// Route a sound to the "music" mixer
var handle = Sound.Play( "sounds/music.theme" );
handle.TargetMixer = Mixer.Get( "music" );

Mixer properties

var music = Mixer.Get( "music" );

music.Volume = 0.5f;      // scale output volume 0–1
music.Mute   = true;      // silence without stopping voices
music.Solo   = true;      // only this mixer (and its children) are heard
music.MaxVoices = 4;      // drop oldest voices beyond this limit

// Per-mixer effect amounts (0–1 scale factor on top of per-voice settings)
music.Occlusion         = 0.0f; // no occlusion on music
music.DistanceAttenuation = 0.0f;
music.AirAbsorption     = 0.0f;
music.Spacializing      = 0.0f; // play music from all speakers equally

Monitoring output levels

Each mixer exposes an AudioMeter you can poll for the current loudness:
protected override void OnUpdate()
{
    var level = Mixer.Get( "music" ).Meter.Level;
    // Drive a UI volume bar, waveform visualizer, etc.
}

Stopping all sounds on a mixer

Mixer.Get( "sfx" ).StopAll( fade: 0.5f );

Routing sounds to a mixer from a component

All BaseSoundComponent-derived components expose a TargetMixer property of type MixerHandle:
// In the editor: set TargetMixer on the SoundPointComponent to "sfx"
// In code:
var spc = Components.Get<SoundPointComponent>();
spc.TargetMixer = MixerHandle.Named( "sfx" );

Master volume

The global volume convar is exposed as Sound.MasterVolume. You can read it but generally should not override it — it respects the user’s system preferences.
float masterVol = Sound.MasterVolume;

Common patterns

Footstep sounds

Play a one-shot sound at the foot position on each step event:
void OnFootstep( Vector3 footPosition )
{
    var handle = Sound.Play( "sounds/footstep.concrete", footPosition );
    // Randomize pitch slightly for variety
    handle.Pitch = Random.Shared.Float( 0.9f, 1.1f );
}

Looping engine with pitch

SoundHandle _engineLoop;

protected override void OnStart()
{
    _engineLoop = Sound.Play( "sounds/engine.idle" );
    _engineLoop.Position = WorldPosition;
}

protected override void OnUpdate()
{
    _engineLoop.Position = WorldPosition;
    _engineLoop.Pitch = 0.8f + Throttle * 0.6f; // 0.8–1.4 based on throttle
    _engineLoop.Volume = 0.4f + Throttle * 0.6f;
}

UI sounds (non-positional)

void OnButtonClick()
{
    var handle = Sound.Play( "sounds/ui.click" );
    handle.ListenLocal = true;          // 2D — no position needed
    handle.DistanceAttenuation = false;
}

Stop all sounds when a scene is torn down

protected override void OnDestroy()
{
    Sound.StopAll( fade: 0.3f );
}
SoundHandle is not garbage-collected immediately. Always call Stop() or Dispose() when you are done with a looping sound, otherwise it will continue playing until the engine shuts down.

Build docs developers (and LLMs) love