OmniMotion®
GuideDraft

Embedding a scene

Putting a scene on a web page — iframe, player, and the OmniMotion runtime host API.

A scene can run on a web page without the editor. The part of OmniMotion that plays scenes — the runtime — is the same code the editor previews with, packaged so a page can load it. This page says what exists today and what a page can do with a running scene. It is short on purpose: embedding is a developer's job, and the pieces are still being finalized.

01What exists today

Three things, from simplest to most flexible:

PieceWhat it isWho uses it
OmniPlayer pageThe page Preview opens. Give it a hosted .omni address and put it in an iframe.Anyone who can host two files
The player componentA stand-alone player that ships as one script plus a web component you write as a tag with the project file as its source.Someone building a site
The runtime packageThe playback core on its own, for developers who want to draw the scene into their own canvas and control it from code.A developer integrating deeply

All three accept a .omni project file — scene plus embedded assets in one gzipped pack. That is what Export → Project file (.omni) produces. Legacy .ntpackv3 / .ompack scene files still play when dropped on OmniPlayer, but .omni is what you should host and send.

The player component and the npm runtime package are built from the same source as the editor. The published package name may still read @eventide/runtime during the OmniMotion rename; ask whoever runs your deployment for the current install line. This page uses OmniMotion runtime for the product name.

02OmniPlayer in an iframe

The quickest embed. Host your .omni somewhere the page can fetch it, and open OmniPlayer with the file's address:

player.html?scene=https://your-site/scenes/hero.omni

Put that address in an iframe and the scene plays inside it, fitted to the frame. Prefer &p=shop — the artboard Address. An &root= number still picks an artboard by tree order (0, 1, 2…). Deep link: &p=item&id=12 (see Routes).

Because the page fetches the file, the file's host has to allow the page to read it (the usual cross-origin rules apply). A .omni carries imported assets inside the pack, so one URL is usually enough.

03What a page can do with a running scene

A scene doesn't have to be a closed box. Two things you set up in the editor become a surface the page can talk to:

  • Exposed variables. In the Variables tab, a variable marked as exposed can be read and written by the page — a speed, a theme, a progress. Whatever in the scene reads that variable (a stream, a guard, a state machine) reacts immediately.
  • Signals. A page can fire a named signal, and every sentence in the scene that starts with On Signal for that name runs. This is how a page button can start an intro or open a panel.

In the other direction, the page can listen: it is told when an exposed variable changes, when a state machine switches state, and when a signal is broadcast. A Data source marked as runtime data can be read and written by the page — any JSON value, not only rows — and a Data Repeater can still be fed rows directly by its layer name.

What this means for you as the designer: name things well. The page addresses variables, signals and data sources by name, so speed, start and Products are the contract.

04Runtime API

The OmniMotion runtime package hands you a player and a host. The host is the only surface a page should call.

import { createPlayer } from '@eventide/runtime'; // OmniMotion runtime — npm rename to @omnimotion/runtime planned

const player = createPlayer({
  canvas,                 // required
  artboard: 0,            // which frame opens (ordinal, same as Preview &root=)
  interactive: true,      // false = watch only, no pointer
  loadStudioFonts: true,  // false = do not touch the page <head>
  dracoBaseUrl: '/draco/', // required when you host compressed models off-root
  dataProxy: '/__data'     // optional — where "via proxy" data sources fetch through
});

// OmniPlayer unpacks .omni for you; at this level pass the inner scene bytes
// (after unpacking the .omni pack) or load through player.html?scene= instead.
player.load(sceneBuffer);
player.artboardCount();
player.goToArtboard(1);     // out of range is ignored
player.resize();            // usually automatic
player.destroy();

player.host talks to the running scene:

CallWhat it does
listInputs()Exposed variables — name, type, current value
getInput(name)One exposed value, or nothing if it is not exposed
setInput(name, value)Writes an exposed variable. Returns false on a missing name or a type mismatch
fireEvent(name, scope?)Broadcasts a signal — every On Signal for that name (and matching Scope) runs
setData(name, value)Writes a Data source by name — any JSON value; every reader (repeaters, templates, streams, markers, On Data rules) follows. If no source has that name, pushes rows into a Data Repeater by layer name (non-objects dropped). A REST source may overwrite this on the next poll
getData(name)The current value of a source marked as runtime data, or nothing
listDataSources()Runtime-data sources — { name, kind, shape, count }
listDataTargets()Everything you can push to — sources and repeaters — { name, rows, dataSource }
listStateMachines()Current state of every machine — names and the active state
subscribe({ onInputChange, onStateChange, onEvent })Listen. Returns an unsubscribe function

Only variables with the exposed checkbox in the Variables tab appear as inputs. Artboards are addressed by ordinal (0, 1, 2… in tree order) or by Address (?p=).

When the player runs inside an iframe, the same host API is also a postMessage bridge (no origin allowlist — the parent page owns its iframe):

{ type: 'omni:ready' }
{ type: 'omni:call', id, method, args }  →  { type: 'omni:ok'|'omni:err', id, value }
{ type: 'omni:event', name, payload }    // subscribe mirror

method is one of the host calls above (setInput, fireEvent, setData…). The parent can drive setInput('mood', 1) without touching window.eventide.

05What the host page must provide

  • Nothing extra for most scenes: text, shapes, effects, images and animation run from the script alone.
  • The decoder files for compressed 3D models, served alongside the player. Uncompressed models need nothing.
  • The map stylesheet for scenes with a map layer.
  • Reachable assets only when a scene references files by URL instead of embedding them in the .omni.

The player never loads the editor's fonts into the host page unless asked, and never touches the page's own styles.

06Versions

The one promise between editor and player is the file format inside the .omni. A player older than the studio that made the file refuses with a clear message — made with a newer OmniMotion — instead of playing it wrong. Update OmniPlayer when you update the studio and the pair stays in step.

Checked against the tool on 2026-09-06.