Skip to main content
Capture records a click-through of a live web app and writes it out as a demo folder. You click through the product yourself. Each click becomes one step, with a screenshot of the page you clicked on and a cursor on the thing you clicked. There are two ways to record:
  • The CLI opens a Chrome window for you and writes the demo into your project. A script or an agent can drive it too.
  • The Chrome extension records in your own browser, signed in as you, with no terminal.

Record with the CLI

Two commands bracket a session:
start returns immediately, so your terminal stays free while you record. Every capture subcommand prints JSON, so a script can drive the same flow a person does. So can an agent: give it the agent skill and ask it to “record a demo of https://your.app”.

How it works

start launches Chrome with a debugging port and talks to it over the Chrome DevTools Protocol. It opens your URL in a new tab, injects a small recorder script, then spawns a detached listener process and hands your prompt back. The listener turns each click into a step until you run stop or cancel. The window is visible by default, because you drive the product. --headless is only useful when something else drives the page: a script sending clicks over CDP, or --connect-to-browser <url> pointing at a browser you already control. Chrome is found for you: Google Chrome first, then Chrome for Testing, Canary, or Chromium. Sites with bot protection tend to block the testing builds. --browser /path/to/chrome or the CHROME_PATH environment variable overrides the choice. The tab is sized to 1440×900 by default and captured at 2× pixel density, so a default session writes 2880×1800 screenshots. start waits up to --timeout milliseconds (120000 by default) for the first page load, and refuses to record if Chrome lands on its own error page.

What a click becomes

Clicks produce steps. Scrolling and typing are recorded as motion and only show up as video. The initial page load is not a step. Each click produces one content step:
  • The background is the page as it looked at click time, before the click’s own repaint and before any navigation it triggers. The screenshot, the pointer, and the label all describe the same page.
  • An annotation is placed at the point you clicked: a message with the cursor variant, reading Click on "Save changes". The label is the clicked element’s accessible name (aria-label, title, alt, its own text, or a value, placeholder, or name), first line only, capped at 48 characters. When nothing usable is found, the text is Continue.
  • A zoom of 1.35× toward the click, eased back as the point approaches an edge. --no-zoom leaves the screen unzoomed.
  • advance.trigger: "click", so the viewer moves on by clicking the annotation.
The last page you reach is not captured, because a step describes the page you clicked on. To end the demo on the destination, click once more, anywhere harmless, before you stop.
Two behaviours are worth knowing while you click around:
  • A click on a link is held. The recorder cancels the navigation, lets the source page be captured, then navigates for you.
  • A click that opens a new tab is followed. For three seconds after a click, a page Chrome opens is treated as the one to keep recording.

What stop writes

stop builds the demo from the recorded screens and writes a folder:
The slug comes from --name, or the page host with www. trimmed, lowercased and hyphenated. If that folder exists it becomes onboarding-2, onboarding-3, and so on. Without --out, stop must run inside a project. It writes into that project’s demos/ and appends the slug to interactive-demo.json when that file keeps a demos list. --out <dir> writes <dir>/<slug> instead and touches no project file. The config gets a fresh demo id, --name as the title, and steps s1, s2, and so on. --compress-images re-encodes screenshots to WebP and keeps the PNG whenever the WebP is not smaller. stop then tears the session down: the listener, Chrome, its temporary profile, and the working files. It prints the demo folder, the step count, and one label per step, so you can read the captured story without opening the JSON.

Video steps

Video needs ffmpeg on PATH. start checks once, up front. With no ffmpeg it prints a warning to stderr, reports videoDisabledReason in its JSON, and records everything as stills for the whole session. --no-video asks for stills and silences the warning. While a session runs, the listener keeps a rolling buffer of screencast frames. When you scroll or type and then click, that click commits the preceding motion as a video step instead of a still. Only the densest continuous run of frames is kept, so idle frames before the motion and the settle after the click are dropped. The clip opens with a half-second hold on its first frame and is padded to at least 1.5 seconds by holding its last frame. It is encoded as VP9 WebM, with its first frame written alongside as the poster. A burst under 10 frames or 1.2 seconds, or one whose frames are all identical, falls back to a still. A video step carries the same cursor annotation and click-to-advance as a screenshot step. The auto-zoom applies to screenshot steps only.

Check and undo while you record

