Every UI state · an animated cursor that glides to each click (with a ripple) · a zoom‑to‑focus camera · the loading/streaming captured live (spinner spinning, results coming in) · step captions · a progress bar. Not a single final‑state "hero shot" — the viewer follows the whole flow.
↑ produced by this tool — every state, the click, the agent's work, and the proof.
Most README/demo GIFs are hero shots — they show the final screen, so a viewer can't tell where the user clicked, what the empty state looked like, or how the result was reached. This tool generates true walkthroughs: clean per‑state frames, an overlaid cursor that eases to each target and ripples on click, an Arcade‑style camera that zooms to the action (and pulls back to frame the result), a step caption, and a progress bar.
It's fully scripted + reproducible (the spec is a checked‑in "tape"), so the GIFs double as a regenerable integration smoke‑test of your UI.
walkthrough.specs.mjs 1. SPEC ordered cap/act ops per feature
│
▼
node walkthrough.mjs 2. CAPTURE Playwright drives your app, screenshots a CLEAN
frame at each state + records the pointer target
→ public/wt/<id>/*.png + writes src/walkthrough.data.js
│
▼
npx remotion render 3. RENDER Remotion overlays the animated cursor + ripple +
src/index.js WT-<id> zoom/pan camera + caption + progress → mp4
│
▼
ffmpeg (two‑pass palette) 4. GIF stats_mode=diff + lanczos + bayer + diff_mode
→ assets/feature-*.gif =rectangle → clean, small, looping GIF
Prerequisites: Node 18+, ffmpeg on PATH, and your app running locally in a clean
(no‑auth / demo) state.
git clone https://github.com/HomenShum/feature-walkthrough-gif
cd feature-walkthrough-gif
npm install
npx playwright install chromium
# Render the bundled worked example (ships with captured frames — no app needed):
npm run render:example # -> out/example.mp4
ffmpeg -y -i out/example.mp4 -vf "fps=15,scale=720:-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=128:stats_mode=diff[p];[s1][p]paletteuse=dither=bayer:bayer_scale=3:diff_mode=rectangle" -loop 0 example.gifThe bundled example is a solo-founder 3D proof-run app
(builder console → generate a scroll-driven 3D product story → customer-facing landing
page → internal proof report with gates) — the captured frames +
src/walkthrough.data.js are included so it renders immediately.
- Write a spec. Edit
walkthrough.specs.mjs— each feature is an ordered list of ops:{ id: "Search", title: "Instant Search", accent: "#10b981", tab: "Search", steps: [ { cap: "Type a query", cursor: "input" }, { act: "fill", sel: "input", value: "invoices", commit: "Enter" }, { cap: "Hit search", cursor: "btn:Search", click: true }, { act: "click", sel: "btn:Search" }, { act: "sleep", ms: 1200 }, { cap: "Results, instantly", hold: 90 }, // captures the result { act: "waitText", value: "results" }, { act: "scrollEl", sel: "df", last: true }, // center the result widget { cap: "Filter to what matters", hold: 100 }, ], }
cap= capture this state.cursor= where the pointer glides (click:trueripples there).hold= frames to dwell.cap+burst: { ms, every }= capture the loading/streaming motion — a rapid frame sequence (spinner spinning, status updating, results streaming in), played back as real motion instead of a frozen snapshot. Put it right after the click that starts the work.act= advance the UI:fill | click | upload | sleep | waitText | notRunning | scrollEl | scrollText | scrollLastChat | scrollTop | scrollY.- Selector shorthand:
textarea·input·file·drop·chat·btn:<name regex>·aria:<label>·aria^:<prefix>·df/iframe/metric(forscrollEl) · any CSS.
- Capture + render: start your app's clean harness, then
node walkthrough.mjs→npx remotion render src/index.js WT-<id> out/<id>.mp4→ ffmpeg. - Embed the GIF under each feature's README heading.
Built and battle‑tested against Streamlit (see the capture lessons in
SKILL.md: scope locators to the active tab panel, await upload registration, data‑grids are canvas, capture the loading state on purpose), but the spec/selector model works for any browser UI. Seewalkthrough.solo-founder.mjsfor a non-Streamlit adaptation (React SPA with hash routes — simpler selector model,gotoaction for URL-based navigation).
Distilled from Arcade, Supademo, HowdyGo, CleanShot, Rekort, Mux, ubitux's
High‑quality GIF with FFmpeg, GIPHY, and WCAG — see SKILL.md for the
full list with sources:
- Two‑pass palette is mandatory (
stats_mode=diff+lanczos+bayer+diff_mode=rectangle) — the difference between a banded mess and a clean demo, and it shrinks the file. - Zoom/pan to focus, eased, with a pre‑move delay — click‑triggered zoom (~1.3–1.6×) beats highlight‑only for comprehension and makes small text legible.
- Cursor at ~1.5–2× OS size + a click ripple — a real cursor is invisible after downscaling; the ripple is the silent stand‑in for a click sound.
- Show every state, including loading — never cut an action straight to a finished result.
- Pace from the caption (no narration), write outcome statements ("Filter to overdue invoices", not "Click Filter").
- 3–10 s, one feature, seamless loop, ~640–800 px wide. Ship MP4 + GIF; GitHub auto‑embeds a bare MP4 URL.
Single-cursor capture can't show what makes a collaborative app special — a change
in one client appearing live in another. So the tool also has a multi-pane
mode: it drives N browser contexts (separate "users") and renders them
side-by-side, cursor on the acting client, with a burst over the moment the
change propagates.
The composition scales to N panes — same spec model, one window per client:
Ships with a worked example (the live-collab counterpart to the single-pane one):
examples/collab-demo/— a runnable, zero-dependency local app (Node SSE server + vanilla JS) that faithfully reproduces the Convex reactive pattern: optimistic paint → server commit → broadcast to all clients → atomic temp→real swap; presence; a server-led agent that streams to every client. Runs with no cloud login, so the GIF reproduces anywhere.examples/convex-reference/— the real Convex + React implementation of the same app (useQueryreactive subscriptions,useMutation().withOptimisticUpdate,ctx.scheduler+internalMutationfor the streamed agent) — the production reference, mapped 1:1 to the local demo.
Reproduce it:
node examples/collab-demo/server.mjs # local demo on :8930 (no install, no login)
node walkthrough.collab.mjs # multi-pane capture: Client A + Client B
npx remotion render src/index.js WTC-LiveSync out/collab.mp4
# then the same two-pass ffmpeg palette → assets/feature-collab.gifPanes + steps live in walkthrough.collab.specs.mjs; the 2-up renderer is
src/Walkthrough2up.jsx. See STACK_GUIDELINES.md for why
Convex + React demos need this and Streamlit doesn't.
The repo also ships with captured walkthroughs of NodeRoom — a production Convex + React live-collaborative diligence room. These prove the tool works against a real, deployed app (not just a demo harness).
Specs: walkthrough.noderoom.specs.mjs. Capture: node walkthrough.collab.mjs (the NodeRoom
specs are imported into the collab specs). Render: npx remotion render src/index.js WTC-NRsolo
/ WTC-NRsync / WTC-NRfresh / WTC-NRdeepDive.
What's worth showing in a walkthrough differs by architecture — a single-cursor
capture flatters a single-user Streamlit data app but misses what makes a
live-collaborative Convex + React app special (a change in one client appearing
live in another). See STACK_GUIDELINES.md for per-stack
guidance — which SDK primitives produce capturable motion, single-pane vs multi-pane
capture, and what to burst — for Streamlit, Convex+React, and Next.js+SQL on Vercel,
grounded in the latest Streamlit & Convex docs.
This repo is a Claude Code skill —
drop it in .claude/skills/ (or reference SKILL.md) and Claude can drive
the whole pipeline: write a spec, capture, render, and embed the GIFs for you.
MIT © Homen Shum





