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:
| Piece | What it is | Who uses it |
|---|---|---|
| OmniPlayer page | The page Preview opens. Give it a hosted .omni address and put it in an iframe. | Anyone who can host two files |
| The player component | A 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 package | The 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.
@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:
| Call | What 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.