Host Overrides

Inject layers, plugins, variables, and settings into any video at runtime without modifying the .mp4 file.

Overview

The overrides prop lets a host application deep-merge partial metadata into any MP4E video at runtime. The original .mp4 file is never modified — overrides are applied in memory by the player before the engine processes the metadata.

This keeps videos fully portable. The same video file plays identically on any host, while each host can layer its own branding, analytics, variables, restrictions, and rules on top.

Non-destructive
Overrides are additive. They never strip content from the embedded metadata — they only add new content or replace specific fields. The video creator’s original work is always preserved.

How It Works

When the player loads metadata from the MP4 atom, it applies overrides using a deep-merge algorithm before passing the result to the WASM engine. The merge rules are:

Data ShapeMerge Behavior
Record objectsDeep-merged recursively. Override keys win; base-only keys are preserved.
Arrays of { id } objectsMatched by id. If an override item has the same ID as a base item, the two are deep-merged. New IDs are appended to the end.
layers (special case)ID-match merge with cascade: the overlays array within each matched layer is also ID-merged.
Plain arrays (no id)Override replaces entirely.
PrimitivesOverride replaces entirely.

The following example shows a full merge from base metadata through overrides to the final result:

Deep-merge walkthrough
1// Original metadata (embedded in the .mp4 file)
2{
3 settings: {
4 restrictions: { navigate: true, download: true }
5 },
6 layers: [
7 {
8 id: 'layer-1',
9 name: 'Products',
10 overlays: [
11 { id: 'overlay-a', type: 'core:button', config: { text: 'Buy' } }
12 ]
13 }
14 ],
15 variables: {
16 score: { type: 'number', value: 0 }
17 }
18}
19
20// Overrides (provided by host at runtime)
21{
22 settings: {
23 restrictions: { navigate: false }
24 },
25 layers: [
26 {
27 id: 'layer-1',
28 overlays: [
29 { id: 'overlay-a', config: { text: 'Purchase Now' } }
30 ]
31 },
32 {
33 id: '__host_brand__',
34 name: 'Branding',
35 overlays: [{ id: 'logo', type: 'core:image', config: { src: '/logo.png' } }]
36 }
37 ],
38 variables: {
39 userName: { type: 'text', value: 'Alice' }
40 }
41}
42
43// Result after merge
44{
45 settings: {
46 restrictions: { navigate: false, download: true } // deep-merged: navigate overridden
47 },
48 layers: [
49 {
50 id: 'layer-1',
51 name: 'Products', // preserved from base
52 overlays: [
53 { id: 'overlay-a', type: 'core:button', config: { text: 'Purchase Now' } }
54 // ^ ID-matched, config overridden
55 ]
56 },
57 {
58 id: '__host_brand__', // new layer appended
59 name: 'Branding',
60 overlays: [{ id: 'logo', type: 'core:image', config: { src: '/logo.png' } }]
61 }
62 ],
63 variables: {
64 score: { type: 'number', value: 0 }, // preserved from base
65 userName: { type: 'text', value: 'Alice' } // new key added
66 }
67}

Override Structure

The overrides prop mirrors the MP4E metadata structure exactly. Any top-level metadata field can be overridden: layers, plugins, variables, settings, projectSettings, scenes, and more.

Full overrides example
1<MP4EPlayer
2 src="/video.mp4"
3 overrides={{
4 layers: [{
5 id: '__host_branding__',
6 name: 'Host Branding',
7 overlays: [{
8 id: 'watermark',
9 type: 'core:image',
10 position: {
11 x: 90, y: 5,
12 width: 8, height: 8,
13 xUnit: '%', yUnit: '%',
14 widthUnit: '%', heightUnit: '%'
15 },
16 config: { src: 'https://myapp.com/logo.png' }
17 }]
18 }],
19 plugins: {
20 'host-analytics': {
21 id: 'host-analytics',
22 type: 'mp4e:analytics',
23 source: 'external',
24 config: { trackingId: 'UA-XXXXX' }
25 }
26 },
27 variables: {
28 hostApiKey: { type: 'text', value: 'sk_live_xxx' },
29 userName: { type: 'text', value: 'John' }
30 },
31 settings: {
32 restrictions: { navigate: false, download: false }
33 }
34 }}
35/>

Common Use Cases

Branding Watermark

Add a persistent logo overlay to every video on your platform without editing each file.

