Events & Communication

How plugins communicate with the engine, rules, and the parent player via events and the iframe bridge.

Overview

MP4E plugins communicate in two primary ways:

  • Emit events that rules can listen to (mp4e.emit())
  • Call actions to cause behavior (mp4e.executeActions())
Prefer events + rules over hardcoded behavior
Emit a semantic event from your plugin, then let rules decide what happens. This keeps experiences portable and editable in Studio without touching plugin code.

Plugin → Engine Events

Use mp4e.emit(eventName, data?) to send a custom event to the parent runtime. Rules can then react to it.

Event payload

PropTypeDefaultDescription
eventName*string-The event key used by rules/tracking.
dataRecord<string, any>-Optional JSON payload (serializable).
Emit an event
1// Emit a custom event that rules can listen to
2mp4e.emit('checkout:started', {
3 objectId: object?.id,
4 sku: object?.data?.sku,
5 price: object?.data?.price,
6})
Rule trigger (example)
1// Example rule trigger (conceptual)
2// When event "checkout:started" is emitted, run actions
3{
4 "trigger": { "type": "event", "eventName": "checkout:started" },
5 "conditions": { "logic": "ALL", "conditions": [] },
6 "actions": [
7 { "type": "showCustomModal", "pluginId": "modal_checkout" },
8 { "type": "trackEvent", "event": "checkout_started", "properties": { "sku": "{{event.data.sku}}" } }
9 ]
10}

Video Events

Plugins can listen to video playback events to build custom controls, progress indicators, or time-synced UI. These events are dispatched to the plugin's document and contain the current video state.

Building Video Controls
Video events are essential for building custom video control skins. Listen to timeupdate for progress bars,play/pause for button states, and volumechange for volume controls.

Available Video Events

PropTypeDefaultDescription
mp4e:video:timeupdateCustomEvent-Fired continuously during playback with current time, duration, volume state.
mp4e:video:playCustomEvent-Fired when video playback starts or resumes.
mp4e:video:pauseCustomEvent-Fired when video playback is paused.
mp4e:video:endedCustomEvent-Fired when video playback ends.
mp4e:video:seekingCustomEvent-Fired when a seek operation begins.
mp4e:video:seekedCustomEvent-Fired when a seek operation completes.
mp4e:video:volumechangeCustomEvent-Fired when volume or mute state changes.
Listening to video events
1// Listen to video playback events inside your plugin
2
3// Fired continuously during playback (~60fps)
4document.addEventListener('mp4e:video:timeupdate', (e) => {
5 const { currentTime, duration, volume, muted } = e.detail.state;
6
7 // Update progress bar
8 const progress = (currentTime / duration) * 100;
9 progressBar.style.width = progress + '%';
10
11 // Update time display
12 timeDisplay.textContent = formatTime(currentTime);
13});
14
15// Fired when video starts playing
16document.addEventListener('mp4e:video:play', () => {
17 playButton.textContent = 'PAUSE';
18 isPlaying = true;
19});
20
21// Fired when video is paused
22document.addEventListener('mp4e:video:pause', () => {
23 playButton.textContent = 'PLAY';
24 isPlaying = false;
25});
26
27// Fired when volume changes
28document.addEventListener('mp4e:video:volumechange', (e) => {
29 const { volume, muted } = e.detail.state;
30 volumeSlider.value = muted ? 0 : volume;
31});
Event Detail Structure
All video events include e.detail.state with: currentTime, duration, paused, ended, volume, muted.

Engine → Plugin Updates

Plugins read engine state primarily via mp4e.getVariable(), mp4e.getStats(), and time getters. Because plugins run in iframes, these calls go through a message bridge.

Iframe bridge (why async exists)
1// Custom plugins run inside iframes.
2// The mp4e API calls cross the boundary via postMessage:
3// - iframe → parent: { type: 'mp4e-api-call', method, args, id, pluginId }
4// - parent → iframe: { type: 'mp4e-api-response', result, error, id, pluginId }
5//
6// This is why many mp4e methods are async in plugins:
7const score = await mp4e.getVariable('score')
Render with templates, react with code
For UI text, prefer HTML templates like {{object.data.price}} so updates stay declarative. Use scripts mainly for click handlers, emitting events, and executing actions.

Overlay Click Bridge (iframes)

Custom overlays render inside iframes for isolation. To ensure clicks still trigger overlay actions (likeshowCustomModal), the iframe reports clicks to the parent runtime which then executes the overlay’s configured actions.

Practical implication
If your overlay “stops clicking”, it’s usually because pointer events are blocked or the click bridge is missing. The runtime should forward iframe clicks back to the overlay container.

Event Naming Conventions

Event names are part of your MP4E “public contract”. Keep them stable and descriptive.

Recommended naming
1// Recommended event names:
2// - Use a namespace and action: "checkout:started"
3// - Include a domain when relevant: "shop:cart:add"
4// - Keep them stable: rules depend on these keys
5
6mp4e.emit('quiz:answer', { choice: 'A', questionId: 'q1' })
7mp4e.emit('shop:cart:add', { sku: 'WATCH-001', qty: 1 })

Examples

Add-to-cart flow
Emit shop:cart:add from the plugin, then use rules to increment a counter variable and show a confirmation notification.
Quiz flow
Emit quiz:answer with the selection; rules can set variables, branch scenes, and reveal overlays.

Debugging

  • Use mp4e.log() from plugin scripts for readable console logs.
  • In Studio preview, keep the event log open to see triggers and executed actions.
  • Validate payloads: event data should be JSON-serializable.