capture status reads the session and reports without touching the browser:
  • ready: the recorder armed.
  • browserAlive: the capture browser is still running. If this is false, further clicks record nothing. Run stop to keep what you have.
  • stepCount, stepLabels, steps: the story so far, one entry per recorded click.
capture undo drops the most recent step. Use it for a probing click you do not want in the demo. capture cancel throws the session away. It kills the listener and Chrome, removes the working files, and writes no demo. You rarely need a session id. With one session running, every subcommand finds it. With more than one, they stop and list the ids so you can pass --session <id>.

Capture an app behind a login

Two things make a login survive. --profile <name> points Chrome at a persistent user-data directory instead of a throwaway one, so cookies live on between captures. A bare name maps to a folder under the capture home. A value containing a slash is treated as a directory path. capture profiles lists what you have, and whether each profile holds cookies. Signing in inside the capture browser does not always work. Identity providers reject an automation-controlled browser, even when a person is doing the clicking. capture login opens a separate, ordinary Chrome window on the same profile, with no debugging port and no automation flags:
start evicts the login window from the profile before taking it over, so its cookies are flushed first. A persistent or attached profile is never deleted on stop, cancel, or an interrupted start.

When a capture goes wrong

stop with zero steps prints ok: false with the reason and cleans the session up, so no cancel is needed. The initial page load is not a step. A step needs an actual click.
start waits up to 15 seconds for the listener to report ready. If it does not, start fails and prints the tail of the listener log and the Chrome log. Nothing is left behind. Start again.
After each click the listener captures the source page, follows the navigation, waits about two seconds, and re-arms the recorder on the new page. A click during that window can land before the recorder is back. Let the new page paint before clicking again. capture status shows exactly which clicks were recorded.
The step shows what was on screen at click time, half-loaded skeletons included. Undo it, wait, and click again.
Ctrl+C during start tears down the Chrome and listener it had already spawned and removes the half-written session.
Each step is written to the session file as it is recorded, so the steps before the crash are still there. Run capture stop and it exports them.
Any command that touches it says so and names the session. capture cancel --session <id> cleans up what it can and reports what it recovered.

Where session files live

A running capture keeps its state outside your project, under ~/.interactive-demo/capture/:
  • sessions/<id>.json is the live session: the browser it launched, the tab it is recording, and every screen recorded so far.
  • captures/<id>/ holds the screenshots and video frames recorded so far, plus listener.log.
  • profiles/<name>/ holds persistent Chrome profiles.
INTERACTIVE_DEMO_CAPTURE_HOME moves all of it somewhere else. Every capture flag and its default is in the CLI reference.

The Chrome extension

Interactive Demo Capture records a demo in your own Chrome, signed in as you, with no terminal. Install it from the Chrome Web Store.
It is a separate extension from the hosted Inkly demo agent’s, which is covered in Chrome extension. Each works only with its own product.
1

Open your product

Open your product in a tab and go to where the demo should begin.
2

Start recording

Open the extension and pick Start Recording. Every click becomes a step: the page as it was just before the click, with a cursor on what you clicked. Scrolling or typing before a click is kept as a short video step. The popup counts the steps as you go.
3

Stop and save

Pick Stop & save. The recording stays in the extension until you decide what to do with it.
A recording is one tab. A click that opens a new tab ends the useful part of it. The extension’s settings turn the video steps and the click zoom off, or blur emails, numbers, and form fields while you record. When you stop, the extension’s last screen offers two ways to use the recording.
The zip holds one demo folder, demo.config.json and its assets/, named after the site you recorded. Import it into a project with no unzip step:
The demo lands in demos/acme-3f91b2/. Add --demo <slug> to choose the folder name.This path works fully offline. Nothing leaves your machine. The popup also copies the command, or a prompt that hands the import and a caption review to your agent.
Either way, the recording stays in the extension until you discard it, or pick Done after an upload. A failed upload or a lost download costs nothing.

After the capture

A captured demo is an ordinary demo folder, the same thing init writes and the editor edits. From here:
  • interactive-demo validate checks the config and that every referenced file exists.
  • interactive-demo dev previews it. Rewrite the generated Click on "…" text into something worth reading, in the editor or in the config.
  • interactive-demo publish puts it online, or interactive-demo build writes the static folder to deploy. See Publish and self-host.