Last month I set out to answer a simple question: how small and how self-contained can a Unity WebGL playable ad actually be? The short answer, after three builds: 4.7 MiB for a match-3 in one file, ~8.2 MiB for an idle-growth game in one file, and a 9.61 MiB 18-file 3D microsite — and the microsite is deliberately not single-file, because it doesn't ship inside an ad network.
Playable ads live inside ad network SDKs (MRAID containers), and networks impose hard caps: file size, single-file delivery, no external requests, muted audio until first interaction, and a CTA that fires mraid.open(). Those constraints rule out the default Unity WebGL export, which ships as index.html + Build/*.unityweb + TemplateData/*. My pipeline became: Unity 2022 → WebGL export → repack.
1. Export settings that matter
- Brotli/gzip compression on the build payload
- Strip engine code, high managed stripping level
- No exceptions, no stack traces (smaller + faster startup)
- IL2CPP + code stripping
2. Repack to a single file
The default export ships as index.html + Build/*.unityweb + TemplateData/*. For playable delivery this gets repacked into one inlined index.html:
- base64-inline the
.wasm,.data, and.framework.jspayloads - inline the loader
- no external requests for the two playable builds; in DevTools, the network panel shows exactly one document request
This applies to the two playable builds (Prism Vein and Core Forge). The 3D microsite (Atlas Relay) stays as 18 files — 9.61 MiB — because it is not a playable ad and does not need the single-file constraint.
3. Audio discipline
Network rules require muted audio until first tap. I keep an AudioContext suspended until the first pointer event, then resume it — sounds only fire after real interaction.
4. CTA through MRAID
The end-card CTA checks for mraid availability:
- inside an MRAID container →
mraid.open(url) - outside (plain web demo) →
window.open(url)fallback
I wrapped the match-3 build in a MRAID 2.0 harness locally (getVersion() returns 2.0) to verify the bridge works.
Size results
| Build | Size | Languages | Single file? |
|---|---|---|---|
| Match-3 (Prism Vein) | 4.7 MiB | EN / ZH (two separate builds) | Yes |
| Idle-growth (Core Forge) | ~8.2 MiB | EN+ZH runtime switch in one build | Yes |
| 3D microsite (Atlas Relay) | 9.61 MiB (18 files) | EN+ZH | No |
Most of the match-3 savings came from texture discipline (palette-swapped sprites) and audio compression, not from hacking the engine.
What I'd tell anyone building playables
- Budget the size cap first, design second
- Test inside a real MRAID harness, not just a browser tab
- Keep one inlined file as the single source of truth for playable delivery — and don't force single-file on things that don't need it
A question for you
I'm a solo developer building these end-to-end (Unity 2022 → WebGL → repack). What's the smallest single-file Unity WebGL payload you've shipped to an ad network, and how do you handle a cap under 5 MiB? If this helped, leave a reaction or ask below — I'll answer in the comments. All three demos are live at https://www.lumenformlab.com
Top comments (0)