Quick answer: Build with -s ALLOW_MEMORY_GROWTH=1, set a generous INITIAL_MEMORY, cap with MAXIMUM_MEMORY, and re-acquire HEAP* typed-array views after any growth.
An Emscripten-built game runs for a while, then crashes with “Cannot enlarge memory arrays” or a detached ArrayBuffer error.
Allow Growth
emcc ... -s ALLOW_MEMORY_GROWTH=1 \
-s INITIAL_MEMORY=268435456 \
-s MAXIMUM_MEMORY=1073741824
Without ALLOW_MEMORY_GROWTH, the WASM heap is fixed — the first allocation past INITIAL_MEMORY crashes. With it, the heap can grow up to MAXIMUM_MEMORY.
Set a Realistic Initial Size
Growth isn’t free — it reallocates the whole buffer. Profile your real peak usage and set INITIAL_MEMORY close to it so growth rarely (or never) happens at runtime.
Re-Acquire HEAP Views After Growth
When the heap grows, the underlying ArrayBuffer is replaced — any cached HEAPU8, HEAPF32, etc. reference is now detached. Don’t cache them across frames; re-read Module.HEAPU8 after any call that might allocate.
Cap the Maximum
Set MAXIMUM_MEMORY so a leak fails loudly at a known ceiling instead of consuming the whole tab and getting OOM-killed by the browser unpredictably.
Verifying
Play a long session through memory-heavy scenes. The heap grows once or twice (or not at all with a good initial size) and never crashes. Detached-buffer errors are gone.
“Allow growth, size the initial heap well, cap the max, and never cache HEAP views.”
Mobile browser tabs have tight memory budgets — test your MAXIMUM_MEMORY on a real phone, not just desktop Chrome.