Player Events

Complete reference for all engine events via the onEvent prop.

Overview

MP4E Player uses a single onEvent prop to deliver all engine events. This provides a clean, unified API for integrating with analytics, syncing external UI, or building custom behaviors.

CategoryEvent NamesDescription
Lifecycleplayback:ready, playback:loaded, metadata:loaded, state:ready, playback:firstPlayLifecycle Events
Playbackstate:playing, playback:ended, playback:timeUpdate, playback:seeking, playback:seeked, playback:buffering, state:volume, state:muted, state:playbackRate, fullscreen:changedPlayback Events
Objectobject:enter, object:exit, object:firstSeen, object:visible, object:hidden, object:click, object:hover, object:hoverEndObject Events
Overlayoverlay:show, overlay:hide, overlay:click, overlay:hover, overlay:hoverEnd, overlay:clickAway, plugin:event, plugin:initOverlay Events
Scenescene:enter, scene:exit, scene:start, scene:end, scene:loopScene Events
Layerlayer:activate, layer:deactivateLayer Events
Variablevariable:changed, timer:start, timer:complete, timer:pauseVariable Events
Actionaction:*, action:error, action:trackEventAction Events
Errorstate:error, metadata:error, plugin:errorError Events
Displaydisplay:show, display:hideDisplay Events
Dialogdialog:show, dialog:hideDialog Events
PerformanceblobCache:statusPerformance Events
Trackingtracking:show, tracking:hideTracking Events

Usage

All events are delivered through the onEvent prop. The callback receives the event name as a string and the event data as the second argument.

onEvent usage
1import { MP4EPlayer } from '@mp4e/react';
2
3<MP4EPlayer
4 src="/video.mp4"
5
6 // Single event handler for ALL engine events
7 onEvent={(event, data) => {
8 switch (event) {
9 case 'playback:ready':
10 console.log('Player ready, duration:', data.duration);
11 break;
12 case 'state:playing':
13 console.log(data ? 'Playing' : 'Paused');
14 break;
15 case 'object:click':
16 console.log('Clicked:', data.objectId, data.objectLabel);
17 break;
18 case 'variable:changed':
19 console.log(data.variableName, ':', data.previousValue, '->', data.newValue);
20 break;
21 }
22 }}
23/>

Lifecycle Events

PropTypeDefaultDescription
playback:ready{ duration: number }-Player is ready for playback. Video metadata is loaded.
playback:loaded{ duration: number }-Video loaded and duration is available.
metadata:loadedMP4EMetadata-MP4E metadata parsed and engine initialized.
state:readyboolean-Engine is ready (true) or not.
playback:firstPlayundefined-First playback started. Fires once per session.
Lifecycle events
1<MP4EPlayer
2 src="/video.mp4"
3 onEvent={(event, data) => {
4 switch (event) {
5 case 'playback:ready':
6 console.log('Player ready, duration:', data.duration, 'seconds');
7 break;
8 case 'metadata:loaded':
9 console.log('Objects:', Object.keys(data.objects || {}).length);
10 console.log('Layers:', data.layers?.length);
11 break;
12 case 'playback:firstPlay':
13 console.log('User started watching');
14 analytics.track('video_start');
15 break;
16 }
17 }}
18/>

Playback Events

