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.
Category Event Names Description Lifecycle playback:ready, playback:loaded, metadata:loaded, state:ready, playback:firstPlay Lifecycle Events Playback state:playing, playback:ended, playback:timeUpdate, playback:seeking, playback:seeked, playback:buffering, state:volume, state:muted, state:playbackRate, fullscreen:changed Playback Events Object object:enter, object:exit, object:firstSeen, object:visible, object:hidden, object:click, object:hover, object:hoverEnd Object Events Overlay overlay:show, overlay:hide, overlay:click, overlay:hover, overlay:hoverEnd, overlay:clickAway, plugin:event, plugin:init Overlay Events Scene scene:enter, scene:exit, scene:start, scene:end, scene:loop Scene Events Layer layer:activate, layer:deactivate Layer Events Variable variable:changed, timer:start, timer:complete, timer:pause Variable Events Action action:*, action:error, action:trackEvent Action Events Error state:error, metadata:error, plugin:error Error Events Display display:show, display:hide Display Events Dialog dialog:show, dialog:hide Dialog Events Performance blobCache:status Performance Events Tracking tracking:show, tracking:hide Tracking 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.
1 import { 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 Prop Type Default Description 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.
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 Prop Type Default Description 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.
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 Prop Type Default Description 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
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 Prop Type Default Description 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.
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 Prop Type Default Description 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
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 Prop Type Default Description 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
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 Prop Type Default Description 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.
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 Prop Type Default Description 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 Prop Type Default Description state:errorstring | null- General engine error occurred. metadata:error{ message: string }- Metadata-specific error. plugin:error{ pluginId: string; error: string }- Plugin error (non-fatal).
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.
Prop Type Default Description 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.
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).
Prop Type Default Description 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.
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 provide visibility into the blob cache system, which downloads the full video in the background for instant seeking.
Prop Type Default Description blobCache:status{ state: 'idle' | 'downloading' | 'swapping' | 'cached' | 'error' | 'skipped'; progress?: number; fileSize?: number }- Blob cache state changes. progress is 0-1 during download.
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.
Prop Type Default Description 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.
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 Copy 1 import { 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.