Free tool · No sign-up

MRAID ValidatorTest a playable ad in a simulated MRAID 3.0 container

Drop in your build and watch it run: does it wait for ready, follow viewability, leave through mraid.open() and stay silent until the first tap? Every call and event is on a live timeline.

1

Drop your playable ad

A single .html or a .zip. It gets a static MRAID report and then runs, live, inside a simulated MRAID 3.0 container.

Runs in a sandboxed frame in your browser. The file is never uploaded.

What MRAID requires of a playable

MRAID is the contract between an ad and the app it runs in. These six rules are the part that is the creative’s job, and they are the six things the validator checks.

1

Reference mraid.js — don't ship it

Put <script src="mraid.js"></script> before your own scripts. It is a placeholder the network's SDK fills at serve time, so the file should not be in your upload. Unity Ads, IronSource / LevelPlay, AppLovin MAX, Chartboost and Vungle (Liftoff) expect it; Meta (Facebook & Instagram) and Moloco forbid it.

<script src="mraid.js"></script>
2

Wait for the container

Nothing in the mraid object is reliable while the state is 'loading'. Check the state first and wait only when you have to — a 'ready' listener added after the event has fired never runs.

mraid.getState() === 'loading'
  ? mraid.addEventListener('ready', start)
  : start();
3

Exit through mraid.open(url)

The container opens the store in-app and counts the click. window.open or a location change works in a browser, but the network never sees it.

cta.onclick = () => mraid.open(STORE_URL);
4

Follow viewability

Containers load the ad before it is on screen. Listen for viewableChange (2.0) and exposureChange (3.0) and let whichever fires drive play and pause.

mraid.addEventListener('viewableChange', setVisible);
mraid.addEventListener('exposureChange', p => setVisible(p > 0));
5

No sound before the first tap

Google/AdMob and AppLovin reject any sound before a user interaction. Route audio through Web Audio: the iOS mute switch silences it, and Unity rejects ads that ignore the switch.

addEventListener('pointerdown', () => ctx.resume(), { once: true });
6

Trust behaviour, not version numbers

Containers advertise MRAID 3.0 and still skip 3.0 events, so subscribe to both generations instead of branching on getVersion(). useCustomClose() is gone in 3.0 — the container owns the close button.

// not: if (mraid.getVersion() === '3.0') …

Questions about validating MRAID

Is this the same as a network's own ad tester?

No. The simulator is a mock MRAID 3.0 container written for this page — it follows the IAB spec with fixed, predictable timing, but it is not Unity's, AppLovin's, IronSource's or any other network's SDK, and real SDKs differ in timing and in bugs. Use it to catch what is the creative's own responsibility — waiting for ready, following viewability, exiting through mraid.open, holding sound until a tap — then confirm on the network's test tool before launch.

What happens to my file when I run it in the simulator?

It stays in your browser tab. The page reads the file with the File API, builds the test document in memory, and runs it in a sandboxed iframe that has no access to this page, its cookies or storage, cannot open pop-ups and cannot navigate the tab. Nothing is sent to a server. Requests the ad itself makes are recorded and flagged, because a real container would block or reject them.

Why does the validator fail an ad that calls MRAID before 'ready'?

Because a container injects mraid.js and then finishes initialising on its own schedule. Until the state changes from 'loading' to 'default', methods can be missing or return nothing, and a store exit called then can be dropped silently. The mirror-image mistake is just as common: adding a 'ready' listener without checking getState() first. If the container was already ready when the script ran, the event has fired and will not fire again, and the ad sits on its first frame forever.

How does the simulator decide that sound played before the first tap?

It records the first pointerdown, touchstart, mousedown or keydown inside the frame, and it wraps every way a creative can start sound — HTMLMediaElement.play(), autoplay on audio and video tags, and Web Audio source nodes starting. Anything unmuted that starts before that first interaction is a failure. Your desktop browser may block the sound anyway, which is exactly why this passes a casual preview and fails review: many ad WebViews allow autoplay.

What is the difference between viewableChange and exposureChange?

viewableChange is the MRAID 2.0 signal: a single true or false. exposureChange arrived in MRAID 3.0 and reports the percentage of the ad on screen plus its visible rectangle. 3.0 deprecates viewableChange, but containers still fire it and some advertise 3.0 without ever firing exposureChange, so the safe creative listens for both. The simulator fires both, and the Make hidden button lets you check that the ad actually pauses.

My playable has no mraid.js tag. Is the validator still useful?

Yes, for half of it. Without the tag the simulator does not inject a container — the ad runs as plain web, which is correct for Meta, Moloco and the networks with their own JavaScript API — but it still records sound before the first tap, network requests, pop-ups and script errors. If the build is meant for an MRAID network, the static report tells you so and you can tick the option to run it inside the mock container anyway.

Related tools and guides