Skip to main content
A demo is a 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:
  • demos fixes the order dev lists demos in.
  • tokens sets the theme for every demo.
  • brand fills the page header.
To add a demo, capture one, scaffold a placeholder with 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 either content (a screen) or cover (a full-screen card built from widgets).

Content steps

Background. 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

The widgets: 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 a form_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.
The body is { 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 in assets/ (WebM or MP4) with a poster frame next to it, then add a content step with a video background:
With 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:
Anything under assets/ works, as does an absolute URL for a file hosted elsewhere. There is no manifest.
  • validate reports a path with no file behind it.
  • dev and build serve the folder next to the page.
  • publish uploads each referenced file once and rewrites the paths to the hosted URLs in the copy it sends. Your file on disk does not change.
The editor’s asset panel writes the path for you. By hand, copy the file into 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 own background 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.
  • logo is an absolute URL or a path relative to the project root. validate checks that a relative file exists and stays inside the project. build copies it to dist/<slug>/brand/.
  • Leave name out when the logo image already carries the wordmark.
  • logoHref turns the mark into a link that opens in a new tab. Without it the mark links to /.
  • Button and logoHref URLs must be http(s) or mailto. The buttons take their colour from the primary token.
publish does not upload a project-relative logo. The hosted page shows the rest of the brand and the command warns. Use an absolute URL for a logo that should appear there.
The bar is plain HTML around the player, not part of 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.

Check your work