Docs

mpwa Video Player

VAST 4.0 InLine + AdPod + Companion player for the web. Vanilla TypeScript, ~11 KB minified, zero runtime dependencies.

Version 0.1.0 · ~11 KB

VAST-ready in one tag

Drop a single <script> + a data-attributed slot anywhere on your page. The player fetches the VAST XML, picks the best MP4 (or HLS) MediaFile for the viewer's bandwidth, renders quartile + click tracking, and gracefully exits on completion or skip.

Quick start (auto-init)

The simplest path: a script + a data-attribute. The player auto-wires on DOMContentLoaded.

html
<!-- 1. Load the player. ~11 KB minified, no dependencies. -->
<script async src="https://mpwa.to/sdk/v1/video-player.min.js"></script>

<!-- 2. Drop a slot that auto-plays a VAST ad. -->
<div data-mpwa-video-ad
     data-vast-url="https://ads.mpwa.to/v1/ad/vast?ad_unit_id=YOUR_AD_UNIT_ID"
     data-skip-after="5"
     data-locale="en"
     style="width:640px; max-width:100%"></div>

Programmatic control

Need to start an ad on a user gesture, hook lifecycle events, or destroy on route change? Use the JS API.

html
<script async src="https://mpwa.to/sdk/v1/video-player.min.js"></script>
<div id="my-video-slot" style="width:640px; max-width:100%"></div>
<div id="my-companion-slot"></div>

<script>
  window.addEventListener('DOMContentLoaded', async function () {
    const handle = await MpwaVideoPlayer.play({
      container: document.getElementById('my-video-slot'),
      companionContainer: document.getElementById('my-companion-slot'),
      url: 'https://ads.mpwa.to/v1/ad/vast?ad_unit_id=YOUR_AD_UNIT_ID',
      skipAfter: 5,
      autoplay: true,
      muted: true,
      locale: 'en',
      onEvent: function (event, ad) { console.log('vast.event', event, ad.id); },
      onError: function (err) { console.warn('player error', err); },
    });
    // handle.skip() to skip programmatically
    // handle.destroy() to tear down
    await handle.done;
  });
</script>

AdPod (sequenced ads)

The ad-server emits N sequenced <Ad> elements when you pass ?pod_size=N. The player plays them back to back; quartiles + tracking fire per ad.

html
<!-- AdPod: 3 ads, sequenced. Player plays them back to back. -->
<div data-mpwa-video-ad
     data-vast-url="https://ads.mpwa.to/v1/ad/vast?ad_unit_id=YOUR_AD_UNIT_ID&pod_size=3"
     data-skip-after="5"></div>

Outstream (in-article / in-feed)

Outstream mode auto-pauses when the player scrolls out of viewport and resumes when it scrolls back in (IntersectionObserver). The slot collapses on completion so the editorial flow reclaims the space.

html
<!-- Outstream: auto-pauses when scrolled out of viewport. -->
<!-- Drop between paragraphs of editorial content. -->
<div data-mpwa-video-ad
     data-vast-url="https://ads.mpwa.to/v1/ad/vast?ad_unit_id=YOUR_AD_UNIT_ID"
     data-outstream="true"
     data-viewability-threshold="0.5"
     data-skip-after="5"
     style="width:100%; max-width:640px; margin:24px auto"></div>

Same behaviour via the JS API. Tune the visibility threshold and decide whether to collapse the slot on completion.

javascript
import { play } from 'https://mpwa.to/sdk/v1/video-player.esm.js';

await play({
  container: document.getElementById('slot'),
  url: 'https://ads.mpwa.to/v1/ad/vast?ad_unit_id=YOUR_AD_UNIT_ID',
  outstream: true,
  viewabilityThreshold: 0.5,   // 50% of player visible to play
  collapseOnComplete: true,    // collapse the slot when the ad ends
});

ESM import

For bundlers (Vite/Rollup/webpack) — same surface as the IIFE.

javascript
import { play } from 'https://mpwa.to/sdk/v1/video-player.esm.js';

await play({
  container: document.getElementById('slot'),
  url: 'https://ads.mpwa.to/v1/ad/vast?ad_unit_id=YOUR_AD_UNIT_ID',
  skipAfter: 5,
});

Options

NameTypeDefaultDescription
containerHTMLElementDOM element the player attaches to. Required.
urlstringVAST XML endpoint to fetch. Either url or xml is required.
xmlstringVAST XML body inline. Useful when you've pre-fetched it.
skipAfternumber5Seconds before the Skip button activates. 0 disables skip.
autoplaybooleantrueStart playing immediately. Browsers require muted=true for unattended autoplay.
mutedbooleantrueStart muted. Toggle via the native controls.
controlsbooleantrueShow native browser video controls in addition to the Skip button.
companionContainerHTMLElementDOM element where Companion ads render.
locale'en' | 'ar''en'UI string locale for the Skip button + ad label.
outstreambooleanfalseEnable outstream mode. Auto-pauses on scroll-out, auto-plays on scroll-in.
viewabilityThresholdnumber0.5Fraction of player area (0–1) that must be visible to play. Outstream only.
collapseOnCompletebooleanoutstreamHide the slot when the ad finishes. Defaults to true in outstream mode, false otherwise.
onEvent(event, ad) => voidLifecycle callback fired after each VAST event (debug/analytics hook).

Tracking events

These VAST verbs are fired automatically; corresponding <Tracking> URLs in the VAST XML are pixel-pinged via navigator.sendBeacon.

startfirstQuartilemidpointthirdQuartilecompleteskippauseresumemuteunmute

Direct downloads

Both the IIFE and ESM bundles cache for 1 year on /sdk/v1/. Breaking changes bump to /sdk/v2/.