~4 min read

3D Audio and AudioBus

AudioStreamPlayer3D, routing through AudioBus, effects.

In Godot, audio is a separate server (AudioServer) with routing through AudioBus. A scene has several player nodes; the project has a single mixer.

Playback Nodes

Three options:

  • AudioStreamPlayer — no positioning. Music, UI sounds, dialogue.
  • AudioStreamPlayer2D — positional for 2D scenes (volume based on distance to the listener).
  • AudioStreamPlayer3D — 3D-positional with attenuation, Doppler, area reverb.
@onready var step_player: AudioStreamPlayer3D = $StepPlayer

func _ready() -> void:
    step_player.stream = preload("res://audio/step.ogg")

func play_step() -> void:
    step_player.pitch_scale = randf_range(0.9, 1.1)  # variation for a natural feel
    step_player.play()

Key properties of AudioStreamPlayer3D:

  • stream — an AudioStream resource (AudioStreamOggVorbis, AudioStreamMP3, AudioStreamWAV).
  • volume_db — volume in decibels. 0 is the original, -10 is quieter, -80 is near silence.
  • pitch_scale — speed/pitch multiplier.
  • unit_size — attenuation factor (default 10.0). The larger the value, the farther the sound is heard. The exact formula depends on attenuation_model. Not a “half-volume distance” but a curve parameter.
  • max_distance — the distance at which the sound fades out completely (0 = no limit).
  • attenuation_modelINVERSE_DISTANCE, INVERSE_SQUARE_DISTANCE, LOGARITHMIC, DISABLED.
  • doppler_tracking — Doppler effect (IDLE_STEP / PHYSICS_STEP / DISABLED).
  • area_mask — bitmask of Area3D layers this player reacts to (an Area3D with a reverb/bus override on the matching layers will override the sound processing of this player, for example an “underwater” effect in a water zone).

AudioBus — the Mixer

Opened in the bottom panel: the Audio tab. Structure:

Master (the main one, always present)
├── Music
├── SFX
│   ├── Voice
│   └── Ambience
└── UI

Each bus has:

  • Volume — a slider.
  • Solo / Mute / Bypass.
  • Effects — a stack of effects: Reverb, EQ, Compressor, Limiter, Chorus, Phaser, Distortion, Delay, Pitch Shift, Filter, Stereo Enhance, Spectrum Analyzer.

On AudioStreamPlayer you set bus = "SFX/Voice", and the sound is routed there. Convenient for muting SFX separately from music, or applying a low-pass to Master when paused.

# Setting volume from the player's settings (a UI slider)
var music_idx = AudioServer.get_bus_index("Music")
AudioServer.set_bus_volume_db(music_idx, linear_to_db(music_volume_0_to_1))

linear_to_db(linear) is a built-in function that converts linear volume (0..1) to decibels (-80..0).

2D / 3D Spatial Blend

Unlike Unity’s AudioSource (with a 2D↔3D slider), in Godot the choice of node itself determines positioning:

  • AudioStreamPlayer — never positional.
  • AudioStreamPlayer2D/3D — always positional.

If you need a sound that starts as 3D and then becomes 2D (for example, a narrator that approaches and turns into UI), those are two different nodes, switched in code.

Listener

Unlike Unity (where AudioListener is a separate component), in Godot the listener is the active Camera3D by default. If you need to offset the listener relative to the camera, there is the AudioListener3D node:

Camera3D (current)
└── AudioListener3D (current = true)  ← the listener is now here

Useful for third-person: the camera is far away, but you want to listen from the character’s head level.

AudioStreamInteractive — Dynamic Music

Introduced in 4.3. This is a special AudioStream that describes clips (parts) and transitions between them with rules:

  • Cross-fade over N beats.
  • Match by beat / bar / next-marker.
  • Conditional transitions (based on a parameter).

Example: a calm theme (clip A) transitions into a combat one (clip B) over 2 bars when set_clip("Battle") is called. This is an analog of FMOD/Wwise states, but built in.

AudioStreamPolyphonic — Layered Sounds

If you play many short sounds (gunshots, impacts) on a single source, a regular AudioStreamPlayer interrupts the previous one. AudioStreamPolyphonic solves this: one player, multiple layers.

var poly_stream = AudioStreamPolyphonic.new()
poly_stream.polyphony = 8  # up to 8 at once
$Player.stream = poly_stream

var playback: AudioStreamPlaybackPolyphonic = $Player.get_stream_playback()
playback.play_stream(preload("res://gunshot.ogg"))
playback.play_stream(preload("res://shell_drop.ogg"))

AudioStreamGenerator — Procedural Audio

If you want to generate sound programmatically (a synthesizer, retro bleeps, reactive music), there is AudioStreamGenerator. It is a specialized AudioStream into which you push PCM frames from code.

extends AudioStreamPlayer

@onready var playback: AudioStreamGeneratorPlayback

var phase: float = 0.0
var frequency: float = 440.0  # A4
var sample_rate: float = 44100.0

func _ready() -> void:
    var gen := AudioStreamGenerator.new()
    gen.mix_rate = sample_rate
    gen.buffer_length = 0.1  # 100 ms buffer
    stream = gen
    play()
    playback = get_stream_playback()

func _process(_delta: float) -> void:
    var frames_to_fill := playback.get_frames_available()
    var increment := frequency * TAU / sample_rate
    for i in frames_to_fill:
        var sample := sin(phase) * 0.3
        playback.push_frame(Vector2(sample, sample))  # stereo
        phase += increment
        if phase > TAU:
            phase -= TAU

This is a sine-wave tone generator. In practice:

  • 8-bit chiptune synthesis — square/triangle/noise via simple algorithms.
  • Reactive music — pitch/timbre that react to gameplay (low health → dissonance).
  • Voice synthesis — formant synthesis for procedural NPC voices.
  • Audio visualizers and spectrum analyzers — the inverse task, via AudioEffectSpectrumAnalyzer on a bus.
The generator runs in _process — watch for skips

If _process falls behind the sample rate, a “gap” appears in the buffer → an audible click. For serious procedural audio generation it is better to fill the buffer from _physics_process, or from a separate thread altogether via WorkerThreadPool (see the Threading chapter).

Import and Formats

Supported: OGG Vorbis, MP3, WAV. Recommendations:

  • Music — OGG (Vorbis) with loop points. MP3 is worse at loop-point accuracy.
  • Short SFX — WAV (lossless) or short OGG.
  • Voice — OGG at a bitrate of 96–128 kbps.

In the Import dock, for each audio file you configure:

  • Loop — whether to loop.
  • Loop Offset / Length — loop points (for seamless music).
  • BPM / Beat Count / Bar Beats — for AudioStreamInteractive.
An audio bus as an 'underwater' effect

Make a copy of Master with a LowPassFilter, EQ, and a slight Pitch Shift attached. When the player is underwater, switch default_bus to it — all 3D sounds become “muffled” automatically. To return, switch back to Master.

The next chapter covers UI and Control nodes.