PropTypeDefaultDescription
state:playingboolean-true when playback starts, false when paused.
playback:endedundefined-Video reached the end.
playback:timeUpdate{ time: number; frame: number }-Time update (throttled to ~60fps).
playback:seeking{ fromTime: number; toTime: number }-Seek started.
playback:seeked{ currentTime: number }-Seek completed.
playback:bufferingboolean-true when buffering starts, false when it ends.
state:volumenumber-Volume changed (0-1).
state:mutedboolean-Mute state changed.
state:playbackRatenumber-Playback rate changed.
fullscreen:changedboolean-Fullscreen state changed.
Playback events
1<MP4EPlayer
2 src="/video.mp4"
3 onEvent={(event, data) => {
4 switch (event) {
5 case 'state:playing':
6 console.log(data ? 'Playing' : 'Paused');
7 break;
8 case 'playback:ended':
9 console.log('Video ended');
10 break;
11 case 'playback:timeUpdate':
12 updateProgressBar(data.time);
13 break;
14 case 'playback:seeking':
15 console.log('Seeking:', data.fromTime, '->', data.toTime);
16 break;
17 case 'playback:seeked':
18 console.log('Seeked to:', data.currentTime);
19 break;
20 case 'playback:buffering':
21 if (data) showLoadingSpinner();
22 else hideLoadingSpinner();
23 break;
24 case 'state:volume':
25 console.log('Volume:', data);
26 break;
27 case 'state:muted':
28 console.log(data ? 'Muted' : 'Unmuted');
29 break;
30 case 'state:playbackRate':
31 console.log('Speed:', data + 'x');
32 break;
33 case 'fullscreen:changed':
34 console.log(data ? 'Fullscreen' : 'Windowed');
35 break;
36 }
37 }}
38/>
Performance
playback:timeUpdate fires frequently during playback (~60fps). For performance-sensitive operations, consider debouncing or filtering by event name early.

Object Events

PropTypeDefaultDescription
object:enter{ objectLabel: string; objectId: string; objectType: string; confidence: number; boundingBoxX: number; boundingBoxY: number; boundingBoxWidth: number; boundingBoxHeight: number; isVisible: boolean }-Object enters frame
object:exit{ objectLabel: string; objectId: string; objectType: string; confidence: number; boundingBoxX: number; boundingBoxY: number; boundingBoxWidth: number; boundingBoxHeight: number; isVisible: boolean }-Object exits frame
object:firstSeen{ objectLabel: string; objectId: string; objectType: string; confidence: number; boundingBoxX: number; boundingBoxY: number; boundingBoxWidth: number; boundingBoxHeight: number; isVisible: boolean }-First time object appears
object:visible{ objectLabel: string; objectId: string; objectType: string; confidence: number; boundingBoxX: number; boundingBoxY: number; boundingBoxWidth: number; boundingBoxHeight: number; isVisible: boolean }-Object becomes visible
object:hidden{ objectLabel: string; objectId: string; objectType: string; confidence: number; boundingBoxX: number; boundingBoxY: number; boundingBoxWidth: number; boundingBoxHeight: number; isVisible: boolean }-Object goes out of frame
object:click{ objectLabel: string; objectId: string; objectType: string; confidence: number; boundingBoxX: number; boundingBoxY: number; boundingBoxWidth: number; boundingBoxHeight: number; isVisible: boolean; clickX: number; clickY: number; clickCount: number }-User clicks object
object:hover{ objectLabel: string; objectId: string; objectType: string; confidence: number; boundingBoxX: number; boundingBoxY: number; boundingBoxWidth: number; boundingBoxHeight: number; isVisible: boolean }-User hovers object
object:hoverEnd{ objectLabel: string; objectId: string; objectType: string; confidence: number; boundingBoxX: number; boundingBoxY: number; boundingBoxWidth: number; boundingBoxHeight: number; isVisible: boolean }-User stops hovering
Object events
1<MP4EPlayer
2 src="/video.mp4"
3 onEvent={(event, data) => {
4 switch (event) {
5 case 'object:visible':
6 console.log('Object visible:', data.objectId, data.objectLabel);
7 analytics.track('product_impression', { id: data.objectId });
8 break;
9 case 'object:hidden':
10 console.log('Hidden:', data.objectId);
11 break;
12 case 'object:click':
13 console.log('Clicked:', data.objectId, data.objectLabel);
14 break;
15 case 'object:hover':
16 console.log('Hovering:', data.objectId);
17 break;
18 case 'object:firstSeen':
19 console.log('First time seeing:', data.objectLabel);
20 break;
21 }
22 }}
23/>
Object Visibility
object:visible and object:hidden fire when objects enter/leave the video frame based on AI tracking data. Use these for product impression tracking.

Overlay Events

