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— anAudioStreamresource (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 (default10.0). The larger the value, the farther the sound is heard. The exact formula depends onattenuation_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_model—INVERSE_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
AudioEffectSpectrumAnalyzeron a bus.
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.
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.