What does clickreel do?
You are making an interactive demo of the user's real app. A visitor opens it on a landing page or from a link, clicks the highlighted spot, and the next real screen of the app appears. They are deciding in under a minute whether the app is worth trying. A good result walks through one thing the app does well in 6 to 12 clicks.
You write the storyboard and judge the result. Clickreel does the rest: it opens the app in a headless browser, performs each step for real, photographs the page before and after, and packs the screens and a player into one HTML file.
The command
Everything goes through one script. It needs only Node and a Chromium-based browser already on the machine.
node ${CLAUDE_SKILL_DIR}/scripts/clickreel.mjs <command>
If ${CLAUDE_SKILL_DIR} appears above as literal text, your agent did not fill it in: use the folder that contains this SKILL.md.
| Command | What it does |
|---|---|
build <storyboard> | Runs the steps and writes <name>.html (the demo) and <name>.stops.png (the contact sheet) next to the storyboard. Takes 10 to 30 seconds. |
check <storyboard> | Runs the steps and writes only the contact sheet, to a temporary folder. Use it to try a storyboard without touching the project. |
doctor | Checks that this machine can build a demo. Run it first if anything fails to start. |
init [file] | Writes a starter storyboard. |
Useful flags: --out <dir>, --json, --headed (watch the browser work), --force.
How to work
1. Decide what the demo shows
One demo, one flow. Ask yourself what the single most convincing thing this app does is, and build the demo around it. If the user named a feature or flow, that is the subject. If they said only "make a demo", pick the core flow yourself (the thing the README's first sentence promises) and say which one you picked. Do not ask the user to choose unless the app has several unrelated products.
2. Get the app running, and learn its real labels
Find how the app starts (package.json scripts, a README, a dist/ folder). Then choose one:
- Already running or you can start it yourself: set
"url". - A static site or a built folder: set
"serve": "./dist"and Clickreel serves it. - You want the build to be repeatable later: set
"start": "npm run dev"with"url", and Clickreel starts it and waits.
Steps target elements by the words a person sees: button text, a field's label or placeholder, an aria-label. Read the components for the flow you chose and copy the labels exactly. Guessing wastes a round trip.
The demo should open on a state worth looking at. An empty first-run screen or a login wall is a weak opening. If the app needs data or a signed-in session, set that up before the first stop: use the app's seed script, or an eval step that writes to localStorage, or sign in with a test account at the start. Put passwords and tokens in environment variables and refer to them as ${env:NAME}; never write a real secret into the storyboard.
3. Write the storyboard
Save it as demo.click.json in the project root, unless the user wants it elsewhere. If the project already has a Demoreel storyboard (*.reel.json), you can build from that file as it is.
{
"url": "http://localhost:5173",
"displayUrl": "acme.app",
"demo": {
"title": "Acme",
"intro": "See how a project goes from idea to done.",
"cta": { "label": "Try it free", "url": "https://acme.app/signup" }
},
"steps": [
{ "say": "Start a project in seconds" },
{ "click": "New project" },
{ "type": "Website launch", "into": "Project name" },
{ "click": "Create project" },
{ "say": "Add tasks as fast as you can type" },
{ "type": "Write the announcement post", "into": "Add a task" },
{ "press": "Enter" }
]
}
Each step becomes a stop (something the visitor does) or nothing:
| Step | In the demo |
|---|---|
{ "click": "New project" } | A hotspot on the element. The visitor clicks it and sees the real result. |
{ "type": "hello", "into": "Email" } | A hotspot on the field. Clicking it plays the text being typed. Leave out into to keep typing in the focused field. |
{ "press": "Enter" } | Key caps to click, or the visitor presses the real key. "Mod+K" is Cmd on macOS and Ctrl elsewhere. |
{ "hover": "Chart" } | A hotspot that reacts to hovering, or to a tap on a phone. |
{ "select": "Pro", "in": "Plan" } | A hotspot on a native dropdown. |
{ "say": "One short line" } | The caption shown during the stops after it. It stays until the next say. A say with no stop after it becomes a card with a Next button. |
{ "zoom": "Total" } | A note: the element is lit, the rest of the page dims, and a card shows the caption with a Next button. |
{ "scroll": 600 }, { "goto": "/settings" }, { "wait": 800 }, { "waitFor": "Report ready" }, { "eval": "..." } | No stop. The demo passes through them by itself. Use them for setup and for getting to the right place. |
The words next to each hotspot are written for you: Click “New project”, Type “Website launch”, Press with the key caps. Add "hint": "Open the settings" to a step to say it your own way. Do this when a step uses a selector that has no readable name (the default is then "Click here").
Targets are matched against visible names, exact match first. When a name is ambiguous add "nth": 2, or use a selector: "css=.sidebar button", "#save", "testid=submit". Add "optional": true to a step that may have nothing to act on, such as dismissing a cookie banner.
The full list of steps, options, styles and frames is in references/storyboard.md. Read it before using anything not shown here.
4. Build
node ${CLAUDE_SKILL_DIR}/scripts/clickreel.mjs build demo.click.json
A step that cannot be done stops the build and tells you exactly what is wrong:
Step 4 (click "Creat project") failed: Could not find "Creat project" to click.
Did you mean: "Create project"?
On screen now: "Cancel", "Create project", "Project name"
Screenshot of the page at that moment: /tmp/clickreel/demo-123/step-04-failed.jpg
Fix the step and build again. If the project already has a <name>.html that Clickreel did not write, the build refuses to replace it: set "name" in the storyboard or pass --out <dir>.
A build that succeeds can still print notes. Read them. Note: Step 4 (...) changed nothing you can see on the page means the step found something to act on, but the page looked the same afterwards: it nearly always found the wrong element, and its stop in the demo does nothing. Fix the target (a more exact name, nth, or a selector) and build again.
5. Look at the result before you report it
build writes a contact sheet, <name>.stops.png: one picture per stop, with the hotspot drawn on it and the step and caption under it, then the last screen. Open it and look at every picture. This is how you check your own demo.
Check each picture for:
- The hotspot. Is the box on the element you meant, and is that element in view? A box on the wrong thing means the name matched something else: use
nthor a selector. - Truth. Does the picture show the app working? Look for error messages, empty states, spinners that never finished, placeholder text. Each picture is what the visitor sees before they click, so the picture after it shows what their click did.
- Captions. Does the caption under the picture describe what is happening in it?
- The last picture. It sits behind the end card. It should be the best-looking state of the app.
Fix the storyboard and build again. Two or three rounds is normal. Stop when the sheet looks right.
6. Hand it over
Tell the user where the file is, its size, how many stops it has and what flow it shows. Give them the embed line that build printed:
<iframe src="demo.html" title="Acme: interactive demo" loading="lazy" allowfullscreen style="width:100%;aspect-ratio:1372/889;border:0;border-radius:12px"></iframe>
Say plainly what the file is: one HTML file with pictures of their app inside, which works from disk and from any web host, and loads nothing from the network. Report any notes Clickreel printed (page errors, covered elements, a large file). If you could not get a step to work and removed it, say so.
A GitHub README cannot run an HTML file. For a README, link to the demo where it is hosted (GitHub Pages serves docs/ well), or use a GIF from Demoreel.
Writing a good demo, in short
- Six to twelve stops. Every stop is a click the visitor has to make. Cut setup, or do it with
evalbefore the first stop. - Captions are headlines. Seven words or fewer, about the benefit ("Check things off as you go"), not the action ("Now click the checkbox"). The hint already says what to click. Three to five captions in a demo.
- Type real things. "Website launch", not "test" or "asdf". The typed text is part of the pitch.
- Open and close well. Set
demo.titleand a one-linedemo.introfor the start card. End on the best state of the app and setdemo.ctato where the visitor should go next. - Pick the size for where it will sit. The default
desktopviewport suits a landing page. For a mobile web app use"viewport": "phone". A smaller viewport makes the app look larger in a small embed.
More on structure, notes, autoplay and listening to the demo's events: references/writing.md.
Privacy and honesty
- Everything runs on this machine. Clickreel opens no network connection of its own; the browser loads only the app you point it at. The finished file makes no requests and tracks nothing.
- The demo is made of pictures of whatever was on the page, and it is meant to be published. Use test accounts and made-up data. Do not capture real customer data, real email addresses or internal URLs without the user's say-so.
- Show the app as it is. Do not use
evalto fake a feature, hide a real error, or make something look faster than it is. Seeding data and hiding developer overlays is fine; inventing behaviour is not.
What it cannot do
Web apps only. The screens are pictures, so a visitor cannot scroll inside a stop, select text or go off the path you chose. A drag step plays when its hotspot is clicked. It does not follow links that open a new tab, cannot reach inside iframes, and cannot upload files. Native alert() and confirm() boxes are accepted automatically and do not appear. If the flow depends on one of these, say so and build the part that works.
When something fails in a way the error message does not explain, see references/troubleshooting.md.