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.

Works on any video
Neither metadata nor overrides requires the source file to carry MP4E metadata at all. An ordinary .mp4 with no embedded atom still gets the full interactive engine — the player generates a minimal base document from the video’s own dimensions and duration, and overrides merges on top of that exactly the same way it merges on top of an authored file. This is what makes it possible to make any video interactive purely from the host side.
Non-destructive
Overrides are additive. They never strip content from the base metadata — they only add new content or replace specific fields. The video creator’s original work is always preserved.
Frame-accurate content on an unauthored video
Fixed overlays, watermarks, and time-based content (like the promo timer below) don’t need exact frame rate information. But if an override defines rules or overlays that must land on an exact frame of a video with no authored metadata, supply your own metadata containing the correct videoDetails.fps — the generated base for an unauthored video defaults fps to 30, which may not match the source.

How It Works

Once the player has an effective base document — host-provided, extracted from the MP4 atom, or generated when the video carries no embedded metadata — 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}

Updating Overrides at Runtime

Re-render <MP4EPlayer> with a new overrides value at any point — during playback, in response to a state change, on a timer — and the player picks it up without reconstructing the engine or reloading the video source.

Each new overrides value replaces the previous one outright; it is not merged into whatever was showing a moment ago. Passing {}, null, or undefined clears the override layer entirely, restoring metadata (or the generated base, if none was provided) unchanged. A parent re-render that produces a new object with identical content is a no-op — only an actual content change triggers an update.

Playback and viewer state carry through
Changing overrides live doesn’t reset the video. Playback position, paused state, variable values, running timers, counters, the current scene, and any layers the viewer has toggled at runtime all carry through the swap when the same ids still exist in the new effective document — only content that actually changed (a new/removed layer, overlay, or variable, or a variable whose type changed) updates. A viewer mid-video with items in a cart or progress through a quiz keeps that state while you push a new promo banner, swap in updated pricing, or toggle a restriction.
Toggling a promo overlay at runtime
1function VideoWithPromo({ videoUrl, promoActive }) {
2 return (
3 <MP4EPlayer
4 src={videoUrl}
5 // Re-rendering with a new overrides value REPLACES the current
6 // override layer outright — it does not merge into whatever was
7 // showing before. The base video (or generated base, if there's no
8 // authored metadata) is untouched either way.
9 overrides={
10 promoActive
11 ? {
12 layers: [{
13 id: '__host_promo__',
14 name: 'Promo',
15 order: 1000, // explicit — see "Host-Injected Layers" below
16 overlays: [{
17 id: 'promo-banner',
18 type: 'core:banner',
19 zIndex: 1000,
20 config: { text: '20% off today only' },
21 }],
22 }],
23 }
24 : undefined // clears the override layer, restoring the base
25 }
26 />
27 );
28}

Both <MP4EPlayer overrides={...}> and the imperative playerRef.current?.reloadMetadata() method (see Methods Reference) apply the new document asynchronously — already-mounted plugins are refreshed with freshly re-interpolated content once the swap lands, with no visible flash or DOM teardown.

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, but appending alone does not make them render on top. Stacking is controlled by each layer’s order and each overlay’s zIndex, exactly like creator content. A host layer that needs to sit above everything else must set an explicit order/zIndex higher than the creator’s content — there is no implicit host-priority tier.

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 — explicit order/zIndex for topmost rendering
1// Convention: prefix host layer IDs with __host__.
2// order/zIndex are set explicitly here so this content renders ABOVE the
3// creator's layers — appending alone would not guarantee that.
4<MP4EPlayer
5 src={videoUrl}
6 overrides={{
7 layers: [
8 {
9 id: '__host_branding__',
10 name: 'Host Branding',
11 order: 1000,
12 overlays: [{
13 id: '__host_logo__',
14 type: 'core:image',
15 zIndex: 1000,
16 position: { x: 90, y: 5, width: 8, height: 8,
17 xUnit: '%', yUnit: '%', widthUnit: '%', heightUnit: '%' },
18 config: { src: '/logo.png' }
19 }]
20 },
21 {
22 id: '__host_cta__',
23 name: 'Host Call-to-Action',
24 order: 1001,
25 overlays: [{
26 id: '__host_signup_btn__',
27 type: 'core:button',
28 zIndex: 1001,
29 position: { x: 50, y: 90, width: 20, height: 6,
30 xUnit: '%', yUnit: '%', widthUnit: '%', heightUnit: '%' },
31 config: { text: 'Sign Up Free' }
32 }]
33 }
34 ]
35 }}
36/>
Stacking is explicit, not implicit
Host layers do not receive implicit visual priority. Set order on the layer and zIndex on its overlays whenever a host override needs to render above (or below) the creator’s content.
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.