Game Engine Issues May 24, 2026

Godot 4.5 Web Export WASM Memory OOM After Browser Tab Refocus - How to Fix

Fix Godot 4.5 web export WASM OOM after browser tab refocus. visibilitychange handlers, texture cache caps, floor epoch teardown, refocus smoke matrix, and wasm_memory_smoke_receipt_v1.json before fest clips.

By GamineAI Team

Godot 4.5 Web Export WASM Memory OOM After Browser Tab Refocus - How to Fix

Problem: Your Godot 4.5 HTML5 demo on itch.io runs fine for ten minutes. The player alt-tabs to Discord, returns, and the canvas freezes, reloads, or shows a gray screen. DevTools may log RuntimeError: memory access out of bounds, wasm memory limit exceeded, or silent tab discard with no stack trace.

Who is affected now: June 2026 festival teams shipping browser clips beside PC Next Fest builds. Boot already passes itch WASM MIME checks—this failure happens after load when visibility changes re-allocate textures, audio decode buffers, or threaded work on a fragmented heap. Distinct from MIME boot hang and from audio silent first load.

Fastest safe fix: Reproduce with a blur/focus matrix → on visibilitychange pause tree + suspend audio when hidden → release prior floor epoch assets → cap texture cache → run ten refocus cycles on Chrome + Firefox → file wasm_memory_smoke_receipt_v1.json before updating the public demo link.

Direct answer

Browser tabs are not desktop play mode. When a tab goes hidden, Chromium may throttle, discard, or trim WASM memory; on refocus Godot may re-init subsystems while old floor scenes, atlases, and audio buses still hold references. WASM cannot grow like OS virtual memory—fix lifecycle on hidden and epoch teardown on floor change, not “buy more RAM.”

Why this issue spikes in H2 2026

  1. Dual-SKU demos — PC floor logic reused for itch without a web memory budget.
  2. Fest clip traffic — Streamers alt-tab constantly; QA only tests uninterrupted play.
  3. Godot 4.5 threaded web exports — Loader threads keep allocating if the tree never pauses on hidden (COOP/SAB hosting).
  4. Roguelite session length — Players exceed the heap that passed a five-minute smoke test.
  5. Tab discard policy — Laptops aggressively reclaim background tabs; refocus replays init on a smaller budget (WASM ceiling playbook).

Pair with unscoped HTML5 SKU opinion when marketing promises a full PC run in the browser.

Symptoms and search phrases

  • Freeze or full reload only after alt-tab / minimize / second monitor switch.
  • Console: memory access out of bounds, abort(), wasm memory.
  • Works in editor Play for 30+ minutes; fails on itch in 15–40 minutes with refocus.
  • Memory column in Chrome Task Manager climbs each refocus cycle.
  • Threaded export: hang correlates with SharedArrayBuffer hosts and background throttling.
  • No MIME error—boot hang help already green.

Root causes (check in order)

  1. No pause on document.hidden — simulation + loaders continue off-screen.
  2. Textures/audio not released on floor transition—prior floors stay referenced.
  3. Missing window.blur / visibilitychange wiring in web bootstrap.
  4. Thread pool keeps allocating while tab backgrounded (threaded preset).
  5. SAB pool too small for refocus re-init path—or growth disabled with assets too large.
  6. Tab discard — browser kills instance; reload doubles peak if caches not cleared.
  7. Press-kit iframe embed — parent page lifecycle + tighter memory vs itch top-level tab.

Beginner path (first 30 minutes)

Prerequisites: Godot 4.5.x, HTML5 export preset, itch demo URL, Chrome DevTools.

  1. Open demo → play until floor 2–3 loads.
  2. Open DevTools → Performance monitor or Task Manager → note memory.
  3. Alt-tab away 30 seconds → return.
  4. If freeze → open Console; screenshot errors.
  5. Repeat ten times—note if failure is cycle 1 or cycle 6+.
  6. Retry in Firefox (different discard timing).
  7. If never reproduces without refocus → suspect long-session OOM; still add hidden pause (prevention).

Common mistake: Testing only in editor or only in uninterrupted fullscreen—refocus is the test case.

Fastest safe fix path

Step 1 — Repro matrix (blur / focus / discard)

Case Action Pass
A Alt-tab 30s, return No freeze; memory stable ±10%
B Minimize window 60s Same
C Open second tab on same window, return Same
D itch embed iframe on press kit Same as top-level or document embed gap
E Ten rapid alt-tabs No OOM by cycle 10

Log browser + preset in release-evidence/qa-and-repro/web-refocus-matrix.md.

Step 2 — Pause tree and suspend audio when hidden

Add an autoload or main scene hook (GDScript 4.x):

# WebVisibility.gd (autoload)
func _ready() -> void:
    if OS.has_feature("web"):
        var win := JavaScriptBridge.get_interface("window")
        if win:
            win.addEventListener("visibilitychange", Callable(self, "_on_visibility_change"))

