Why this happens
A Playworks/Luna export isn't one flat bundle — it's an index.html, an engine/ folder with the runtime scripts, an assets/ folder, and (in single-HTML/self-contained builds) a cache/<id>/ folder containing scripts.js, jsons.js, and blobs.js — Luna's own cache system, which already knows how to embed audio and binary blobs via window.sounds/window.blobs and its decompressString/decompressArrayBuffer helpers (brotli-compressed, base64 or base122-encoded). Nothing about audio or blobs needs special handling on top of that — Luna does it natively. Assets go missing when one of these pieces doesn't line up with what the final package expects:
- A relative-path asset was never actually written into
engine/. In folder-mode output (Google's ZIP, Mintegral's nested folder), the export still references files by relative path. If Playworks/Luna exported an incompleteengine/orassets/tree — a texture atlas or audio file the build referenced but didn't emit — that reference stays broken no matter how faithfully it's copied into the network package. This is a source-export gap, not a conversion-step gap. - A cache manifest entry exists, but the payload never landed. Luna's
jsons.jscan list a sound or blob key thatblobs.jsnever actually populated — usually from an interrupted or partial Unity export. The cache scripts still get inlined verbatim;window.sounds/window.blobsthen has a hole at runtime, and that one asset silently fails to play or render even though everything else in the same cache works. - The file extension isn't one the media inliner recognizes. Direct
<img>/<video>references in the export's own markup (outside Luna's cache system) get inlined by extension — currently.png,.jpg,.jpeg,.webp,.gif, and.mp4. A reference to an extension outside that list (an unusual video codec's container, for example) is left as a relative path with nothing behind it once the file is embedded, and self-contained networks like AppLovin and Meta allow zero external requests — so that reference is dead on arrival. - A decompression mismatch on the cache payload. The single-HTML pipeline rewrites every
decompressString(...)/decompressArrayBuffer(...)call it recognizes, decompressing the original brotli payload and re-encoding it as base64 so it survives every network's serving layer. If a cache script was hand-edited, produced by a mismatched Luna version, or uses a call shape the rewrite doesn't match, that payload is left in its original encoding — and the runtime that expects the rewritten form fails to decode it, so its assets never resolve. - Wrong cache ID selected when a build has more than one. Playworks/Luna can leave multiple numbered folders under
cache/from previous builds. Pointing a conversion at the wrong one pulls in scripts, JSON, and blobs from a different build than the one actually referenced byindex.html, producing assets that don't match what the visuals expect.
How to diagnose it
Before assuming it's a conversion problem, check the source export itself. Playable Kit's free Playable Ad Validator checks a Luna/Playworks export or a converted package for exactly this class of issue — a missing entry file, an incomplete engine/ tree, a cache folder missing one of its three required files, or a broken inline script — before you spend a review cycle finding out from the network instead. Run it on the original export first; if the problem exists there, no converter can fix it.
What's handled automatically vs. what stays a source-export fix
| Cause | PlayableKit's conversion | What you still need to fix |
|---|---|---|
Asset never written into engine//assets/ | Copies exactly what's in the export | Re-export from Unity/Playworks/Luna with the asset included |
| Cache manifest entry with no matching blob/sound data | Inlines the cache scripts as-is | Re-export the affected cache; the hole exists upstream |
| Unsupported image/video extension | Inlines every extension in the supported set automatically | Convert the asset to a supported format (.png/.jpg/.webp/.gif/.mp4) before export |
| Cache decompression/encoding mismatch | Rewrites recognized decompressString/decompressArrayBuffer calls to network-safe base64 automatically | Avoid hand-editing cache scripts; re-export with a consistent Luna version |
| Ambiguous or wrong cache ID | Lets you pin a specific cache ID for the conversion | Confirm which cache folder your current build actually uses |
Missing assets are also worth ruling out alongside package size — an asset that fails to embed cleanly is sometimes symptomatic of a build that's already over a network's cap. If your export is also close to or over the 5 MB (or 2 MB recommended) ceiling, see what to do when a Unity Playworks/Luna build is too large.