Scaffold a Playwright-driven walkthrough video of a web app — chapter cards, action annotations, smooth scrolling, fake cursor, and a Driver.js spotlight for explainer beats. Use when the user asks to record / produce / regenerate a feature walkthrough, demo, or preview video.
How this skill is triggered — by the user, by Claude, or both
Slash command
/playwright-walkthrough:playwright-walkthroughThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
A reusable pattern for producing a polished **walkthrough video** — a scripted tour of a web app recorded by Playwright. The output is a `.webm` that drops into a PR description, release note, stakeholder email, or docs page.
A reusable pattern for producing a polished walkthrough video — a scripted tour of a web app recorded by Playwright. The output is a .webm that drops into a PR description, release note, stakeholder email, or docs page.
Builds on Playwright 1.59+'s native page.screencast API for chapter cards and action annotations. The skill adds only what Playwright doesn't: a fake cursor dot (browser video omits the OS cursor), a Driver.js spotlight for explainer scenes, and smooth-scroll helpers.
User asks for any of:
A Playwright spec (e.g. <tests-dir>/walkthrough/<name>.spec.ts) that, when run, records a video with:
page.screencast.showChapter()page.screencast.showActions()requestAnimationFrame with ease-in-out, tunable durationpage.screencast API)driver.js as a devDependency (used by the spotlight helper)Record from whatever branch the user is already on — no dedicated preview branch required. The walkthrough spec is just another Playwright file; leave staging and commits to the user.
Look for playwright.config.*, a playwright/ dir, or @playwright/test in package.json. If found, use the existing harness — you get fixtures for free (login helpers, globalSetup for DB seeding, baseURL, stored auth state).
Bump @playwright/test to 1.59+ if it isn't already, and npm install --save-dev driver.js.
Default to a hidden subdirectory — .walkthrough/ — following the same convention as .vscode/, .venv/, .terraform/. Self-contained, scoped to the project, trivially gitignorable:
<project-root>/
├── <project's normal files>
└── .walkthrough/ ← self-contained Playwright install
├── package.json
├── playwright.config.ts (baseURL → the running dev server)
├── overlay.ts
├── feature.spec.ts
└── node_modules/
Setup (run inside .walkthrough/):
npm init -y
npm install --save-dev @playwright/test@^1.59 driver.js
npx playwright install chromium
Then add .walkthrough/ to the project's .gitignore (or leave it untracked). Run specs from inside that directory so the local node_modules and playwright.config.ts resolve:
cd .walkthrough && npx playwright test feature.spec.ts --project=chromium
Don't install Playwright at the project root unless the user explicitly asks. Playwright pulls in browser binaries (~200MB) and driver.js that likely shouldn't be committed to a non-test codebase.
Sibling directory (../<repo>-walkthrough/) is an option if the user prefers zero footprint in the project — but ask first, since it means writing outside the current directory.
When the user asks for a new walkthrough, walk through these steps. Don't create branches or commits unless the user asks.
Check for playwright.config.*, a playwright/ dir, or @playwright/test in package.json.
.walkthrough/ in the project root and set up Playwright there as described above. Remind the user to add .walkthrough/ to .gitignore.Then npm install --save-dev driver.js in whichever directory Playwright lives in.
Drop template/overlay.ts from this skill alongside the walkthrough spec — for path A, inside a walkthrough/ subfolder of the project's tests directory; for path B, at the root of .walkthrough/. The file is generic — no project-specific references inside.
Start from template/walkthrough.spec.ts.template. Customize:
test.use({ viewport }) — HD default (1440×900) works welltest.setTimeout(5 * 60_000) — default is 30s, which fires mid-recordingPLAYWRIGHT_HTML_OPEN=never \
npx playwright test walkthrough/<feature> --project=chromium
Use whatever package manager / script the project already uses (yarn playwright test …, pnpm …). Wire baseURL in however the project expects — config, env var, or --config flag.
Output lands at test-results/<spec-name>-<test-title>-<project>/walkthrough.webm. Playwright wipes test-results/ on every run, so copy the file out before re-running.
Record locally, not in CI. Headless CI runners often lack the GPU, font rendering, and viewport stability needed for a polished recording.
ffmpeg -i walkthrough.webm out.mp4 # re-container
ffmpeg -i walkthrough.webm -ss 00:00:02 -to 00:03:00 out.mp4 # trim
ffmpeg -i in.webm -vf "pad=1440:900:(ow-iw)/2:(oh-ih)/2:color=#0c101c" out.mp4 # pad narrow recordings
Useful once a walkthrough has enough scenes that re-recording the whole thing to tweak one is painful, and it's the cleanest way to mix viewport sizes. Requires ffmpeg on PATH.
Split into one test() per scene (or one spec file per scene). Each calls page.screencast.start/stop around its own segment and produces its own .webm. To iterate, re-record only the scene you changed.
cat > scenes.txt <<'EOF'
file 'scene-01-intro.webm'
file 'scene-02-filter.webm'
file 'scene-03-detail.webm'
EOF
# Fast path — scenes share codec/resolution/framerate
ffmpeg -f concat -safe 0 -i scenes.txt -c copy combined.webm
# Fallback if codecs/dimensions disagree
ffmpeg -f concat -safe 0 -i scenes.txt -c:v libvpx-vp9 -b:v 2M combined.webm
Each scene boots cold (re-auth, re-navigate). Open each with page.screencast.showChapter() — the card covers the initial-render flash.
Record a mobile scene in its own spec at a phone viewport, then pad it up to the desktop canvas before concatenation. The phone-shaped frame sits centered on a dark stage — the "DevTools device toolbar" look, no CDP hackery.
ffmpeg -i mobile-scene.webm \
-vf "pad=1440:900:(ow-iw)/2:(oh-ih)/2:color=#0c101c" \
-c:v libvpx-vp9 -b:v 2M mobile-scene-padded.webm
Then list mobile-scene-padded.webm in scenes.txt alongside desktop scenes.
All exported from overlay.ts.
installOverlay(page, opts?) — injects Driver.js, cursor element, and mousemove tracking via addInitScript. Call once, before any navigation. Options: stagePadding, stageRadius, overlayOpacity.pause(page, ms = 900) — a waitForTimeout. Reads better than waitForTimeout(900) scattered through a spec.smoothScrollTo(page, locator, settleMs?, durationMs?) — animated scroll that centers the locator. Ease-in-out.smoothScrollBy(page, deltaY, settleMs?, durationMs?) — animated relative scroll.highlight(page, locator, { hold? }) — moves the dim-veil spotlight to the locator. Successive calls animate between targets. Hides the fake cursor while active.clearSpotlight(page) — tears down the spotlight and restores the cursor. Call at the end of a spotlight scene before one that needs the cursor back.Not wrapped — call directly:
page.screencast.start({ path, size }) / page.screencast.stop() — begin/end recordingpage.screencast.showChapter(title, { description, duration }) — full-screen title card with blurred backdroppage.screencast.showActions({ position, duration, fontSize }) — toggle action annotations on/off for the rest of the testpage.screencast.showOverlay(html, { duration }) — custom HTML overlay (use for one-off callouts)Use the spotlight for explainer scenes ("here's what this page is"). Use the cursor for action scenes ("click this to filter"). Mixing them reads as noisy. Convention:
highlight hides the cursor while active; clearSpotlight brings it back.
Don't change viewport mid-recording — video.size is locked at context creation. Instead, record the mobile scene in its own spec at a phone viewport, then pad it up during stitching (see "Mixing desktop and mobile scenes" above).
addInitScriptaddInitScript runs on every document load. Vue Router / React Router SPA routes don't re-run it — but the overlay DOM is attached to document.body, which survives SPA nav, so the cursor keeps working. The Driver.js singleton is stored on window, which also survives SPA nav.
If the target app has a scrollBehavior override in Vue Router or similar, hash-anchor navigation may not fire. smoothScrollTo / smoothScrollBy explicitly animate window scroll to dodge this.
If the spec hardcodes a numeric ID (e.g. /posts/42) and the app's data comes from a seed, that ID can shift on reseed. Two approaches:
globalSetup if the harness supports itDriver.js sets its overlays at z-index: 10000 by default, which is below our cursor (z-index: 2147483646). If your app reaches into that territory, Driver.js exposes stageRadius and popoverOffset but not a z-index knob — worst case, bump the cursor's z-index in overlay.ts.
pw-walk- so nothing collides with app IDs.<feature-name>.spec.ts under the project's walkthrough/ test folder..webm somewhere stable after a good take — Playwright wipes test-results/ on the next run.A ~3-minute walkthrough typically comprises 8–10 scenes: one orientation / spotlight tour of the main surface, several interaction scenes, and a closing chapter card. Budget 15–30 seconds per scene — longer for spotlight explainers, shorter for crisp click-through demos. Err on the side of more, shorter scenes rather than a few long ones; the chapter transitions give the viewer a breather and make the whole thing feel deliberate.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub wreality/playwright-walkthrough --plugin playwright-walkthrough