Quick answer: Enable Memory Growth in Player Settings > WebGL > Publishing Settings, set Initial Memory Size to 256–512 MB, compress all textures with Crunch or ASTC, and strip unused code via IL2CPP Code Stripping High. If still failing on mobile, show a “desktop recommended” notice.
Here is how to fix Unity WebGL build memory out of bounds. You export a WebGL build, upload to itch.io or host on a website, and the game shows a loading bar that freezes at 80% with a red error: “memory access out of bounds” or “Uncaught RangeError: WebAssembly.Memory(): could not allocate memory.” Or it loads fully, plays for a minute, then crashes with the same message. The error message is cryptic, the browser console is useless, and there is no stack trace pointing at your code.
The Symptom
Browser console shows one of:
Uncaught abort(OOM): memory access out of boundsUncaught RangeError: WebAssembly.Memory(): could not allocate 1073741824 bytesAn error occurred running the Unity content on this page- The game loads partially, freezes, then shows the above
Desktop Chrome and Firefox are the most tolerant. Safari, iOS Safari, and older Android browsers fail earliest. Reproducing only on certain machines is a strong indicator of memory limits.
What Causes This
Fixed heap too small. WebGL builds allocate a fixed-size WebAssembly heap at startup. If Unity asks for more memory than the heap size, allocation fails — and in WebAssembly, an out-of-bounds write doesn’t just “corrupt memory,” it terminates execution immediately.
Memory Growth disabled. Without Memory Growth, the heap is fixed. With it, the heap can grow at runtime up to a maximum (2 GB on most browsers, 4 GB on desktop Chrome/Firefox with 4 GB memory flag).
Large uncompressed textures. Textures that live in memory consume large amounts of heap. A 2048x2048 uncompressed RGBA32 texture is 16 MB in memory — 100 of those is 1.6 GB. Crunch compression for ETC2/ASTC cuts memory by 3–4x.
Scene loaded additively without unload. Additive scene loading accumulates memory. Loading 5 additive scenes without unloading any is 5x the memory of a single-scene game.
AudioClips loaded in memory. Uncompressed or pre-decompressed audio clips consume memory permanently. A 3-minute music track at 44.1 kHz stereo uncompressed is 30 MB in RAM.
Browser limits. iOS Safari imposes a hard 2 GB WebAssembly heap cap. Desktop browsers cap at 2 GB by default, 4 GB with flag. Embedded browsers (Discord activities, Roblox iframes) may cap lower.
The Fix
Step 1: Enable Memory Growth and set Initial Size. Player Settings > WebGL > Publishing Settings:
- Memory Growth Mode: Linear or Geometric (Linear preferred for predictable growth)
- Initial Memory Size: 256 MB for small games, 512 MB for medium, 1024 MB for large
- Maximum Memory Size: 2048 MB (or higher if your game truly needs it, but expect failures on mobile)
- Memory Growth Step: 16 MB or 32 MB (smaller = less wasted memory)
Setting Initial too low causes slow start as memory grows. Too high causes failure on low-RAM mobile devices. 256–512 MB initial is the sweet spot for most games.
Step 2: Compress textures aggressively. In texture import settings:
- Set Max Size to 1024 or 2048 — rarely need 4096 for WebGL
- Enable Crunch Compression where supported
- Format: DXT5/DXT1 for WebGL 1, ASTC_6x6 for WebGL 2
- Mip Maps: disable for UI sprites that do not need them
Run the Memory Profiler in Editor, sort by Texture2D, look for your largest textures, and compress them. Reducing peak texture memory by 50% is often achievable with no visible quality loss.
Step 3: Stream audio. Set audio clips to “Streaming” Load Type in import settings. Streaming clips do not live fully in memory; they decode on demand. The tradeoff is slight latency on first play, which is acceptable for music and ambient audio (not for tight SFX).
For short SFX, use “Decompress On Load” which costs RAM but plays instantly. Music and long voiceover: Streaming.
Step 4: Enable IL2CPP code stripping. Player Settings > Other Settings > Managed Stripping Level: High. This removes unused .NET assemblies from the build, reducing both code size and memory footprint. Test thoroughly — High stripping can remove things reflection needs. Add a link.xml to preserve types used via reflection.
<!-- Assets/link.xml -->
<linker>
<assembly fullname="MyGame" preserve="all"/>
<assembly fullname="Newtonsoft.Json" preserve="all"/>
</linker>
Add assemblies that use reflection or get stripped aggressively. Run the build and confirm no runtime crashes.
Measuring Actual Memory Use
Enable the Unity Memory Profiler in Player Settings > WebGL > Publishing Settings (or add the “Profiler” package). Connect via WebGL support in the Profiler. Take snapshots to see peak memory, which assets dominate, and where growth happens. Without this data, optimization is guessing.
In the browser, use unityInstance.Module.HEAPU8.byteLength / 1048576 in DevTools console to see current heap size in MB.
Mobile-Specific Strategy
If your game targets mobile WebGL, plan for 1–1.5 GB peak memory maximum. Serve a lightweight version to mobile and a full version to desktop based on user agent:
// In your index.html
if (/iPad|iPhone|iPod/.test(navigator.userAgent)) {
alert("iOS: please use desktop for best experience");
}
Or ship two builds (lite and full) and pick based on device.
“WebAssembly memory is not your computer’s memory. It is a fixed box with a hard edge and a hard failure mode. Budget accordingly.”
Related Issues
For HTML5-specific setup, see Setting Up Crash Reporting for HTML5 Browser Games. For general memory leak patterns, Unity Memory Leak Texture Not Releasing covers related issues.
Memory Growth on, Initial 256 MB, textures crunched, audio streamed. WebGL ships.