Audio & Middleware Problems May 11, 2026

FMOD Banks Load in Unity WebGL But Audio Is Silent Until Second Scene Load - How to Fix

Solved guide for Unity WebGL FMOD builds where the bank loaded successfully but audio is silent on the first scene. Covers AudioContext gesture unlock, bus mute and snapshot leakage, sample-rate tier mismatch, and Studio System init order against Unity AudioSettings under 2026 HTML5 autoplay tightening.

By GamineAI Team

FMOD Banks Load in Unity WebGL But Audio Is Silent Until Second Scene Load - How to Fix

Problem: Your Unity WebGL build with FMOD Studio Unity Integration loads cleanly, the FMOD Profiler confirms the bank loaded in the parity test against the Editor, but on the first scene the meters stay flat, no music or SFX plays, and audio only kicks in after the player reloads the page or transitions to a second scene.

Common symptoms reported in 2026 HTML5 demo lanes:

  • RuntimeManager.LoadBank returns OK but Studio::EventInstance::start produces no sound.
  • The Voices and CPU meters in the FMOD Profiler show zero activity on first scene; both wake up after a reload.
  • Logs show FMOD_OK everywhere, no ERR_* codes, and no console errors in the browser DevTools.
  • Audio works in Editor Play Mode and standalone builds, fails only on the deployed WebGL host.
  • Hosted demos pass during local Build And Run but fail when uploaded to itch.io, Newgrounds, or your own static host.

This article gives you the fastest safe fix path (gesture unlock + bus suspend check) for ~80% of cases, then walks the four deeper root causes that account for the rest.

Direct answer

In 2026 HTML5 demos, FMOD banks routinely load before the browser AudioContext is unlocked by a user gesture, so Studio::System keeps voices in a suspended state. Audio appears silent until the next scene load happens to coincide with a click. Force an explicit gesture handler, resume the AudioContext, and un-mute the master bus on the first user interaction. If silence persists, validate the bank sample rate against the browser sample rate and serialize Studio::System::init against Unity AudioSettings.

Why this issue spikes in 2026

Three independent shifts in early 2026 combined to push this failure into mainstream reports:

  1. Browser autoplay policy tightened again - Chromium 122+ and Safari 17.4+ now treat in-page navigation between Unity scenes as insufficient user activation if the original page load did not trigger a gesture. Older builds got an implicit pass; 2026 builds do not.
  2. FMOD Studio Unity Integration moved to FMOD 2.03.x as default through 2026 - the WebAssembly init path tightened against Unity's own audio subsystem, exposing init-order races that were latent before.
  3. Unity 6.x WebGL renderer changes the load sequence around scene additive loads; the first scene's Awake/Start now runs slightly before the audio thread is ready on some browsers, which the second scene masks because the player has already clicked once by then.

If your project shipped fine in 2025 and broke in 2026, this is almost always the cause.

Root causes (in order of likelihood)

  1. AudioContext suspended until gesture - The browser holds the WebAudio context in suspended state. FMOD's WebAssembly backend hands voices to the suspended context; meters stay flat. The second scene works because by then the player clicked something.
  2. Master bus mute or snapshot leakage across scene loads - A snapshot or bus mute set in your title scene (often for a fade-in) was not cleared. Bus state persists across LoadScene because Studio::System is global.
  3. Sample rate tier mismatch - The bank was built at 48 kHz but the browser AudioContext defaulted to 44.1 kHz (or vice versa). FMOD resamples internally on most platforms but WebAssembly can stall on the first scene if the resampler initializes lazily.
  4. Studio::System::init ordering against Unity AudioSettings - Unity's audio system suspends on focus loss / page hidden; if RuntimeManager initialized before Unity's audio thread was ready (common on Unity 6.x WebGL), the FMOD output device binds to a stub that only flushes on the next audio reset.
  5. Web-incompatible bank contents - A bank built with desktop-only plugins or unsupported codecs silently disables voices on web. Less common but worth checking on a fresh project.

Fastest safe fix path (resolves ~80% of cases)

This sequence handles root causes 1 and 2 - the overwhelming majority of "second scene works" reports.

Step 1 - Add an explicit gesture-unlock bridge

Create a new MonoBehaviour FMODWebAudioUnlock.cs and attach it to a persistent GameObject in your title scene (the one that loads first):

using UnityEngine;
using UnityEngine.UI;
using FMODUnity;