Watermark layer
1// Add a watermark logo to every video
2<MP4EPlayer
3 src={videoUrl}
4 overrides={{
5 layers: [{
6 id: '__host_watermark__',
7 name: 'Watermark',
8 overlays: [{
9 id: 'logo',
10 type: 'core:image',
11 position: {
12 x: 92, y: 4,
13 width: 6, height: 6,
14 xUnit: '%', yUnit: '%',
15 widthUnit: '%', heightUnit: '%'
16 },
17 config: {
18 src: 'https://myapp.com/assets/logo-white.png',
19 opacity: 0.7
20 }
21 }]
22 }]
23 }}
24/>

Analytics Service Plugin

Inject a service plugin that tracks viewer interactions and reports them to your analytics backend.

Analytics plugin
1// Inject an analytics service plugin
2<MP4EPlayer
3 src={videoUrl}
4 overrides={{
5 plugins: {
6 'host-analytics': {
7 id: 'host-analytics',
8 type: 'mp4e:analytics',
9 source: 'external',
10 config: {
11 trackingId: 'UA-XXXXX',
12 endpoint: 'https://myapp.com/api/analytics'
13 }
14 }
15 }
16 }}
17/>

Override Restrictions

Enforce sequential playback or disable downloads for content that requires it, regardless of what the video creator originally configured.

Restrictions
1// Disable seeking and downloading for paid content
2<MP4EPlayer
3 src={videoUrl}
4 overrides={{
5 settings: {
6 restrictions: {
7 navigate: false, // Disable seeking (sequential viewing only)
8 download: false // Remove download option
9 }
10 }
11 }}
12/>

Inject Host-Specific Variables

Provide runtime variables that plugins can reference via {{variableName}} interpolation. This is how hosts pass user context, API keys, and locale data into videos.

Host variables
1// Inject host-specific variables that plugins can reference
2<MP4EPlayer
3 src={videoUrl}
4 overrides={{
5 variables: {
6 userName: { type: 'text', value: currentUser.name },
7 userTier: { type: 'text', value: currentUser.tier },
8 apiBaseUrl: { type: 'text', value: 'https://myapp.com/api' },
9 currency: { type: 'text', value: userLocale.currency }
10 }
11 }}
12/>

Host-Controlled Rules

Add event handlers that fire on scene transitions, overlay clicks, or any engine event. The host can wire up its own analytics, navigation, or business logic.

Event rules
1// Add host-controlled event handlers
2<MP4EPlayer
3 src={videoUrl}
4 overrides={{
5 projectSettings: {
6 eventRules: {
7 'scene:enter': [{
8 type: 'pluginAction',
9 pluginId: 'host-analytics',
10 actionId: 'trackEvent',
11 config: { event: 'scene_view' }
12 }]
13 }
14 }
15 }}
16/>

Overrides vs Config

Two complementary props
Overrides and config serve different purposes and work together:
  • overrides injects content (additive) — adds layers, plugins, variables, settings into the metadata before the engine processes it.
  • config restricts behavior (subtractive) — disables actions, sandboxes plugins, gates dangerous operations, and limits what the engine can do at runtime.

Use overrides to put content in. Use config to keep behavior out. They are complementary, not interchangeable.

Host-Injected Layers

Layers added via overrides are appended after the video creator’s layers, so they render on top by default. This makes them ideal for branding, CTAs, and host-controlled UI.

By convention, prefix host layer and overlay IDs with __host__ to avoid collisions with creator-defined IDs. This also makes it easy to identify host-injected content when debugging.

Host layer conventions
1// Convention: prefix host layer IDs with __host__
2<MP4EPlayer
3 src={videoUrl}
4 overrides={{
5 layers: [
6 {
7 id: '__host_branding__',
8 name: 'Host Branding',
9 overlays: [{
10 id: '__host_logo__',
11 type: 'core:image',
12 position: { x: 90, y: 5, width: 8, height: 8,
13 xUnit: '%', yUnit: '%', widthUnit: '%', heightUnit: '%' },
14 config: { src: '/logo.png' }
15 }]
16 },
17 {
18 id: '__host_cta__',
19 name: 'Host Call-to-Action',
20 overlays: [{
21 id: '__host_signup_btn__',
22 type: 'core:button',
23 position: { x: 50, y: 90, width: 20, height: 6,
24 xUnit: '%', yUnit: '%', widthUnit: '%', heightUnit: '%' },
25 config: { text: 'Sign Up Free' }
26 }]
27 }
28 ]
29 }}
30/>
ID-match behavior
If a host override layer has the same id as a creator layer, the two will be deep-merged (including their overlays). Use the __host__ prefix to ensure host layers are always separate from creator content.