demo.config.json and the media it references. This page explains the concepts. The config reference lists every field.
Project layout
interactive-demo.json needs only a name. Demos are discovered by walking demos/, so a folder is enough. The optional fields:
demosfixes the orderdevlists demos in.tokenssets the theme for every demo.brandfills the page header.
interactive-demo init --demo <slug>, or import a folder or a capture zip with init --from <dir|zip>. The editor edits demo.config.json in place.
The demo config
id is a 12-character URL-safe string that identifies the demo for its whole life. init and capture mint one. Keep it when you rename the folder, because the published URL is keyed on it.
The $schema line gives your text editor autocompletion and inline validation.
Steps
A step is eithercontent (a screen) or cover (a full-screen card built from widgets).
Content steps
background.type is image or video. naturalWidth and naturalHeight are the media’s pixel size and drive the player’s aspect ratio. A video background adds posterSrc, autoplay, and muted. Hotspots and zoom appear once the clip has played.
Hotspots. annotations are the hotspots. x and y (and w and h for an area) are fractions of the screen, from 0 to 1. A message comes in four variants:
A message with
advancesStep: true, the default, moves to the next step when clicked. Two more annotation types sit beside messages: text overlays, and blur boxes to hide sensitive data.
Captions. captions are timed subtitles in milliseconds. On a step with a voiceover they follow the audio’s clock. Otherwise they follow the time since the step started. start and end are optional. Leave both out and the caption shows for the whole step.
Voiceover. voiceover is an audio file played with the step. Record or attach one in the editor’s voiceover panel.
Zoom. transform zooms the screen towards a point. The editor’s Zoom sets it.
Timing. duration, in milliseconds, pins how long the step runs. Leave it out and the step lasts as long as its voiceover or its video, whichever is longer, or 5 seconds when it has neither.
advance.trigger is click (wait for the viewer) or auto (move on when the step’s duration is up). It defaults to auto on a content step and click on a cover.
chrome.autoplay must be true for auto steps to run on their own. Without it, every step waits at its end.Cover steps
Button actions are
next, prev, restart, step, chapter, and url. A closing cover with a restart button is the usual way to end a demo.
Form submissions
A form’s values go two places. The player always emits aform_submit event, which a React host receives through onEvent and an embedding page receives as a message. See Listening for events.
To store the values without writing code, give the widget a submitTo URL. The player POSTs the fields as JSON to it, then follows the submit button’s action.
{ demoId, stepId, widgetId, fields: [{ id, label, value }], timestamp }. The endpoint must accept a cross-origin POST. Webhook services and form backends do. Nothing is sent unless submitTo is set.
Video steps by hand
Put the recording inassets/ (WebM or MP4) with a poster frame next to it, then add a content step with a video background:
chrome.autoplay on and advance.trigger: "auto", the step advances when the clip ends. Otherwise it holds on the last frame.
Chapters and chrome
chapters group step ids under titles. The default player layout has no chapter menu. Chapters are what a chapter button action jumps to, what controls.seekToChapter(id) seeks to, and what Demo.Chapters lists in a custom layout.
chrome controls the frame around the screen:
Assets
A config references its media by path, relative to the demo folder:assets/ works, as does an absolute URL for a file hosted elsewhere. There is no manifest.
validatereports a path with no file behind it.devandbuildserve the folder next to the page.publishuploads each referenced file once and rewrites the paths to the hosted URLs in the copy it sends. Your file on disk does not change.
assets/ and reference it.
Theme
There is one theme: an indigo accent, a dotted canvas, a macOS-style frame, and a watercolor behind cover steps. Give a cover its ownbackground to replace the watercolor.
Customise the theme with four tokens. Set them project-wide in the project file’s tokens, or per demo in theme.tokens. The demo’s win.
Theme presets were removed. A demo’s
theme.preset and the project file’s theme still load and are ignored. validate warns about them, so validate --strict fails until you delete them.Brand and the page header
dev and build put a bar above the player: the brand mark and name on the left, the demo title, and up to two buttons on the right. All of brand is optional. With nothing set, the bar shows only the demo title.
logois an absolute URL or a path relative to the project root.validatechecks that a relative file exists and stays inside the project.buildcopies it todist/<slug>/brand/.- Leave
nameout when the logo image already carries the wordmark. logoHrefturns the mark into a link that opens in a new tab. Without it the mark links to/.- Button and
logoHrefURLs must behttp(s)ormailto. The buttons take their colour from theprimarytoken.
player.js. An iframe of a built page shows it unless you add ?embed=inline, and a page you assemble from the page contract does not have it. Under dev the bar also has an Edit button that opens the demo in the editor.
A demo can also carry its own theme.brand (logo, name, logoHref) for the header inside the player. That is the logo the editor’s demo settings set. It travels with the demo wherever the player runs, including <Demo> in a React app.