public class FMODWebAudioUnlock : MonoBehaviour
{
    [SerializeField] private Button anyButton;
    private bool _unlocked;

    void Awake()
    {
        DontDestroyOnLoad(gameObject);
    }

    void Start()
    {
        if (anyButton != null)
        {
            anyButton.onClick.AddListener(UnlockAudio);
        }
    }

    void OnGUI()
    {
        if (_unlocked) return;
        if (Event.current.type == EventType.MouseDown ||
            Event.current.type == EventType.TouchDown ||
            Event.current.type == EventType.KeyDown)
        {
            UnlockAudio();
        }
    }

    private void UnlockAudio()
    {
        if (_unlocked) return;
        _unlocked = true;

        var system = RuntimeManager.StudioSystem;
        system.getCoreSystem(out var core);
        core.mixerResume();

        if (RuntimeManager.StudioSystem.getBus("bus:/", out var master) == FMOD.RESULT.OK)
        {
            master.setMute(false);
            master.setVolume(1.0f);
        }
    }
}

Why this works: the OnGUI path catches any mouse/touch/keyboard event, calls mixerResume() on the FMOD core system (which in turn resumes the browser AudioContext), and explicitly un-mutes the master bus in case a snapshot or earlier code left it muted.

Step 2 - Force a gated entry point in your title scene

Add a click-to-start gate before the title scene plays anything. Even one click solves the gesture problem permanently for the rest of the session:

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class TitleGate : MonoBehaviour
{
    [SerializeField] private Button startButton;
    [SerializeField] private string nextScene = "MainMenu";

    void Start()
    {
        startButton.onClick.AddListener(() =>
        {
            FindFirstObjectByType<FMODWebAudioUnlock>()?.SendMessage("UnlockAudio");
            SceneManager.LoadScene(nextScene);
        });
    }
}

Wire startButton to a "Tap to play" UI element. This pairs the gesture-unlock with the first navigation; audio is ready in the next scene by design rather than by accident.

Step 3 - Clear bus suspend state on every scene load

Add this to a persistent audio manager:

using UnityEngine;
using UnityEngine.SceneManagement;
using FMODUnity;

public class FMODSceneAudioReset : MonoBehaviour
{
    void Awake()
    {
        DontDestroyOnLoad(gameObject);
        SceneManager.sceneLoaded += OnSceneLoaded;
    }

    void OnDestroy()
    {
        SceneManager.sceneLoaded -= OnSceneLoaded;
    }

    private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        if (RuntimeManager.StudioSystem.getBus("bus:/", out var master) == FMOD.RESULT.OK)
        {
            master.setMute(false);
        }
    }
}

This catches the most common "developer forgot a setMute(true) from a fade-in snapshot" case without forcing you to audit every event in the project.

Verification

