Technical · 11 min

Why Mintegral still wants ES5

A tour of the PlayTurbo WebView's refusals — no clamp(), no flex gap, no safe-area insets — and how to build a fixed scaled stage that survives them.

Playable ads do not run in a browser. They run inside an SDK-hosted WebView, on devices several Android versions behind the phone in your pocket, with the network's own wrapper injected around your markup. The lowest common denominator in that set is much lower than caniuse suggests.

The refusals worth designing around

Modern syntax: arrow functions, const/let, template literals and optional chaining can fail on the oldest WebViews. Transpiling to ES5 costs nothing and removes an entire class of blank-screen reports.

clamp(), min(), max(): fluid typography quietly collapses. Compute sizes once against a fixed stage instead.

Flex gap: unsupported on older WebKit. Use margins.

Safe-area insets: env() resolves to nothing, so anything anchored to the bottom edge sits under the home indicator.

External requests: no fetching fonts, images or scripts. Everything is inlined into a single document or bundled in the zip.

The fixed scaled stage

The technique that survives all of it is to stop being responsive. Lay the unit out once at a fixed logical size — 720×1280 is a safe portrait canvas — and scale that stage to the viewport with a transform, centring the overflow.

Every measurement inside the stage becomes an absolute pixel value in a coordinate space you control. Text does not reflow, hit areas do not move, and the only thing that changes between a small phone and a tablet is the scale factor. Handle orientation by picking the smaller of the width and height ratios and re-applying the transform on resize.

Packaging

Mintegral and Unity expect a zip with index.html at the root, all assets inlined or included, and no directory traversal above the root. AppLovin and Google UAC accept a single self-contained HTML document. The studio exports both shapes from the same build, because the same creative frequently ships to more than one network.

Test on the constraint, not the target

If the unit renders correctly with ES5 output, absolute units and no network access, it will render everywhere. The reverse is not true, and the failure mode is a black frame that no reporting dashboard will explain to you.