func _on_visibility_change(_ev = null) -> void:
    var hidden := JavaScriptBridge.eval("document.hidden", true)
    if hidden:
        get_tree().paused = true
        AudioServer.set_bus_mute(AudioServer.get_bus_index("Master"), true)
    else:
        get_tree().paused = false
        AudioServer.set_bus_mute(AudioServer.get_bus_index("Master"), false)

Pro tip: Do not spawn new ResourceLoader.load_threaded_request while document.hidden is true.

Step 3 — Floor epoch teardown (release prior floor)

On floor transition, free the previous scene instance and clear caches:

func _transition_to_floor(packed: PackedScene) -> void:
    if _current_floor:
        _current_floor.queue_free()
        _current_floor = null
    # Optional: drop atlases held only by prior floor
    ResourceLoader.clear_cache() # use sparingly; prefer targeted unref
    _current_floor = packed.instantiate()
    add_child(_current_floor)

See WASM memory ceiling playbook for epoch tables and roguelite scope caps.

Step 4 — Cap texture cache and streaming

Knob Web demo guidance
Max texture size 2048 or 1024 for UI atlases on web SKU
Simultaneous floors in memory 1 active + optional preload 1
Particles/VFX Disable heavy GPUParticles on web preset
Audio streams Stream Ogg; avoid loading full WAV banks

Export a Web_demo_scope preset separate from PC—document deltas in release-evidence/README.

Step 5 — Threaded export decision

If refocus OOM only on threaded itch preset:

  • Re-test non-threaded export (MIME help preset table).
  • Or keep threads but enforce Step 2–3 strictly.
  • Confirm itch SharedArrayBuffer toggle matches COOP help.

Step 6 — Emscripten memory flags (advanced)

When custom export hooks exist, align with Emscripten memory growth docs—raising MAXIMUM_MEMORY without teardown delays OOM; it does not fix retained floors. Prefer release discipline first.

Verification checklist

  • [ ] Ten refocus cycles without OOM on Chrome (latest)
  • [ ] Same on Firefox
  • [ ] Chrome Task Manager: memory does not climb unbounded across cycles
  • [ ] Hidden tab: get_tree().paused == true (debug overlay or log)
  • [ ] Floor 6+ transition still releases prior floor (heap step-down visible)
  • [ ] itch embed + fullscreen both pass matrix row D
  • [ ] wasm_memory_smoke_receipt_v1.json committed with pass: true

Working dev path — wasm_memory_smoke_receipt_v1.json

{
  "schema": "wasm_memory_smoke_receipt_v1",
  "engine": "godot-4.5",
  "host": "itch.io",
  "export_preset": "Web_demo_nonthreaded",
  "checked_at_utc": "2026-05-24T18:00:00Z",
  "browsers": ["chrome", "firefox"],
  "refocus_cycles": 10,
  "refocus_pass": true,
  "floor_reached": 6,
  "peak_heap_mb_estimate": 412,
  "visibility_pause_enabled": true,
  "floor_epoch_teardown": true,
  "pass": true
}

Wire a manual or CI step: human runs matrix E, fills receipt, attaches to Wednesday demo smoke ritual folder beside BUILD_RECEIPT.

Proof table (fest gate)

Check Evidence Owner
Refocus ×10 wasm_memory_smoke_receipt_v1.json QA
Hidden pause Screenshot of debug flag or log line Engineering
Web SKU scope doc Web_demo_scope preset diff vs PC Design
Embed vs tab Matrix row D notes Marketing
Long session 45m Optional heap log in web-heap-log.md QA lead

Alternative fixes

Branch When Action
Boot never loads MIME / SAB errors itch WASM MIME fix
Audio only broken after refocus Buses not suspended Web audio gesture help
OOM without refocus Long roguelite run WASM ceiling playbook
Phaser/HTML5 sibling Cross-engine team Phaser tab-refocus OOM blog
Export preset vanished After 4.5 patch EditorExportPlugin preset help

Prevention

  • Add refocus cycles to browser demo scope doc before trailer capture.
  • Block public itch updates until receipt pass: true.
  • Cap web SKU floors/enemies separately from PC in design doc.
  • Run web CI matrix preflight when toggling threaded presets.
  • Pair with 5-day crash log challenge for repro folders.

FAQ

Is this the same as itch WASM MIME boot hang?
No. MIME failures stall at the progress bar before main scene. Refocus OOM happens after gameplay starts.

Will increasing Godot export memory fix it?
Only buys time. Without epoch teardown and hidden pause, you will still OOM on long runs or repeated refocus.

Does editor Play reproduce it?
Rarely. Test exported HTML5 on itch or python -m http.server with the same preset.

Should I disable threads?
Try non-threaded if refocus OOM correlates with background loader activity; many jams ship non-threaded successfully.

Steam wrapper vs itch?
Steam browser shell has its own lifecycle; run matrix on the same surface players use in fest clips.

Related links

Run ten refocus cycles before you send the fest clip link—uninterrupted playtests lie about browser demos.