Communitygithub.com

journey-demo

Use when someone wants a product demo video, walkthrough clip or storyboard of a web app flow — "make a demo of signing up", "clip the checkout journey", "storyboard the onboarding flow", "turn docs/journeys/x.md into a video", or refreshing a demo after the UI changed.

What is journey-demo?

journey-demo is a Claude Code agent skill that use when someone wants a product demo video, walkthrough clip or storyboard of a web app flow — "make a demo of signing up", "clip the checkout journey", "storyboard the onboarding flow", "turn docs/journeys/x.md into a video", or refreshing a demo after the UI changed.

Works with✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/dheerajsarwaiya/demo-skill/tree/main/journey-demo

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Journey demo

Write a journey, test it, clip it. Claude runs a user journey in Chrome, saves every screen as a frozen HTML page, lays the frames out on a storyboard the person edits in their browser, and only after they approve renders an MP4 with HyperFrames. Nothing is screen-recorded, so any word on any screen can change later without running the journey again.

Source of truth: STORYBOARD.md + snapshots/*.html in the demo folder. Everything else is generated.

Needs

  • Chrome with the Claude in Chrome extension (to run the journey and capture)
  • Node 18+, and npx hyperframes (stills and the render; no install step)
  • Scripts in this skill: scripts/freeze.js (capture), scripts/demo.mjs (build and the editable board). Run them by path: node <this skill>/scripts/demo.mjs …

The demo folder

docs/demos/<slug>/ in the person's repo, unless they name another place:

STORYBOARD.md        the plan: frames, captions, clicks, timing, notes      (edit)
snapshots/*.html     the captured screens                                    (edit text only)
index.html scenes/ boards/ .build/    generated by demo.mjs                 (never edit)
out/<slug>.mp4       the render

Suggest gitignoring scenes/ .build/ boards/stills/ out/.

1. The journey

Use a journey file when one exists (docs/journeys/*.md: Start:, numbered steps, Expect: checks, > Note: lines). If the browser-journeys skill is installed, it owns that format and its run rules, and you follow them. No journey yet? Write one with the person first: the demo is only as good as the steps.

Before running, ask in one message, with your recommendation for each:

  • Host: local or staging; never production unless they say so.
  • Branch: when the journey offers a choice (copy from library or write new, saved card or new card), which path the demo shows. Default: the shortest happy path.
  • Values: the journey's test values ("Journey test — please ignore") or demo-friendly ones you propose. Default: propose demo-friendly ones, and use them everywhere they appear.
  • Message and audience: draft both from the journey; they confirm.
  • Side effects: say what the run creates on that host (accounts, orders, bids) and the journey's cleanup, if it has one.

A journey written for another host (Start: local, seed commands, codes read from a dev log)? Its setup lines belong to that host: ask for the equivalents on this one, never guess.

2. Run it and capture

  1. resize_window to 1440×900 and keep that size for the whole run: every frame must share one viewport, and the build refuses mixed sizes.
  2. Do each step as the journey says. Check every Expect:; on a failure, stop and report. A demo is never built on a broken flow. A capture run needn't go at a watchable pace: wait only until the page has settled before each freeze.
  3. Sign-in and persona switches are the person's. Say "Sign in as and tell me when you're done", wait, then carry on. A code the journey prints to a dev log you started may be entered by you; never a password or a code from someone's inbox. Don't freeze sign-in screens (they show emails): the storyboard cuts from before sign-in to after it.
  4. Choose the frames: one click per frame. A frame is the screen right before a click the viewer needs to follow the flow. Typing, ticking and choosing happen before the freeze, so the frame shows them done; when the frame should show an empty field, freeze before typing. A screen reached by URL or with nothing to press is a frame with no click (it fades in). Skip setup and sign-in. Aim for 4–8 frames, and always freeze the final screen.
  5. Freeze: paste the whole of scripts/freeze.js into the page with javascript_tool (once per page load; typeof window.__freeze tells you), then await window.__freeze('03-open-the-orbit') (two-digit order, then a slug). Compare the viewport it returns with the first frame's; if it differs (a download or info bar opened), close the bar and freeze again.
  6. Each freeze downloads <name>.html to ~/Downloads. Move them into snapshots/ when the run ends. Freezing a name again makes Chrome save <name> (1).html: delete the old download first. If Chrome asks to allow multiple downloads, the person clicks Allow.

3. Write STORYBOARD.md

HyperFrames' storyboard format. One frame per frozen screen:

---
message: Open an Orbit in under a minute
audience: YouTube creators deciding whether to try ShoutOrbit
journey: docs/journeys/create-an-orbit.md (steps 4–5, staging, 2026-11-13)
url: staging.shoutorbit.com
status: draft
---

## Frame 3: Open the Orbit (8-12s)
- duration: 4s
- snapshot: 03-open-the-orbit.html
- click: Open the Orbit
- scene: Dates and offer fill themselves in

The bottom of the form, dates already filled. The cursor presses "Open the Orbit".
Open question: the title field still shows its placeholder. Type it out on screen, or keep it?
  • click: the element's whole label, exactly: its visible text, value, placeholder or aria-label ("Place bid · $25", not "Place bid"). The build finds it on every run and fails loudly if it can't. Leave it out on a frame with no click.
  • duration: seconds on screen, 3–5; 4s when left out.
  • scene: the caption on screen: short, about what the viewer gets ("Dates fill themselves in"), not what they press.
  • Plan lines: what the frame shows and what the cursor does, in plain words.
  • Open question: one line per thing the person must decide. Look at every frozen screen for: test or junk data on screen; personal data (emails, real names, account numbers); placeholders where real values belong; a caption that the screen contradicts ("live" over "Not taking bids yet"); stale dates. Raise them; never quietly fix them.

4. Build and hand over the board

node <skill>/scripts/demo.mjs serve docs/demos/<slug> --port 4321   # run in the background

It builds, then serves http://127.0.0.1:4321/. Open it for the person (Chrome or open) and stop. On the board they can change captions, clicks, seconds, titles and order, remove frames, write notes for Claude, retype words on the captured page itself (Edit page text; renaming the clicked element carries the click along), and press Approve for render. Every save rebuilds the stills.

5. Act on notes

When they say they've left notes, read STORYBOARD.md. For each > Note: line:

They ask forChange
Caption, timing, order, clickThe frame's keys in STORYBOARD.md
Different words on the pageText in that snapshots/*.html (text only: keep tags and classes)
A screen that wasn't captured, or a different stateRun that journey step again and freeze it
Something the flow can't showSay so; offer the nearest true version

Then turn the note into > Done: <what changed> (the board shows these as done), set status: draft in the frontmatter (only the person approves), run node <skill>/scripts/demo.mjs build docs/demos/<slug>, and tell them to refresh the board. The server can keep running.

6. Render

Only when the frontmatter says status: approved, or the person says go in chat:

cd docs/demos/<slug> && npx hyperframes check && npx hyperframes render -o out/<slug>.mp4

check warnings about sub-compositions, track density and a silent film are expected. Open the MP4 for them. If the UI changes later, run the journey again, re-freeze the frames whose screens changed, keep the storyboard, and render: that's the whole update.

Common mistakes

SymptomCause and fix
Cursor lands below or beside its targetFrames captured at different window sizes, or the page was mid-scroll. Recapture at one size.
nothing labelled "…" from the buildThe click text doesn't match the screen exactly (case, spacing, a →). Copy it from the page.
probe viewport was …Headless Chrome's window chrome changed; adjust CHROME_UI in demo.mjs.
A frame shows an empty fieldFrozen before typing. Fill, then freeze.
Images or fonts missing in a frameServed cross-origin without CORS, so freeze.js kept the URL; they load only while that host is up. Note it as an open question.
Stripe or other iframes blankfreeze.js drops iframes. Freeze the screen before or after them, or say what's missing.
Chrome not foundSet CHROME=/path/to/chrome.

Privacy

A snapshot holds everything that was on screen, with images inlined. Before anyone shares the board, the folder or the video, check the frames for personal data, and raise what you find as open questions.

Related Skills