Quick answer: AudioServer.set_bus_mute(true) only flips a flag for future buffers. Voices already playing keep flowing because polyphonic players cache their gain at start time and decoded audio sits in the output buffer. Replace the mute call with a tween from 0 dB to -80 dB on set_bus_volume_db, stop active polyphonic streams explicitly, and recreate the AudioStreamPlayer when you change its bus.
Here is how to fix Godot audio that keeps playing after you mute the bus. The player opens the pause menu and you call AudioServer.set_bus_mute(master_bus, true). The footstep loop keeps marching. The hum of the engine fades half a second later. The fanfare you triggered the frame before mute plays through to its end. You expected silence and got a slow trail-off. The culprit is a mix of buffer latency, cached per-voice gain, and how Godot routes already-active streams through buses that change settings mid-flight.
The Symptom
You toggle a master mute — from a settings menu, an alt-tab handler, or a focus-lost event. AudioServer.set_bus_mute returns immediately. New sounds you trigger after the call are silent, as expected. But everything that was already playing continues:
Looping ambience keeps looping. A wind loop on a dedicated bus keeps cycling through its sample. New iterations after mute are silent only if the loop restarts; if the underlying stream never stops, you hear it forever.
Short SFX play to completion. A gunshot triggered one frame before mute plays its full ~150 ms tail. Footstep one-shots audibly tail off. Music fades over multiple seconds.
Polyphonic players are worst. If you use AudioStreamPolyphonic for SFX, every voice that was playing at mute time keeps playing at its original volume regardless of what you do to the bus.
What Causes This
Bus mute does not affect in-flight voices. Internally, set_bus_mute sets a boolean that the audio thread checks during the next mix tick. Any audio already in the output ring buffer is past that check — it has been decoded and queued. Buffer latency is typically 20-50 ms, so you hear that tail before silence.
Polyphonic streams cache gain at playback start. When you call play_stream on an AudioStreamPlaybackPolyphonic, Godot snapshots the player’s current volume and applies it to the new voice. Subsequent changes to the bus or player volume do not back-propagate to that voice. The only way to silence it is to stop the voice or recreate the player.
Per-stream volume drift. Each AudioStreamPlayer has its own volume_db independent of the bus. If you wrote code that ramps the player’s volume up at the start of a sound, that ramp is unaffected by bus mute. The voice keeps playing at its locally-computed gain.
Hardware mixing on some platforms. Web exports and certain mobile audio backends route audio through the OS mixer. Godot’s bus mute affects the engine’s internal mix but the OS holds its own buffer that can play out independently. This is invisible on desktop but very audible in browser builds.
Output capture nodes bypass downstream buses. If you use a Capture effect on a bus to record audio, the captured stream may continue to feed downstream nodes even when the source bus is muted, depending on the capture configuration.
The Fix
Step 1: Replace bus mute with a volume ramp. Tween set_bus_volume_db from 0 to -80 over 30-50 ms. This actively scales every voice on the bus, including polyphonic ones, because the bus volume is applied per-mix-tick after the per-voice gain. Going to -80 dB is below audibility for any reasonable speaker; do not use -INF because some platforms treat it as undefined and produce clicks.
# MasterAudio.gd - autoload that handles muting
extends Node
var _saved_volumes: Dictionary = {}
var _muted: bool = false
func set_muted(value: bool) -> void:
if value == _muted:
return
_muted = value
var bus_count := AudioServer.get_bus_count()
for i in bus_count:
var name := AudioServer.get_bus_name(i)
if value:
# Cache the user-set volume
_saved_volumes[name] = AudioServer.get_bus_volume_db(i)
_ramp_bus(i, -80.0, 0.04)
else:
var restore: float = _saved_volumes.get(name, 0.0)
_ramp_bus(i, restore, 0.04)
func _ramp_bus(idx: int, target_db: float, duration: float) -> void:
var tween := create_tween()
var from := AudioServer.get_bus_volume_db(idx)
tween.tween_method(
func(v): AudioServer.set_bus_volume_db(idx, v),
from, target_db, duration
)
This pattern silences audio in roughly one buffer cycle and restores it cleanly on unmute. Cache the user’s preferred volume before you ramp down so a settings slider change is not lost.
Step 2: Stop polyphonic voices explicitly. If you use AudioStreamPolyphonic for ricochets, footsteps, or particle SFX, hold the playback reference and stop active streams when you mute. Otherwise the cached per-voice gain keeps them audible regardless of bus volume.
# Polyphonic SFX manager that supports hard-stop on mute
extends AudioStreamPlayer
var _playback: AudioStreamPlaybackPolyphonic
var _active_ids: Array[int] = []
func _ready() -> void:
stream = AudioStreamPolyphonic.new()
play()
_playback = get_stream_playback()
func play_sfx(s: AudioStream, db: float = 0.0) -> int:
var id := _playback.play_stream(s, 0.0, db)
if id != AudioStreamPlaybackPolyphonic.INVALID_ID:
_active_ids.append(id)
return id
func stop_all() -> void:
for id in _active_ids:
if _playback.is_stream_playing(id):
_playback.stop_stream(id)
_active_ids.clear()
Call stop_all() on the polyphonic SFX manager from the same code path that triggers your mute. The bus volume ramp handles long-tail sounds; the explicit stop kills already-decoded short ones.
Bus Reassignment During Playback
If you change an AudioStreamPlayer’s bus property mid-playback, Godot keeps the existing voice on the old bus and only routes new playbacks to the new bus. This catches teams who try to “move” a sound from SFX to Music at runtime — the original keeps playing through the original bus and the “mute SFX” toggle never silences it. To force a reassignment, call stop() on the player, change the bus, and call play() again from the saved playback position.
Web and Mobile Specifics
HTML5 audio uses the WebAudio API, which has its own scheduling layer with ~50-100 ms of latency on top of Godot’s mix buffer. Bus mute can take twice as long to take effect there. The volume ramp approach still works because WebAudio applies gain in real time. On mobile, audio focus events (call, alarm, app switch) can pause the audio thread without notifying Godot, leaving the bus mute flag desynced. Always re-apply your mute state in NOTIFICATION_APPLICATION_FOCUS_IN handlers.
“Bus mute is a hint about future audio. Volume ramp is a command about current audio. If you need silence right now, ramp the volume.”
Related Issues
If your bus effects (reverb, EQ) are not applying as expected, see Godot Audio Bus Effect Not Applying. If your bus layout resets when the player restarts the game, check Audio Bus Layout Reset on Restart for save/load patterns.
Ramp volume_db to -80 for instant silence — set_bus_mute is a future-tense flag, not a stop button.