Run the first scene in a freshly opened browser tab (Incognito or Private window so you do not inherit a previous session's unlocked AudioContext).

Confirm in this order:

  1. Manual ear test - First click anywhere on the canvas should produce audible audio within ~200 ms.
  2. FMOD Profiler attached to the WebGL build (over the network: RuntimeManager.IsInitialized && enableProfiler == true in your FMOD Settings) - Voices meter rises on the first event, not zero.
  3. Browser DevTools -> Performance -> Audio - The AudioContext state transitions from suspended to running on first click.

Automated CI verification (recommended once you have one passing run):

[UnityTest]
public IEnumerator FirstSceneAudioPostGestureRmsAboveThreshold()
{
    SceneManager.LoadScene("Title");
    yield return new WaitForSeconds(0.5f);

    var gate = Object.FindFirstObjectByType<TitleGate>();
    gate.GetType().GetMethod("SimulateClick").Invoke(gate, null);
    yield return new WaitForSeconds(0.5f);

    RuntimeManager.StudioSystem.getBus("bus:/", out var master);
    master.getCPUUsage(out var exclusive, out var inclusive);
    Assert.Greater(inclusive, 0.001f);
}

The 500 ms post-gesture window is enough for any reasonable first SFX or music stem to register CPU activity above the noise floor.

Alternative fixes for edge cases

Edge case 1 - Audio still silent after gesture unlock

If your master bus shows un-muted and the AudioContext is running but voices still produce nothing, you almost certainly hit root cause 3 - sample rate tier mismatch.

  1. In FMOD Studio, open the bank and check Project Settings -> Built-in Outputs -> WASM sample rate.
  2. Open the build in DevTools console: new AudioContext().sampleRate returns the browser's native rate.
  3. If they disagree (48000 vs 44100 is the common case), either:
    • Rebuild banks at the browser-native rate, or
    • In Unity, set AudioSettings.outputSampleRate to match the bank rate before RuntimeManager initializes.
  4. Force a reset:
void Awake()
{
    var config = AudioSettings.GetConfiguration();
    config.sampleRate = 48000;
    AudioSettings.Reset(config);
}

Edge case 2 - Studio System init runs before Unity audio is ready

If you instantiate RuntimeManager manually (rare; default plugin handles this), or you call RuntimeManager.StudioSystem very early in Awake, you may get root cause 4.

Serialize the init explicitly:

IEnumerator Start()
{
    while (!AudioSettings.GetConfiguration().speakerMode.HasFlag(AudioSpeakerMode.Stereo))
    {
        yield return null;
    }
    RuntimeManager.StudioSystem.flushCommands();
}

The wait loop forces Unity's audio thread to come up before any FMOD command is queued. On most projects this is unnecessary, but it resolves the rare "audio never works at all on web" case where the gate-unlock approach above also fails.

Edge case 3 - Web-incompatible plugin in the bank

Open FMOD Studio, switch to the WASM platform target, and inspect each event for plugin warnings (red icons in the event list).

Common offenders:

  • Steam Audio (FMOD plugin variant) - desktop only.
  • Resonance Audio spatializers - check the WASM compatibility column.
  • Custom DSP plugins built as native libraries - not portable to WebAssembly.

Strip these from the web bank specifically. Either:

  1. Build a dedicated web bank with desktop-only events removed.
  2. Use platform-specific assets in FMOD Studio so the WASM build automatically excludes incompatible events.

Edge case 4 - itch.io or hosting platform serves wrong MIME

Adjacent issue but easy to mistake for an audio bug. If the host serves .bank files with application/octet-stream and your build relies on streaming bank loading, the load can stall until a memory-mode fallback retries.

Verify with: curl -I https://your-host.example/Build/myproject.wasm

The WASM MIME should be application/wasm. Bank files load fine as application/octet-stream, but COOP/COEP companion headers must be set if you enabled threads.

Prevention

Add this checklist to your WebGL build smoke test so the regression never reaches a public demo again:

  • [ ] Title scene contains a gesture gate (click-to-start) before any audio plays.
  • [ ] Master bus is explicitly un-muted on every SceneManager.sceneLoaded event.
  • [ ] WebGL build sample rate matches the bank sample rate (most often 48 kHz in 2026).
  • [ ] Build target in FMOD Studio is set to WASM for the web bank, with desktop-only plugins stripped.
  • [ ] CI run produces a 500 ms post-gesture RMS proof on every PR touching audio.
  • [ ] DevTools Performance trace from the deployed URL shows AudioContext state running within 200 ms of first click.

A 30-second smoke harness in CI is worth the time. Once added, you stop shipping silent-first-scene demos to itch.io.

FAQ

Why does the second scene work but not the first?

By the time the player navigates between scenes they have already clicked something (a button, even the canvas). That click satisfies the browser's user-gesture requirement and resumes the AudioContext. The first scene plays before the click, so the same code path produces silence.

Does this affect FMOD 2.02.x as well?

Yes, but it spiked in 2026 because FMOD 2.03.x tightened the WebAssembly init path against Unity's audio subsystem. The fix is the same regardless of FMOD version.

Why does my Editor parity test pass?

Editor Play Mode does not run inside a browser AudioContext. It uses Unity's native audio device directly with no autoplay gate. The parity test confirms the bank is valid; it cannot catch browser-side init gating.

Should I migrate off FMOD for web builds?

No. The fix above is approximately 40 lines of code and resolves the issue permanently. FMOD's WebAssembly target is mature in 2026 for small-to-medium audio designs.

Can I just call AudioContext.resume() from JavaScript?

You can, but you also need to resume FMOD's internal core mixer (core.mixerResume()) and un-mute the master bus. The C# bridge above handles both halves; calling only one is the most common cause of a half-working fix.

Related help articles

Further reading


Bookmark this fix for quick reference before your next demo upload. Share this article with your dev friends if they ship FMOD + Unity WebGL - silent-first-scene is the single most common regression report in 2026 indie audio threads, and the fix is one MonoBehaviour and one master-bus reset.