| name | demo-video |
| layer | stack |
| description | Record a WebM demo video of a UI flow with shot-scraper video โ a YAML storyboard drives the browser (click, type, scroll, wait) with an animated cursor overlay, so a reviewer can watch the feature instead of driving it. Use to attach motion evidence to a UI sign-off (#307), to turn an issue's "How to test" steps into a watchable clip, or when asked to "record a demo", "make a video of this flow", "show me it working". |
| allowed-tools | Bash, Read, Write, Grep, Glob, SendUserFile |
Demo Video โ storyboard-recorded WebM demos
Records a watchable demo of a UI flow using
shot-scraper video
(Simon Willison, Apache-2.0, Playwright-based): a declarative YAML storyboard
opens the app, clicks, types, scrolls and waits โ with an injected animated
cursor + click-ring overlay so the recording reads like a human demo, not a
ghost session.
What this is: evidence capture โ the motion analog of scripts/app-shots.mjs.
What this is NOT: assertion testing. The e2e harness (/e2e-smoke) remains
the correctness gate; Playwright's own video: 'on' test artifacts (#356) remain
the test-run recordings. This skill is for narratable demos a reviewer watches.
When to use
- UI sign-off (#307/#590): alongside the staging preview URL, attach a clip
of the flow under review โ richer than a PNG, cheaper than the reviewer
driving it themselves.
## ๐งช How to test: a storyboard is an executable version of those
numbered steps; the video is the before/after evidence.
- POC demos: show a
pocs/* feature working without deploying.
- On ask: "record a demo", "video of this flow", "show me it working".
Setup (once per session)
bash scripts/demo-video-setup.sh
Idempotent. Installs shot-scraper via pip and makes sure Playwright's expected
chromium exists โ downloading it where possible, or (in the egress-blocked web
harness) symlink-shimming the expected build dirs onto the pre-installed
/opt/pw-browsers browsers. Don't hand-roll the shim; don't conclude "no
browser" from a failed playwright install (see CLAUDE.md's headless-browser
note โ same trap).
Recording
- Copy
example-storyboard.yml (next to this file) and adapt it. The
storyboard can boot the app itself via server: (e.g. pnpm dev), or point
url: at an already-running server / local HTML file.
- Output path:
screenshots/<name>.webm โ same HARD-GATE location rule as
screenshots; *.webm/*.mp4 are gitignored so recordings stay out of commits.
- Run it:
shot-scraper video storyboard.yml
shot-scraper video storyboard.yml --mp4
Storyboard essentials
output: screenshots/feature-demo.webm
url: http://localhost:3000/
server: pnpm dev
viewport: {width: 1280, height: 720}
cursor: true
wait_for: "text=Dashboard"
scenes:
- name: Create a thing
do:
- click: "#new-thing"
- type: {into: "input[name=title]", text: "Demo item", delay_ms: 40}
- press: Enter
- wait_for: "text=Demo item"
- screenshot: screenshots/feature-demo-result.png
- pause: 1
Actions: click, type (visible keystrokes) / fill (instant), press,
scroll (eased, or to: a selector), pause, wait_for, wait_for_url,
open, js, screenshot, sh, python. Full syntax: the video.py pydantic
models in the shot-scraper source, or shot-scraper video --help.
Authed apps: log in once with shot-scraper auth <url> auth.json, then
record with shot-scraper video storyboard.yml -a auth.json. Never commit
auth.json.
Delivering the video
GitHub's API can't attach binary media to comments, so pick by context:
- Interactive session โ send the
.webm to the user directly (SendUserFile).
- PR evidence โ upload as a CI artifact (like the #356 e2e reports) and link
it from the PR comment; or drag-and-drop manually โ a human gesture, not an
agent one.
- WebM plays in every modern browser; only convert to MP4 (
--mp4, needs
system ffmpeg with libx264) when the audience needs QuickTime/Slack inline.
Gotchas
type vs fill: use type with delay_ms: 25โ50 for anything on camera โ
fill is instant and reads as a glitch.
- Recording starts after the top-level
wait/wait_for, so open on a settled
page; add a trailing pause: 1 so the last state is visible.
- A failed
wait_for/selector aborts the recording with a Playwright timeout โ
that's a feature (broken flow โ no misleading demo), but keep storyboards
short and re-runnable.
sh:/python:/server: in a storyboard are arbitrary code execution โ treat
third-party storyboards like shell scripts.
- Videos are viewport-size at 25fps VP8; bigger viewports = bigger files. 1280ร720
is the sweet spot.