Godot 4.5 Web Export WASM Memory Ceiling - H2 2026 Browser Demo Trend Playbook

Your Godot editor playtest runs twenty minutes. Your itch HTML5 demo dies at minute thirty-eight when the player returns from Discord. The console says wasm memory limit exceeded or the canvas goes gray with no stack trace. You did not change code between builds—you changed session length and heap budget.
H2 2026 festival traffic pushes more Godot teams to ship browser demos beside PC SKUs. WASM is not desktop RAM. This Trend-Jacking playbook explains the ceiling, the failure mode, and the discipline that pairs with threaded floor loading without pretending web is free.
Why this matters now (May 2026)
Three pressures stack this quarter:
- Dual-SKU demos — PC Next Fest build plus itch browser slice; teams reuse PC floor logic that allocates entire scenes.
- Roguelite session length — Players expect 45–75 minute runs; web heap does not forgive unbounded floor retention.
- Tab discard — Laptops discard background tabs; refocus replays initialization on a fragmented heap (Phaser sibling pattern).
Community threads now rhyme: “Floor 6, alt-tab, white screen.” That is often WASM OOM, not difficulty.
The failure mode in one paragraph
Naive pattern: Each floor change_scene_to_packed() a large scene, keep prior floors referenced in autoload caches, load all textures synchronously, never drop GPU or WASM allocations on transition.
What breaks: Godot web export runs inside a fixed WASM memory growth policy and browser tab budgets. Retained scenes + atlases + audio decode buffers compound. Floor seven exceeds what floor one fit because you never released floor three’s resources.
Honest limit: Streaming buys session stability; it does not fix unbounded content growth per floor.
What changed in H2 2026 (trend framing)
Nothing magical shipped in a single Godot patch note that “caused” WASM OOMs. The market changed: more roguelites, longer intended sessions, more dual PC+browser SKUs, and more players on 8 GB laptops with aggressive tab discard. Godot 4.5 made threaded loading and larger default workflows easier—which helps PC—but teams imported PC habits into web without epoch discipline. The trend is behavioral adoption, not a semver regression.
Who should read this
- Teams shipping HTML5 demos for October 2026 Next Fest or summer festivals
- Leads who fixed web MIME/audio smoke tests but still see OOM
- Engineers using floor-load coordinator on PC who have not web-gated it
Direct answer (TL;DR)
- Measure heap in Chrome during a 60-minute scripted run—not editor.
- Add floor epoch teardown: release prior floor scene tree + unref atlases.
- Prefer threaded preload only when host headers allow.
- Cap demo scope (floors, enemies, VFX) for web SKU separately from PC.
- Log
release-evidence/qa-and-repro/web-heap-log.mdfor fest submissions.
WASM vs desktop mental model
| Desktop export | Web export |
|---|---|
| OS virtual memory | Browser tab cap |
| Lazy GPU driver recovery | Context loss on trim |
| Long playtests normal | Alt-tab is a test case |
| Bigger default assets OK | MB discipline required |
Treat web as a different SKU with a memory budget table, not a checkbox in export dialog.
Floor epoch pattern (Godot 4.5)
Epoch = one floor lifecycle from load to transition.
# Autoload FloorEpoch.gd (pattern sketch)
var epoch: int = 0
func begin_floor() -> void:
epoch += 1
_release_previous_floor()
func _release_previous_floor() -> void:
# queue_free old floor root, clear caches keyed by epoch-1
pass
Pair with threaded ResourceLoader.load_threaded_request from loader guide—but await completion before swapping epoch.
What to unload on epoch end
| Asset class | Action |
|---|---|
| Previous floor scene | queue_free root; null references |
| Tilemaps / meshes | Drop refs; call ResourceLoader.clear_cache sparingly |
| Audio streams | Stop players; unref long music |
| Particles | Kill one-shots tied to old epoch |
| Autoload caches | Key by epoch; delete prior keys |
Warning: clear_cache() every frame stutters—use on epoch boundary only.
Measurement sprint (ninety minutes)
| Minutes | Task |
|---|---|
| 0–15 | Export HTML5 debug; host locally with correct COOP/COEP if using threads |
| 15–35 | Chrome DevTools → Memory → heap snapshot at floor 1 |
| 35–55 | Script bot plays to floor 6; snapshot again |
| 55–70 | Alt-tab 60s; return; note delta |
| 70–85 | Document MB growth per floor in markdown |
| 85–90 | File row in release-evidence |
If growth is linear per floor, you leak on transition—not on content size alone.
Threaded loading on web
Threaded Godot web builds require hosting discipline. If headers are wrong, fall back to non-threaded web demo branch rather than shipping broken threads.
Cross-read:
PC threaded loader guide does not automatically authorize web threads.
Demo scope card (publish on itch page)
Template players appreciate:
Browser demo limits: Floors 1–6, ~45 minute design cap, saves disabled, best on desktop Chrome/Edge. Alt-tab once is OK; if canvas blanks, refresh—known browser memory limit.
Honesty reduces refund noise versus mysterious crashes.
Tab-refocus protocol
- On
visibilitychangehidden → pause simulation, drop nonessential particles. - On visible → do not allocate new atlases; validate epoch still current.
- If WebGL context lost → show refresh UI, not infinite reload loop.
Document in marketing-and-demo/ truth folder per wishlist truth audit.
Visibility handler sketch (web)
func _ready() -> void:
if OS.has_feature("web"):
get_tree().root.connect("visibility_changed", _on_visibility_changed)
func _on_visibility_changed() -> void:
if not get_tree().root.visible:
get_tree().paused = true
_trim_ephemeral_vfx()
else:
get_tree().paused = false
if _webgl_context_lost():
_show_refresh_overlay()
_trim_ephemeral_vfx() should not allocate. _webgl_context_lost() checks renderer API where available; fallback is detecting zero-size viewport after refocus.
Threaded load status polling (web-safe)
When threads are enabled and headers validate:
var path := "res://floors/next_floor.tscn"
ResourceLoader.load_threaded_request(path)
while true:
var st := ResourceLoader.load_threaded_get_status(path)
if st == ResourceLoader.THREAD_LOAD_LOADED:
break
if st == ResourceLoader.THREAD_LOAD_FAILED:
push_error("web load failed")
return
await get_tree().process_frame
var packed := ResourceLoader.load_threaded_get(path)
Never swap epoch until THREAD_LOAD_LOADED. On web, cap concurrent requests to one—parallel loads spike heap.
CI gate (lightweight)
Add a headless or scripted smoke job:
| Step | Fail build if |
|---|---|
| Export web preset | Export errors |
| Run 15-min bot | Console OOM string |
| Compare manifest | web_allowed false floor reached |
Artifact: upload web-heap-log.md from CI to release-evidence tag.
Fest calendar (May–October 2026)
| Month | Web demo focus |
|---|---|
| May | Split presets + first heap log |
| June | Epoch teardown merged to main |
| July | Alt-tab protocol + scope card |
| August | Freeze asset sizes |
| September | Header/MIME re-verify only |
| October | No new art; refresh link if patch |
Align with festival calendar.
itch embed vs standalone tab
Embedded iframes on press kits often run with stricter memory than a top-level itch page. Test:
- Top-level itch game page
- Embed on your press HTML
- Steam community link wrapper if applicable
Log which context failed in heap markdown—marketing can warn press.
PC + web dual wishlist strategy
Store page may wishlist PC SKU while itch hosts web demo. Ensure price-anchor and short description clarify which build is which—players anger-spike when web demo scope mismatches PC trailer.
Community Hub copy template
Browser demo note: This HTML5 build is capped at floors 1–6 (~45 min) to stay within browser memory limits. For full scope, wishlist the PC build. If the canvas goes blank after alt-tab, refresh the page.
Pin during fest week; reduces duplicate bug threads.
Engine version pinning
Godot 4.5 web export templates moved between 4.4.x—pin editor version in release-evidence/build-and-binary/ when sharing demo links. “Works on my machine” disputes are version disputes.
Audio-specific heap leaks
Stream players left playing after epoch change keep decode buffers alive. On begin_floor():
for p in get_tree().get_nodes_in_group("music_players"):
p.stop()
Group music buses explicitly—ambient from floor N should not survive floor N+1 on web.
Particle and shader caution
GPU particles and heavy shaders survive epoch bugs longer than meshes—they respawn allocations on refocus. Web demo SKU should disable nonessential particles entirely.
Comparison to Phaser trend (sibling)
| Dimension | Phaser 3.90 trend | Godot 4.5 web trend |
|---|---|---|
| Unit of leak | Tilemap grid | Retained scenes |
| Fix axis | Chunk stream | Floor epoch |
| Host issue | Tab discard | WASM + tab discard |
| Guide pairing | Phaser preflight chapter | Floor coordinator |
Read both if your studio prototypes in Phaser and ships in Godot.
Publisher conversation framing
Publishers ask “is the demo stable?” Show:
- Heap log with flat delta after epoch fix
- Scope card URL
- Video of alt-tab recovery (refresh overlay, not silent crash)
Avoid promising “identical PC demo in browser.”
Post-mortem questions after a fest OOM spike
- Did heap grow linearly per floor?
- Was web preset used for the uploaded file?
- Did marketing link embed or top-level?
- Were threads enabled without headers?
- Did PC trailer show content absent from web manifest?
Answer in writing in release-evidence/qa-and-repro/post-fest-web.md for next year.
Pairs and contrasts
| Topic | Relationship |
|---|---|
| Phaser tilemap OOM trend | Sibling engine, same player behavior |
| PC threaded loader | Upstream; web gates apply |
| Floor coordinator beginner | Implement epoch there |
| Stack rationalization | One web SKU worth maintaining |
Export preset discipline (web branch)
Maintain two export presets in export_presets.cfg:
| Preset | Purpose |
|---|---|
Web-Demo-Fest |
Reduced textures, fewer floors, threads off if headers fail |
PC-NextFest |
Full scope per vertical slice honesty |
Use feature tags:
# Example pattern — adapt to your repo
web_demo=true
Gate heavy systems with if OS.has_feature("web_demo"): — cut procedural breadth, not just graphics.
Texture and audio budgets (starter table)
| Asset | PC demo | Web demo |
|---|---|---|
| Max texture size | 2048 | 1024 |
| Music streams | 2 concurrent | 1 |
| Ambient loops | 3 | 1 |
| Floor scene MB target | team metric | team metric −30% |
Reimport atlases when switching presets—do not rely on runtime downscale alone.
Floor manifest JSON (web-safe)
Extend floor coordinator manifest with web fields:
{
"floor_id": "f06_boss",
"scene": "res://floors/f06_boss.tscn",
"web_allowed": true,
"estimated_mb": 42
}
Coordinator skips disallowed floors in web SKU or shows “demo ends” card—better than OOM.
Hosting checklist (itch + Cloudflare + self-host)
| Check | Pass criteria |
|---|---|
| COOP/COEP | Matches Godot thread preset if enabled |
| MIME | .wasm, .pck, audio types correct per smoke test article |
| Compression | Brotli/gzip; verify download size vs heap |
| Embed iframe | Test memory in embed, not only top-level tab |
| CDN cache | Versioned URLs after demo patch |
Log host URL and header snapshot in release-evidence.
Ninety-second preflight (before sharing demo link)
- Cold load in Chrome guest profile.
- Play to floor 3 without devtools open.
- Alt-tab 30 seconds; return.
- Open heap snapshot only if step 3 passed.
- Compare
estimated_mbsum to heap delta.
Fail any step → block link post in Community Hub.
Synthesized player patterns (forums, not metrics)
Reports cluster as:
- Minute 20–45 first OOM (unreleased floor assets)
- After alt-tab white canvas (trim + refocus)
- Floor 8+ in demos advertised as “endless” (scope honesty failure)
Do not cite invented studio recovery percentages—track your heap log.
Integration with operating review
Add to Block 1 engineering:
- Web demo heap log fresh? Y/N
- Last epoch teardown test build:
- Web SKU scope card published? Y/N
When to ship PC-only for fest
Ship PC-only when:
- Threaded web cannot pass headers before submission deadline
- Art pass cannot hit web texture budget without unacceptable rework
- Gameplay requires save systems you will not support in browser
Say so on the store page—truth audit beats silent omission.
Soft-lock and replay on web
Cert-style replay hooks from deterministic soft-lock guide are harder on web—prioritize epoch cleanliness over deep replay for browser SKU. PC build keeps replay packet for partners.
Marketing visual stack
Browser OOM crashes hurt screenshot composition trust if store shows PC-only spectacle—label web limits in short description.
Decision tree
Shipping HTML5 demo?
no → Archive this playbook for later
yes → Split export preset
↓
Heap linear per floor?
yes → Fix epoch teardown
no → Check texture/audio budget
↓
Alt-tab fails?
yes → visibilitychange protocol + scope card
no → Log evidence; monitor weekly
Pro tips (2026)
- Run heap tests on 8 GB RAM laptop, not dev workstation.
- Version demo URL in
release-evidencewhen MB target changes. - Pair web demo with 14 screenshot tools only after scope honest.
- Do not hotfix fest week with bigger assets—only smaller.
- Watch Phaser OOM threads for player vocabulary—you will get the same reports.
Common mistakes (2026)
- Identical project.godot for PC and web without feature tags
- Keeping all defeated enemy scenes for “effects”
- Infinite audio decode cache
- Ignoring itch embed vs standalone tab memory differences
- Testing only first floor in CI
- Shipping web demo with full-resolution 4K textures
Key takeaways
- WASM ceiling is a design constraint, not a surprise bug.
- Floor epochs must release prior floor resources.
- Measure in browser, sixty-plus minutes, with alt-tab.
- Threaded web needs header discipline or a fallback branch.
- Publish demo scope card on store/itch pages.
- Log heap tables in release-evidence.
- Web SKU ≠ PC SKU scope.
- Tab refocus is a required QA case in 2026.
- Pair with Godot loader and coordinator guides.
- Trend pressure rises with H2 roguelite density.
- Music buses and particles need explicit epoch cleanup on web.
- Test itch embed, not only top-level tabs.
Seven-day stabilization sprint (optional)
| Day | Focus |
|---|---|
| Mon | Split export presets |
| Tue | Implement epoch teardown |
| Wed | Heap log floors 1–6 |
| Thu | Alt-tab protocol |
| Fri | Scope card + Hub pin |
| Sat | CI smoke export |
| Sun | Evidence README + operating review |
Pairs with 7-day truth audit in a different week—do not run both simultaneously if team is two people.
FAQ
Is this Godot-only?
Pattern applies to any WASM game; Godot 4.5 web export is the 2026 spike context.
Can we raise WASM limit?
Export settings and host matter—still finite; design for release.
Do we drop PC quality?
Separate budgets per SKU; PC keeps scope.
What heap MB target?
Team-specific; track delta per floor, not magic number.
Steam wrapper vs itch tab?
Test both; wrappers add overhead.
We use only PC export for fest.
Still read scope honesty for store copy referencing browser plans later.
Does ResourceLoader.clear_cache() fix everything?
No—fix retained scene refs first; cache clear is epoch punctuation.
Can we stream like Phaser chunks?
Conceptually yes—Godot uses scene chunks + epoch, not necessarily tile chunks.
What about mobile web?
Stricter caps; test Safari iOS separately if you link mobile web demo.
Extended FAQ
Editor says 60 FPS so we are fine.
Editor is not WASM; browser is the truth environment.
Our game is not a roguelite.
Epoch teardown still helps any multi-scene web demo.
We host on GitHub Pages.
Headers and MIME still apply; size limits bite sooner.
Partner wants both builds in one zip.
Two folders, two README heap logs, two build IDs.
Should we use GDExtension on web?
Treat extensions as part of heap budget; many teams disable for demo SKU.
Multiplayer web demo?
Memory doubles fast—separate playbook; cap players to two or ship PC-only.
Heap log markdown template
# Web heap log - <game> - <date>
- Editor: Godot 4.5.x
- Export preset: Web-Demo-Fest
- Host: itch top-level / embed / other
- Build ID:
| Floor | Heap MB (Chrome) | Notes |
|-------|------------------|-------|
| 1 | | |
| 3 | | |
| 6 | | post alt-tab |
Pass criteria: delta floor-to-floor < team threshold; alt-tab no blank canvas.
Store under release-evidence/qa-and-repro/ and link from operating review Block 1.
When not to chase web demo
If your game relies on large open-world streaming already at PC quality, a browser demo may be marketing-negative even if technically possible—ship PC demo + honest store copy instead of a crashy browser slice.
Internal links checklist (read next)
After this trend pass, execute in order if fest is approaching:
- Floor coordinator beginner pipeline — add
web_allowedmanifest fields. - Threaded ResourceLoader guide — gate threads on web.
- Web smoke tests — headers and MIME before heap tuning.
- Truth audit challenge — scope card on itch + Steam.
- Release-evidence taxonomy — store heap log and export preset version.
Skipping step 3 and jumping to epoch teardown still leaves demos that fail on hosting before memory matters.
One-sentence team policy (paste in Discord pin)
We do not merge web demo export changes without a heap log row, a manifest
web_allowedcheck, and an alt-tab pass on the slowest laptop in the studio.
Short policies beat long trend articles in day-to-day chat. Pin the policy next to your export preset notes so nobody uploads the PC .pck by mistake during a late-night fest upload.
Close: Browser demos win festivals when they survive real sessions—not when they survive the editor. Epoch your floors before October traffic proves you did not. Log the heap, publish the scope card, and treat alt-tab as a first-class test—not a player skill issue. The trend is already visible in Hub threads; your move is to ship discipline before the threads are about you.