Some playables fail loudly: rejected at upload with an error that names the problem. This guide is for the other kind: the file that uploads, opens and then misbehaves. A blank screen on one device. A button that does nothing inside the network's test app. Sound on a muted iPhone. These are diagnosable, and the fastest way in is from the symptom, because the symptom is what you can actually see.
Two companion guides cover the neighbouring ground: why playables get rejected and how to test on devices. What follows assumes you already have a misbehaving file in hand.
First, get a console
Almost every failure below is visible in the JavaScript console and invisible without it. On iOS, connect the device and use Safari's Web Inspector. On Android, enable USB debugging and open chrome://inspect, which lists debuggable webviews as well as Chrome tabs (provided the host app allows webview debugging, which test builds usually do and store builds usually do not). A network's own test app is the most faithful environment there is, and a remote console attached to it turns an hour of guessing into a line number.
Blank screen, nothing ever appears
- The creative is waiting for a container signal that never arrives. A playable that holds its content until the container reports ready or viewable works in a browser, where there is nothing to wait for, and can sit blank forever in an SDK that fires the signal late or fires a different generation of it. Listen for both the MRAID 2.0 and 3.0 viewability events, and start on a timeout as a safety net.
- An asset is fetched from a URL. Containers block outbound requests, so an image, font or script that is not embedded never arrives, and code waiting on it never renders.
- A script error before the first frame. One exception during initialisation stops everything after it. This is where the console earns its keep.
- Code that evaluates strings as code. Some ad webviews enforce a content security policy that forbids eval and new Function, and anything built on them (some templating and expression libraries) works on a desktop and throws inside the ad.
Blank canvas, but the sound plays
This one has a distinctive signature and a distinctive cause. Audio is usually handled outside the rendering path, so it starts; the renderer then fails somewhere after initialisation, often inside a try and catch that swallows the error. The canvas is sized, the scene is empty, and nothing is reported anywhere.
The commonest trigger is the difference between a development build and a production one. Minifiers rename local variables, and code that reaches for a name the minifier has changed (frequently code that is serialised and re-evaluated, or that looks functions up by name) runs in development and throws a ReferenceError in the shipped file. Always test the minified production build that will actually be uploaded, never a development build of the same creative.
A black box where the video should be
- No decoded frame yet. A video element paints a black rectangle until its first frame decodes; a poster image fills the gap.
- Autoplay was refused. Webviews generally autoplay only muted video, and iOS needs the playsinline attribute or it tries to go fullscreen. Start muted and inline, and bring sound in after the first tap.
- An unsupported codec. H.264 in an MP4 container plays nearly everywhere; newer codecs do not, particularly in older webviews.
The CTA does nothing
- The wrong exit call for the network. Each expects its own: mraid.open on MRAID networks, ExitApi.exit on Google, FbPlayableAd.onCTAClick on Meta, window.install on Mintegral, window.openAppStore on TikTok. A creative built for one network and uploaded to another often has a button that silently does nothing.
- window.open was used instead. Containers commonly block it, and it works in every desktop browser, which is why it survives internal testing.
- Something invisible sits on top of the button. A transparent full-screen layer, the container of a finished animation, or an object hidden visually but still accepting input takes the tap first. Check the stacking order around the button.
- It is a preview build. In Playable Ads Maker, free web-preview builds deliberately ship with no destination at all, so their button does nothing by design. Test the click on a network export.
Taps land in the wrong place, or not at all
When taps register but hit the wrong object, the cause is almost always coordinate spaces. A canvas has a logical size in CSS pixels and a physical size in device pixels, and on a high-density screen they differ by a factor of two or three. Code that converts a touch using the physical size is off by exactly that factor on retina devices and correct on a desktop monitor, which is how it ships. The other usual cause is a canvas scaled to fit the screen without the same scale being applied to the input.
When taps do not register at all, look for hit areas smaller than a thumb, a layer above the target that accepts input, and page gestures (scrolling, pinch-zoom) that the webview claims before the creative sees them.
No sound, or sound when there should be none
Sound should start only after the first user interaction. Browser autoplay rules enforce it, and several networks reject creatives that make any sound before the viewer has touched them. An AudioContext created before that interaction starts suspended and has to be resumed inside the gesture handler, or it stays silent for the whole impression.
The reverse failure is harder to spot. On iOS the hardware mute switch silences audio played through the Web Audio API but not audio played by media elements, which the switch deliberately ignores. A creative that plays sound through a plain audio element is therefore audible on a muted iPhone, and at least one major network rejects creatives for not respecting the mute switch, and for sound that carries on after the device locks. Route every sound through Web Audio, and stop it when the page is hidden.
Cropped, tiny or rotated wrongly
- No viewport meta tag, so the page renders at desktop width and is scaled down to a postage stamp.
- A layout designed for one orientation and served in the other.
- A cover-style scale that fills tall phones by cropping the sides, taking a CTA near the edge with it. Fit the content inside the screen and extend only the background to the edges.
- Critical elements under the notch, the home indicator or the container's own close button.
Wrong font, or text wrapped strangely
If text appears in a fallback font, the font was either not embedded or lacks the characters used. If it appears in the right font but wraps at the wrong points or sits in the wrong place, it was probably measured before the font finished loading: canvas renderers lay text out when the text object is created and never measure it again. The custom fonts guide covers both.
A triage order
- Reproduce inside the network's own test app, not a desktop browser.
- Attach a remote console and read the first error, not the last one: later errors are usually consequences.
- Run the static checks (external URLs, file size, the exit call) on the exact file that was uploaded.
- Work from the symptom using the sections above.
- Change one thing, re-export, and retest on the device where it failed.
Create playable end cards in minutes—no code required.
Open Playable Ads MakerCheck a build against these specs
The free Playable Ad Spec Checker grades an HTML or ZIP against all 13 networks’ size caps, packaging, MRAID rules and store-exit calls. It runs entirely in your browser — no upload, no sign-up.