PropTypeDefaultDescription
overlay:show{ overlayName: string; overlayId: string; overlayType: string; zIndex: number; opacity: number; positionX: number; positionY: number; isVisible: boolean }-Overlay becomes visible
overlay:hide{ overlayName: string; overlayId: string; overlayType: string; zIndex: number; opacity: number; positionX: number; positionY: number; isVisible: boolean }-Overlay hidden
overlay:click{ overlayName: string; overlayId: string; overlayType: string; zIndex: number; opacity: number; positionX: number; positionY: number; isVisible: boolean; clickX: number; clickY: number; clickCount: number }-User clicks overlay
overlay:hover{ overlayName: string; overlayId: string; overlayType: string; zIndex: number; opacity: number; positionX: number; positionY: number; isVisible: boolean }-User hovers overlay
overlay:hoverEnd{ overlayName: string; overlayId: string; overlayType: string; zIndex: number; opacity: number; positionX: number; positionY: number; isVisible: boolean }-User stops hovering
overlay:clickAway{ overlayName: string; overlayId: string }-User clicks outside this overlay (on video background)
plugin:event{ eventName: string; pluginId: string; data: any }-Custom plugin event emitted via mp4e.emit().
plugin:init{ pluginId: string }-Plugin iframe initialized.
Overlay events
1<MP4EPlayer
2 src="/video.mp4"
3 onEvent={(event, data) => {
4 switch (event) {
5 case 'overlay:show':
6 console.log('Overlay shown:', data.overlayId, data.overlayType);
7 break;
8 case 'overlay:hide':
9 console.log('Hidden:', data.overlayId);
10 break;
11 case 'overlay:click':
12 console.log('Clicked:', data.overlayId);
13 break;
14 case 'plugin:event':
15 console.log('Plugin event:', data.eventName, 'from:', data.pluginId);
16 if (data.eventName === 'quiz:answer') {
17 handleQuizAnswer(data.data);
18 }
19 break;
20 }
21 }}
22/>
Plugin Events
Plugins can emit custom events via mp4e.emit(eventName, data). These are forwarded as plugin:event in the onEvent handler.

Scene Events

PropTypeDefaultDescription
scene:enter{ sceneName: string; sceneId: string; sceneStartFrame: number; sceneEndFrame: number; sceneDuration: number; loopCount: number; isLooping: boolean; reason: string; fromScene?: string }-Scene becomes active (semantic — fires on any activation: playback, seek, goToScene)
scene:exit{ sceneName: string; sceneId: string; sceneStartFrame: number; sceneEndFrame: number; sceneDuration: number; loopCount: number; isLooping: boolean; reason: string; toScene?: string }-Scene becomes inactive (semantic — fires on any deactivation: playback end, seek away, goToScene away)
scene:start{ sceneName: string; sceneId: string; sceneStartFrame: number; sceneEndFrame: number }-Playhead crosses scene start frame (positional — natural playback only, does not fire on seeks)
scene:end{ sceneName: string; sceneId: string; sceneStartFrame: number; sceneEndFrame: number }-Playhead crosses scene end frame (positional — natural playback only, does not fire on seeks)
scene:loop{ sceneName: string; sceneId: string; sceneStartFrame: number; sceneEndFrame: number; sceneDuration: number; loopCount: number; isLooping: boolean; currentLoopIndex: number; maxLoops: number }-Scene loops
Scene events
1<MP4EPlayer
2 src="/video.mp4"
3 onEvent={(event, data) => {
4 switch (event) {
5 case 'scene:enter':
6 console.log('Entered:', data.sceneId, data.sceneName);
7 updateBreadcrumbs(data.sceneId);
8 break;
9 case 'scene:exit':
10 console.log('Exiting:', data.sceneId);
11 break;
12 case 'scene:start':
13 console.log('Playhead reached start of:', data.sceneId);
14 break;
15 case 'scene:end':
16 console.log('Playhead reached end of:', data.sceneId);
17 break;
18 case 'scene:loop':
19 console.log('Loop #', data.loopCount, 'for scene:', data.sceneId);
20 break;
21 }
22 }}
23/>

Layer Events

