/godot-audio
Play and mix audio in Godot 4.7: AudioStreamPlayer (2D/3D variants), audio buses with volume/mute and effects, music vs SFX routing, db/linear volume, and precise sync-to-beat playback timing. Use when playing sounds or music in a Godot project, routing AudioStreamPlayer nodes
$ npx -y skills add gamedev-skills/awesome-gamedev-agent-skills --skill godot-audio --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/godot-audio
Context preview
The summary Claude sees to decide when to auto-load this skill.
Play and mix audio in Godot 4.7: AudioStreamPlayer (2D/3D variants), audio buses with volume/mute and effects, music vs SFX routing, db/linear volume, and precise sync-to-beat playback timing. Use when playing sounds or music in a Godot project, routing AudioStreamPlayer nodes
SKILL.md
godot-audio.SKILL.mdname: godot-audio
description: >
Play and mix audio in Godot 4.7: AudioStreamPlayer (2D/3D variants), audio buses
with volume/mute and effects, music vs SFX routing, db/linear volume, and precise
sync-to-beat playback timing. Use when playing sounds or music in a Godot project,
routing AudioStreamPlayer nodes to buses, adjusting bus volume via AudioServer, or
syncing gameplay to the beat.
Godot Audio (4.x)
Play SFX and music, route them through buses, control volume in decibels, and time gameplay to the beat. Targets **Godot 4.7**.
When to use
- Use when playing sound effects or music, routing audio to buses (Master/Music/SFX),
adjusting volume/mute from code, adding bus effects (reverb, compressor), positional 3D audio, or syncing events to music.
**When *not* to use:** engine-agnostic audio *design* (adaptive music structure, mixing philosophy, ducking patterns) → `audio-design`; importing/encoding assets outside Godot.
Core workflow
1. **Pick the player node:**
- `AudioStreamPlayer` — non-positional (music, UI, global SFX).
- `AudioStreamPlayer2D` / `AudioStreamPlayer3D` — positional; volume/pan from distance.
2. **Assign an `AudioStream`** to `stream` (`.ogg` for music/loops, `.wav` for short SFX) and `play()`. Set `autoplay` for music that starts with the scene. 3. **Route to a bus.** Set the player's `bus` to a named bus (e.g. `"Music"`, `"SFX"`). Define buses in the Audio panel (bottom dock); each can have volume, mute, solo, and effects. 4. **Control volume in dB**, not linear (audio is logarithmic). `0 dB` = unchanged, `-80 dB` ≈ silent. Convert with `linear_to_db`/`db_to_linear`. 5. **Drive volume/mute from code** with `AudioServer` by bus index. 6. **For rhythm**, compute precise playback time using output latency compensation.
Patterns
1. One-shot SFX (fire-and-forget)
@onready var sfx: AudioStreamPlayer = $Sfx # stream assigned in the editor
func play_jump() -> void:
sfx.pitch_scale = randf_range(0.95, 1.05) # slight variation avoids fatigue
sfx.play()
# For many overlapping copies, use an AudioStreamPlayer with an
# AudioStreamPolyphonic stream, or spawn short-lived players and free on `finished`.2. Set a bus's volume and mute via AudioServer
func set_music_volume(linear_0_to_1: float) -> void:
var bus := AudioServer.get_bus_index("Music")
# Convert a 0..1 slider to decibels; clamp avoids -inf at 0.
AudioServer.set_bus_volume_db(bus, linear_to_db(maxf(linear_0_to_1, 0.0001)))
func toggle_sfx(muted: bool) -> void:
AudioServer.set_bus_mute(AudioServer.get_bus_index("SFX"), muted)3. Crossfade between two music tracks
@onready var a: AudioStreamPlayer = $MusicA
@onready var b: AudioStreamPlayer = $MusicB
func crossfade_to(stream: AudioStream, secs := 1.5) -> void:
b.stream = stream
b.volume_db = -40.0
b.play()
var tw := create_tween().set_parallel(true)
tw.tween_property(a, "volume_db", -40.0, secs) # fade out current
tw.tween_property(b, "volume_db", 0.0, secs) # fade in next
tw.chain().tween_callback(a.stop)
var tmp := a; a = b; b = tmp # swap roles4. Beat-accurate timing (compensate for output latency)
@onready var music: AudioStreamPlayer = $Music
func get_playback_time() -> float:
# Add time since the last audio mix, subtract output latency, for sub-frame accuracy.
var t := music.get_playback_position() + AudioServer.get_time_since_last_mix()
return t - AudioServer.get_output_latency()Pitfalls
- **Treating volume as linear.** `volume_db`/`set_bus_volume_db` are decibels. Setting
`volume_db = 0.5` is nearly full volume, not half. Map sliders with `linear_to_db`.
- **`linear_to_db(0.0)` is `-inf`.** Clamp the linear value to a small minimum (e.g.
`0.0001`) before converting, or special-case 0 → mute.
- **Bus name typos fail quietly.** `get_bus_index("Muisc")` returns `-1`; calls then error
or no-op. Match the exact bus name from the Audio panel.
- **Short SFX cut off** when the same player is retriggered. Use separate players, an
`AudioStreamPolyphonic`, or `AudioStreamPlayer` per-shot freed on `finished`.
- **Music doesn't loop** unless the import/stream loop is enabled (`.ogg` import has a
Loop option; `AudioStreamWAV` has `loop_mode`).
- **Syncing to `get_playback_position()` alone is jittery** — it updates per audio mix,
not per frame; add `get_time_since_last_mix()` and subtract `get_output_latency()`.
- **3D audio inaudible** → no `AudioListener3D`/`Camera3D` to hear it, or `max_distance`/
attenuation too tight, or wrong bus muted.
References
- For the bus layout (`.tres`), adding effects (reverb/compressor/EQ) and side-chain
ducking, `AudioStreamPolyphonic`/`AudioStreamInteractive`, microphone capture, and procedural audio with `AudioStreamGenerator`, read `references/buses-and-effects.md`.
Related skills
- `audio-design` — engine-agnostic adaptive music, mixing, and ducking practice.
- `godot-animation` — syncing animation/Tween to `get_playback_position()`.
- `godot-ui-control` — volume sliders wired to `AudioServer`.
Read more
name: godot-audio description: > Play and mix audio in Godot 4.7: AudioStreamPlayer (2D/3D variants), audio buses with volume/mute and effects, music vs SFX routing, db/linear volume, and precise sync-to-beat playback timing. Use when playing sounds or music in a Godot project, routing AudioStreamPlayer nodes to buses, adjusting bus volume via AudioServer, or syncing gameplay to the beat.
Godot Audio (4.x)
Play SFX and music, route them through buses, control volume in decibels, and time gameplay to the beat. Targets **Godot 4.7**.
When to use
- Use when playing sound effects or music, routing audio to buses (Master/Music/SFX),
adjusting volume/mute from code, adding bus effects (reverb, compressor), positional 3D audio, or syncing events to music.
**When *not* to use:** engine-agnostic audio *design* (adaptive music structure, mixing philosophy, ducking patterns) → `audio-design`; importing/encoding assets outside Godot.
Core workflow
1. **Pick the player node:**
- `AudioStreamPlayer` — non-positional (music, UI, global SFX).
- `AudioStreamPlayer2D` / `AudioStreamPlayer3D` — positional; volume/pan from distance.
2. **Assign an `AudioStream`** to `stream` (`.ogg` for music/loops, `.wav` for short SFX) and `play()`. Set `autoplay` for music that starts with the scene. 3. **Route to a bus.** Set the player's `bus` to a named bus (e.g. `"Music"`, `"SFX"`). Define buses in the Audio panel (bottom dock); each can have volume, mute, solo, and effects. 4. **Control volume in dB**, not linear (audio is logarithmic). `0 dB` = unchanged, `-80 dB` ≈ silent. Convert with `linear_to_db`/`db_to_linear`. 5. **Drive volume/mute from code** with `AudioServer` by bus index. 6. **For rhythm**, compute precise playback time using output latency compensation.
Patterns
1. One-shot SFX (fire-and-forget)
@onready var sfx: AudioStreamPlayer = $Sfx # stream assigned in the editor
func play_jump() -> void:
sfx.pitch_scale = randf_range(0.95, 1.05) # slight variation avoids fatigue
sfx.play()
# For many overlapping copies, use an AudioStreamPlayer with an
# AudioStreamPolyphonic stream, or spawn short-lived players and free on `finished`.2. Set a bus's volume and mute via AudioServer
func set_music_volume(linear_0_to_1: float) -> void:
var bus := AudioServer.get_bus_index("Music")
# Convert a 0..1 slider to decibels; clamp avoids -inf at 0.
AudioServer.set_bus_volume_db(bus, linear_to_db(maxf(linear_0_to_1, 0.0001)))
func toggle_sfx(muted: bool) -> void:
AudioServer.set_bus_mute(AudioServer.get_bus_index("SFX"), muted)3. Crossfade between two music tracks
@onready var a: AudioStreamPlayer = $MusicA
@onready var b: AudioStreamPlayer = $MusicB
func crossfade_to(stream: AudioStream, secs := 1.5) -> void:
b.stream = stream
b.volume_db = -40.0
b.play()
var tw := create_tween().set_parallel(true)
tw.tween_property(a, "volume_db", -40.0, secs) # fade out current
tw.tween_property(b, "volume_db", 0.0, secs) # fade in next
tw.chain().tween_callback(a.stop)
var tmp := a; a = b; b = tmp # swap roles4. Beat-accurate timing (compensate for output latency)
@onready var music: AudioStreamPlayer = $Music
func get_playback_time() -> float:
# Add time since the last audio mix, subtract output latency, for sub-frame accuracy.
var t := music.get_playback_position() + AudioServer.get_time_since_last_mix()
return t - AudioServer.get_output_latency()Pitfalls
- **Treating volume as linear.** `volume_db`/`set_bus_volume_db` are decibels. Setting
`volume_db = 0.5` is nearly full volume, not half. Map sliders with `linear_to_db`.
- **`linear_to_db(0.0)` is `-inf`.** Clamp the linear value to a small minimum (e.g.
`0.0001`) before converting, or special-case 0 → mute.
- **Bus name typos fail quietly.** `get_bus_index("Muisc")` returns `-1`; calls then error
or no-op. Match the exact bus name from the Audio panel.
- **Short SFX cut off** when the same player is retriggered. Use separate players, an
`AudioStreamPolyphonic`, or `AudioStreamPlayer` per-shot freed on `finished`.
- **Music doesn't loop** unless the import/stream loop is enabled (`.ogg` import has a
Loop option; `AudioStreamWAV` has `loop_mode`).
- **Syncing to `get_playback_position()` alone is jittery** — it updates per audio mix,
not per frame; add `get_time_since_last_mix()` and subtract `get_output_latency()`.
- **3D audio inaudible** → no `AudioListener3D`/`Camera3D` to hear it, or `max_distance`/
attenuation too tight, or wrong bus muted.
References
- For the bus layout (`.tres`), adding effects (reverb/compressor/EQ) and side-chain
ducking, `AudioStreamPolyphonic`/`AudioStreamInteractive`, microphone capture, and procedural audio with `AudioStreamGenerator`, read `references/buses-and-effects.md`.
Related skills
- `audio-design` — engine-agnostic adaptive music, mixing, and ducking practice.
- `godot-animation` — syncing animation/Tween to `get_playback_position()`.
- `godot-ui-control` — volume sliders wired to `AudioServer`.
<img src="docs/assets/banner.png" width="820" alt="awesome-gamedev-agent-skills — game-dev skills for AI coding agents.
Repo: gamedev-skills/awesome-gamedev-agent-skills
Other skills on awesome-gamedev-agent-skills.
- /audio-design
Implement game audio practice — bus/mixer architecture and gain in decibels, ducking (sidechain), adaptive/dynamic music via layering and re-sequencing, SFX variation, and beat synchronization. Engine-neutral. Use when the user mentions audio mixing, audio buses,
Open skill - /camera-systems
Build game cameras that feel good — 2D follow with a deadzone, look-ahead, smoothing, and level-bounds clamping; 3D third-person orbit with collision and first-person look; plus multi-target framing and a shake hook. Engine-neutral techniques that pair with the engine's camera
Open skill - /create-game-assets
Plan, generate, source, normalize, and validate cohesive visual game assets. Use for art direction, style bibles, sprites, tilesets, backgrounds, UI art, icons, textures, concept art, or 3D asset briefs.
Open skill - /dialogue-systems
Build branching dialogue and narrative — a node/choice graph with conditions, variables, and localization hooks — and choose between authoring tools Ink and Yarn Spinner or a custom data-driven runner. Engine-neutral. Use when the user mentions dialogue system, branching
Open skill - /game-ai
Design NPC and enemy decision-making with finite state machines, behavior trees, steering behaviors, and A* pathfinding — engine-neutral algorithms that pair with the detected engine's navigation API. Use when building enemy AI, an FSM or behavior tree, steering/flocking, or
Open skill - /game-feel
Add "juice" and game feel that makes actions satisfying — screen shake, hit-stop/freeze frames, tweened/eased motion, squash & stretch, knockback, and layered audio-visual feedback — as engine-neutral techniques that pair with the detected engine's tween, particle, and camera
Open skill

