The problem
The export worked when you tested it locally or inside Unity's own preview. After conversion, the same creative runs — visuals, animation, click-through all fine — but no audio. It's a reasonable assumption that something in the conversion step dropped the sound. In practice, that's rarely what's happening, because Luna doesn't hand audio off to any per-network asset step in the first place.
Cause 1: it may not be a bug — most networks require audio to be gated on user interaction
Browsers enforce autoplay restrictions on audio with sound by default — Chrome, Safari, and WebView-based renderers will not let a clip play until the user has interacted with the page. Ad networks build their own rules on top of that reality, and several are explicit about it in their playable specs:
- Google Ads — "Audio must stay muted until a user interaction."
- AppLovin — "Do not auto-click or auto-play audio before user interaction."
- Meta — "Do not auto-play audio before a user interaction."
- Unity Ads — "Do not auto-play audio before a user interaction."
- Vungle (Liftoff) — "Do not auto-play audio before a user interaction."
So if your creative has a sound cue tied to scene start rather than a tap, "silent on load" is the expected outcome, not a defect — both the browser's own autoplay policy and the network's playable spec are working as designed. The fix isn't in the conversion pipeline at all; it's moving whatever triggers that clip in your Unity scene to fire after the first tap/touch, the same way the rest of the creative's gameplay logic already waits for interaction.
Cause 2: a genuinely broken source export — the sound never made it into the cache
This is the case that actually is a bug, and it happens upstream of any network conversion. In a single-HTML build, Luna doesn't inline audio the way it inlines an <img> or <video> tag from your markup. Audio (and other binary blobs) is handled by Luna's own cache system — a cache/<id>/ folder containing scripts.js, jsons.js, and blobs.js, which populate window.sounds/window.blobs at runtime through decompressString/decompressArrayBuffer calls against brotli-compressed, base64- or base122-encoded payloads. That cache is inlined into the output as-is; there's no separate "embed the audio" step layered on top of it, because Luna already does that natively.
The actual failure mode is a mismatch inside that cache: jsons.js lists a sound key, but the matching payload in blobs.js never got written — usually the result of an interrupted or partial Unity export. The cache scripts still get inlined verbatim, so everything else in the build plays normally; that one clip just has a hole where its data should be, and window.sounds[path] never gets assigned. Nothing downstream can recover data that was never in the source export.
Cause 3: testing in a context that blocks autoplay differently than the real placement
Opening a converted HTML file directly from disk, or previewing it in a plain browser tab, is not the same environment a network actually serves it in. A bare local file and a real MRAID/webview ad container can apply autoplay rules differently — a tap that "counts" as a user gesture inside an in-app webview may not register the same way when you double-click an HTML file on your desktop. If audio works when you tap through the creative in an actual test placement (or a network's own preview tool) but not when you just open the file locally, that's a test-harness difference, not a packaging problem. Always validate audio behavior in the same kind of container the ad will actually run in before concluding anything is broken.
How to tell which one you're looking at
| Symptom | Likely cause | What to check |
|---|---|---|
| Silent until first tap, then plays fine | Autoplay gating (expected) | Nothing to fix — confirm the network's own preview behaves the same way |
| Silent even after multiple taps, same clip every time | Broken cache entry from the source export | Re-export from Unity/Playworks/Luna; validate the cache before converting again |
| Plays in a real test placement but not opening the file locally | Test environment, not the build | Test inside an MRAID/webview container, not a bare local file |
| Some sounds play, one specific clip never does, across every network | Cache manifest/payload mismatch for that asset | Run the validator against the original export |
The fastest way to separate a policy issue from a real one is to run the original Luna/Playworks export — before any network conversion — through PlayableKit's Playable Ad Validator. It checks the cache folder's structural integrity (all three required files present, manifest entries with matching payloads) for free, with no signup, so you know whether the problem exists in the source export before spending a review cycle finding out from the network instead.
For more on how Luna's single-HTML cache pipeline fits together end to end, see Luna single HTML export explained. And if a creative already came back rejected rather than just quietly silent, Luna playable ad rejected: common reasons covers the broader set of causes network review actually flags.