PropTypeDefaultDescription
layer:activate{ layerName: string; layerId: string; layerOrder: number; overlayCount: number; blendMode: string }-Layer becomes active
layer:deactivate{ layerName: string; layerId: string; layerOrder: number; overlayCount: number; blendMode: string }-Layer becomes inactive
Layer events
1<MP4EPlayer
2 src="/video.mp4"
3 onEvent={(event, data) => {
4 if (event === 'layer:activate') {
5 console.log('Layer activated:', data.layerId, data.overlayCount, 'overlays');
6 }
7 if (event === 'layer:deactivate') {
8 console.log('Layer deactivated:', data.layerId);
9 }
10 }}
11/>

Variable Events

PropTypeDefaultDescription
variable:changed{ variableName: string; variableType: string; previousValue: any; newValue: any }-Any variable value changed.
timer:start{ timerName: string; target: number; direction: "up" | "down" }-Timer started.
timer:complete{ timerName: string; duration: number }-Timer completed.
timer:pause{ timerName: string; currentValue: number }-Timer paused.
Variable events
1<MP4EPlayer
2 src="/video.mp4"
3 onEvent={(event, data) => {
4 switch (event) {
5 case 'variable:changed':
6 console.log(data.variableName, ':', data.previousValue, '->', data.newValue);
7 // Sync with external state
8 if (data.variableName === 'cartTotal') {
9 updateCartUI(data.newValue);
10 }
11 break;
12 case 'timer:start':
13 console.log('Timer started:', data.timerName, 'target:', data.target);
14 break;
15 case 'timer:complete':
16 console.log('Timer completed:', data.timerName);
17 showTimeoutMessage();
18 break;
19 case 'timer:pause':
20 console.log('Timer paused at:', data.currentValue);
21 break;
22 }
23 }}
24/>
State Sync
Use the variable:changed event to sync video state with your application. For example, update a cart UI when a "cartTotal" variable changes.

Action Events

PropTypeDefaultDescription
action:*{ type: string; [key: string]: any }-Action executed (e.g. action:seek, action:setVariable).
action:error{ actionType: string; error: string }-Action failed.
action:trackEvent{ eventName: string; properties?: Record<string, any> }-Analytics event fired via trackEvent action.
Action Events
Action events use the action: prefix followed by the action type (e.g. action:seek, action:setVariable). Listen for specific action types or use event.startsWith('action:') to catch all actions.

Error Events

PropTypeDefaultDescription
state:errorstring | null-General engine error occurred.
metadata:error{ message: string }-Metadata-specific error.
plugin:error{ pluginId: string; error: string }-Plugin error (non-fatal).
Error events
1<MP4EPlayer
2 src="/video.mp4"
3 onEvent={(event, data) => {
4 switch (event) {
5 case 'state:error':
6 if (data) {
7 console.error('Engine error:', data);
8 errorReporter.capture({ code: 'ENGINE_ERROR', message: data });
9 }
10 break;
11 case 'metadata:error':
12 console.error('Metadata error:', data.message);
13 // Fall back to non-interactive playback
14 break;
15 case 'plugin:error':
16 console.warn('Plugin error:', data.pluginId, data.error);
17 break;
18 }
19 }}
20/>

Display Events

Display events fire when object or overlay displays are shown or hidden, triggered by the display resolution cascade in response to user interactions.

PropTypeDefaultDescription
display:show{ displayId: string; objectId?: string; overlayId?: string; eventType: string; config?: object; position?: object; pluginType?: string; html?: string; css?: string; script?: string; autoClose?: object }-Object/overlay display is shown.
display:hide{ displayId: string; objectId?: string; overlayId?: string }-Display is hidden.
Display events
1<MP4EPlayer
2 src="/video.mp4"
3 onEvent={(event, data) => {
4 switch (event) {
5 case 'display:show':
6 console.log('Display shown:', data.displayId, 'type:', data.eventType);
7 if (data.objectId) {
8 console.log('For object:', data.objectId);
9 }
10 break;
11 case 'display:hide':
12 console.log('Display hidden:', data.displayId);
13 break;
14 }
15 }}
16/>
Display vs Overlay
Displays are dynamic UI elements (tooltips, product cards) that appear in response to object interactions. They differ from overlays, which are positioned on the video. A single overlay can trigger different displays based on hover vs click events.

Dialog Events

