UI Demo Video
Record a Playwright-driven walkthrough of the running app into a shareable video,
emitting a PNG frame per scene. The frames are the point for the model: Read
them after recording to visually verify the UI state, and iterate (fix code or
scenario, re-record) until the frames show the expected result. The video is the
human-shareable artifact (GitHub PR descriptions accept mp4 drag-drop).
When to use
After UI work that a video can verify: a new element, layout change, or flow.
Use proportionate visual feedback with existing tooling. Record a video when
requested, required by the repo, or useful within the authorized UI task; a small
visual check does not require installing a recording toolchain.
Not for API-only or non-visual changes, and not as a test suite: scenes
demonstrate and verify visually, they do not assert.
Prerequisites
- The app runs locally. Find the project's documented run path: a project run
skill, README, package scripts, and use that; do not invent a launch command.
- Playwright is installed in the project (
@playwright/test or
playwright) with a Chromium browser available. If absent, use an available
browser tool for visual checks and report the video prerequisite. Install a new
persistent dependency/browser only when that setup is in the authorized scope,
using the repository's package manager and documented setup. Do not silently
modify package or lockfiles just to record a demo.
ffmpeg on PATH for mp4 conversion (optional: the webm is always produced).
Workflow
- App running first. Start the dev server via the documented run path, in
the background, and health-check it before anything else. If booting can
reseed or migrate a local dev database, back that file up first. Seed the
data the scenes need through the app's real surface: a fixture/seed
endpoint, the REST/RPC API, a documented CLI seeder, never direct DB
writes: seeded state must pass the same validation real usage does.
- Copy the harness, then write a scenario file next to it. Copy
scripts/harness.mjs from this skill's directory into the project's tmp/
folder: the harness must live inside the project so Node resolves the
project's own Playwright install: then write tmp/<scenario>.mjs beside it
(example below). Scenes should be short and named for what they prove. Use
highlight() to draw the eye to the element under test. List every route
the scenario visits in prewarm so dev-compile skeletons stay out of frame.
- Run it:
node tmp/<scenario>.mjs. Outputs land in tmp/<name>/:
per-scene PNGs, <name>.webm + <name>.mp4, manifest.json. A scenario
failure still saves the video and a scene-FAIL.png; those are debugging
evidence, not garbage.
- Feedback loop (mandatory): Read every
scene-*.png with the Read tool
and check the UI is actually correct: the element present, states right, no
hidden runtime errors or half-loaded skeletons. Inspect unfiltered UI/error
evidence first; overlay suppression is presentation-only and must not conceal
a verification failure. Wrong → fix in scope and re-record. Only
a frame-verified recording counts as evidence.
- Cleanup: delete the demo entities you seeded (through the same real
surface you created them with), stop any dev server you started, and confirm
the port is closed.
- Delivery: provide the local recording and frames. Attach them to a PR or
tracker only when existing authorization explicitly covers that external write.
When authorized, use the host's supported attachment mechanism; do not turn
local verification into an implicit publication step.
Scenario example
import { recordUiDemo } from "./harness.mjs";
const BASE = "http://localhost:3000";
await recordUiDemo(
{
name: "my-feature-demo",
baseUrl: BASE,
prewarm: ["/items", "/items/42"],
},
async ({ page, scene, highlight }) => {
await scene("list shows the new item", async () => {
await page.goto(`${BASE}/items`, { waitUntil: "networkidle" });
await page.getByText("Quarterly report").first().click();
});
await scene("detail dialog renders the new action", async () => {
await page.getByRole("button", { name: "Share" }).click();
await highlight(page.getByRole("dialog").getByRole("link", { name: "Copy link" }));
});
},
);
Notes
- The harness leaves overlays visible by default. After inspecting and preserving
unfiltered failure evidence, an optional presentation recording may set
hideNextDevOverlay: true or explicit hideSelectors. Those frames alone do
not establish that no runtime error occurred.
- Realistic demo data reads better than test slugs: name seeded entities like
a user would ("Sprint review"), not "TEST-1234 probe".
- Viewport defaults to 1280×720; override via
viewport if the surface needs it.
- mp4 conversion needs
ffmpeg on PATH; without it the webm is still produced.
- The app under test must be the working tree you changed: the dev server
compiles from source, so a stale server proves old code; restart when in doubt.
1---2name: ui-demo-video3description: Use after UI work that can be verified visually, such as a new element, layout change, or user flow in a web app whose dev server a browser can drive. Not for API-only or non-visual changes, and not a test suite.4---56# UI Demo Video78Record a Playwright-driven walkthrough of the running app into a shareable video,9emitting a PNG frame per scene. **The frames are the point for the model**: Read10them after recording to visually verify the UI state, and iterate (fix code or11scenario, re-record) until the frames show the expected result. The video is the12human-shareable artifact (GitHub PR descriptions accept mp4 drag-drop).1314## When to use1516After UI work that a video can verify: a new element, layout change, or flow.17Use proportionate visual feedback with existing tooling. Record a video when18requested, required by the repo, or useful within the authorized UI task; a small19visual check does not require installing a recording toolchain.20Not for API-only or non-visual changes, and not as a test suite: scenes21demonstrate and verify visually, they do not assert.2223## Prerequisites2425- The app runs locally. Find the project's documented run path: a project run26 skill, README, package scripts, and use that; do not invent a launch command.27- Playwright is installed **in the project** (`@playwright/test` or28 `playwright`) with a Chromium browser available. If absent, use an available29 browser tool for visual checks and report the video prerequisite. Install a new30 persistent dependency/browser only when that setup is in the authorized scope,31 using the repository's package manager and documented setup. Do not silently32 modify package or lockfiles just to record a demo.33- `ffmpeg` on PATH for mp4 conversion (optional: the webm is always produced).3435## Workflow36371. **App running first.** Start the dev server via the documented run path, in38 the background, and health-check it before anything else. If booting can39 reseed or migrate a local dev database, back that file up first. Seed the40 data the scenes need through the app's **real surface**: a fixture/seed41 endpoint, the REST/RPC API, a documented CLI seeder, never direct DB42 writes: seeded state must pass the same validation real usage does.432. **Copy the harness, then write a scenario file next to it.** Copy44 `scripts/harness.mjs` from this skill's directory into the project's `tmp/`45 folder: the harness must live inside the project so Node resolves the46 project's own Playwright install: then write `tmp/<scenario>.mjs` beside it47 (example below). Scenes should be short and named for what they prove. Use48 `highlight()` to draw the eye to the element under test. List every route49 the scenario visits in `prewarm` so dev-compile skeletons stay out of frame.503. **Run it:** `node tmp/<scenario>.mjs`. Outputs land in `tmp/<name>/`:51 per-scene PNGs, `<name>.webm` + `<name>.mp4`, `manifest.json`. A scenario52 failure still saves the video and a `scene-FAIL.png`; those are debugging53 evidence, not garbage.544. **Feedback loop (mandatory):** Read every `scene-*.png` with the Read tool55 and check the UI is actually correct: the element present, states right, no56 hidden runtime errors or half-loaded skeletons. Inspect unfiltered UI/error57 evidence first; overlay suppression is presentation-only and must not conceal58 a verification failure. Wrong → fix in scope and re-record. Only59 a frame-verified recording counts as evidence.605. **Cleanup:** delete the demo entities you seeded (through the same real61 surface you created them with), stop any dev server you started, and confirm62 the port is closed.636. **Delivery:** provide the local recording and frames. Attach them to a PR or64 tracker only when existing authorization explicitly covers that external write.65 When authorized, use the host's supported attachment mechanism; do not turn66 local verification into an implicit publication step.6768## Scenario example6970```js71import { recordUiDemo } from "./harness.mjs";7273const BASE = "http://localhost:3000";7475await recordUiDemo(76 {77 name: "my-feature-demo",78 baseUrl: BASE,79 prewarm: ["/items", "/items/42"],80 },81 async ({ page, scene, highlight }) => {82 await scene("list shows the new item", async () => {83 await page.goto(`${BASE}/items`, { waitUntil: "networkidle" });84 await page.getByText("Quarterly report").first().click();85 });86 await scene("detail dialog renders the new action", async () => {87 await page.getByRole("button", { name: "Share" }).click();88 await highlight(page.getByRole("dialog").getByRole("link", { name: "Copy link" }));89 });90 },91);92```9394## Notes9596- The harness leaves overlays visible by default. After inspecting and preserving97 unfiltered failure evidence, an optional presentation recording may set98 `hideNextDevOverlay: true` or explicit `hideSelectors`. Those frames alone do99 not establish that no runtime error occurred.100- Realistic demo data reads better than test slugs: name seeded entities like101 a user would ("Sprint review"), not "TEST-1234 probe".102- Viewport defaults to 1280×720; override via `viewport` if the surface needs it.103- mp4 conversion needs `ffmpeg` on PATH; without it the webm is still produced.104- The app under test must be the working tree you changed: the dev server105 compiles from source, so a stale server proves old code; restart when in doubt.