Quick answer: Look up bus and effect indices once at startup and cache them. Use AudioServer.get_bus_index("BusName") then iterate effects to find by class. Toggle via cached indices.

You toggle a LowPass effect for underwater audio with AudioServer.set_bus_effect_enabled(2, 0, true). Doesn’t change audio. The hardcoded indices (2, 0) don’t match the runtime bus layout.

Index Lookup Helper

var _lowpass_bus_idx: int = -1
var _lowpass_effect_idx: int = -1

func _ready():
    _lowpass_bus_idx = AudioServer.get_bus_index("SFX")
    if _lowpass_bus_idx == -1:
        push_error("SFX bus not found")
        return

    var count = AudioServer.get_bus_effect_count(_lowpass_bus_idx)
    for i in count:
        var e = AudioServer.get_bus_effect(_lowpass_bus_idx, i)
        if e is AudioEffectLowPassFilter:
            _lowpass_effect_idx = i
            break

func enter_water():
    AudioServer.set_bus_effect_enabled(_lowpass_bus_idx, _lowpass_effect_idx, true)

func exit_water():
    AudioServer.set_bus_effect_enabled(_lowpass_bus_idx, _lowpass_effect_idx, false)

Lookup once, cache, use by index. Rearranging the bus layout doesn’t break the code; the lookup runs again on init.

Diagnose Index Issues

for b in AudioServer.get_bus_count():
    print(b, ": ", AudioServer.get_bus_name(b))
    for e in AudioServer.get_bus_effect_count(b):
        print("  ", e, ": ", AudioServer.get_bus_effect(b, e).get_class())

Prints the full bus + effect layout. Verifies your assumed indices match reality.

Editor vs Runtime Layout

If you saved a default_bus_layout.tres but a different layout is loaded at runtime (via Project Settings → Audio → Bus Layout), indices differ. Stick to one layout; check Project Settings to verify which is active.

Verifying

Toggle enter_water() / exit_water() and listen. Low-pass filter applies/removes. The cached indices remain stable across runs of the same project.

“Index-based audio API is fast but fragile. Cache by lookup at startup; never hardcode indices.”

Build an AudioBus singleton with named-effect helpers — gameplay code calls AudioBus.enable_underwater() instead of remembering indices.