Dialog events fire when the engine presents or dismisses a dialog (alert, confirm, prompt, or custom modal).

PropTypeDefaultDescription
dialog:show{ type: 'alert' | 'confirm' | 'prompt' | 'customModal'; title?: string; message?: string; config?: object }-Dialog presented to the user.
dialog:hide{ type: string; result: any }-Dialog dismissed. result contains the user's response.
Dialog events
1<MP4EPlayer
2 src="/video.mp4"
3 onEvent={(event, data) => {
4 switch (event) {
5 case 'dialog:show':
6 console.log('Dialog opened:', data.type, data.title);
7 break;
8 case 'dialog:hide':
9 console.log('Dialog closed:', data.type, 'result:', data.result);
10 if (data.type === 'confirm' && data.result === true) {
11 console.log('User confirmed action');
12 }
13 break;
14 }
15 }}
16/>

Performance Events

Performance events provide visibility into the blob cache system, which downloads the full video in the background for instant seeking.

PropTypeDefaultDescription
blobCache:status{ state: 'idle' | 'downloading' | 'swapping' | 'cached' | 'error' | 'skipped'; progress?: number; fileSize?: number }-Blob cache state changes. progress is 0-1 during download.
Performance events
1<MP4EPlayer
2 src="/video.mp4"
3 enableBlobCache={true}
4 onEvent={(event, data) => {
5 if (event === 'blobCache:status') {
6 switch (data.state) {
7 case 'downloading':
8 console.log('Caching video:', Math.round((data.progress || 0) * 100) + '%');
9 break;
10 case 'cached':
11 console.log('Video cached, size:', data.fileSize, 'bytes');
12 break;
13 case 'error':
14 console.warn('Blob cache error');
15 break;
16 case 'skipped':
17 console.log('File too large for caching');
18 break;
19 }
20 }
21 }}
22/>
Blob Cache States
The blob cache transitions through states: idle → downloading (with progress 0-1) → swapping → cached. If the file exceeds the size limit, the state will be skipped. Errors are reported as error.

Tracking Events

Tracking events fire when tracking visualizations (polygons, bounding boxes, mesh, corners) are shown or hidden at runtime.

PropTypeDefaultDescription
tracking:show{ mode: string; target: string; targetId: string; style?: object; duration?: number; zIndex?: number }-Tracking visualization shown (via showTracking action).
tracking:hide{ target: string; targetId: string }-Tracking visualization hidden.
Tracking events
1<MP4EPlayer
2 src="/video.mp4"
3 onEvent={(event, data) => {
4 switch (event) {
5 case 'tracking:show':
6 console.log('Tracking shown:', data.mode, 'for', data.target, data.targetId);
7 if (data.duration) {
8 console.log('Will auto-hide after', data.duration, 'ms');
9 }
10 break;
11 case 'tracking:hide':
12 console.log('Tracking hidden:', data.target, data.targetId);
13 break;
14 }
15 }}
16/>
Tracking Modes
Available tracking modes include polygon, bbox, corners, mesh, meshUV, amodal, and all. Tracking can target a specific object, a group, or all objects.

Advanced

Wildcard Subscription

The onEvent prop acts as a wildcard subscriber — it receives every event emitted by the engine. The first parameter (event) contains the event name as a string, and the second parameter (data) contains the event payload. This makes it easy to forward all events to an analytics or logging system without listing individual event types.

Wildcard event subscription
1import { MP4EPlayer } from '@mp4e/react';
2
3// Subscribe to ALL events using onEvent
4<MP4EPlayer
5 src="/video.mp4"
6 onEvent={(event, data) => {
7 // 'event' contains the event name (e.g. 'playback:ready')
8 // 'data' contains the event payload
9 console.log(`[${event}]`, data);
10
11 // Forward all events to your analytics or logging system
12 analytics.track('mp4e_event', {
13 eventName: event,
14 payload: data,
15 timestamp: Date.now()
16 });
17 }}
18/>
Event Filtering
Since onEvent fires for every event, use event.startsWith() or a switch statement to filter for the categories you care about. For high-frequency events like playback:timeUpdate, consider early returns to avoid unnecessary processing.