Skip to content
Public template

About

Human-paced Playwright demo harness for polished product walkthroughs

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

Playwright Demo Kit

Human-paced Playwright harness for polished product demos.

What this is for

Use this when normal product screens exist, but demo experience still feels rough.

This kit turns Playwright from test runner into repeatable demo runner:

  • opens app in controlled mobile viewport
  • moves through flow at human pace
  • shows clean on-screen cursor
  • adds short captions for story beats
  • records browser video automatically
  • renders higher-quality mp4 for sharing

Good fit for:

  • founder demos
  • investor walkthroughs
  • sales enablement clips
  • product teasers
  • internal launch videos
  • repeatable QA demos across multiple apps

Why use it

Raw screen recordings usually break down in same ways:

  • mouse moves too fast
  • important moments pass without context
  • clicks feel chaotic
  • narration becomes required to explain basics
  • every retake is manual and inconsistent

This kit fixes that by making demo itself scripted, repeatable, and product-aware.

Main motivation:

  • tell one clear story per flow
  • keep pacing deliberate
  • remove presenter shakiness
  • regenerate same demo after UI changes
  • reuse same framework across many apps

What's included

  • mobile-first demo config
  • slow motion capture
  • fake on-screen cursor
  • cinematic caption overlays
  • HQ .webm → .mp4 render script
  • starter demo spec template

Install

npm install
npx playwright install chromium

Adopt in existing app

  1. copy playwright.demo.config.ts
  2. copy tests/demo/helpers.ts
  3. copy scripts/render-demo-video.mjs
  4. copy scripts/with-recording-lock.py and add package scripts from this repo
  5. set PLAYWRIGHT_BASE_URL
  6. optionally set PLAYWRIGHT_DEMO_WEB_SERVER_COMMAND
  7. replace tests/demo/example.demo.spec.ts with app flow
  8. run DEMO_SLUG=check npm run demo:test -- --list

Wire app

Set one or both env vars:

export PLAYWRIGHT_BASE_URL="http://127.0.0.1:3000"
export PLAYWRIGHT_DEMO_WEB_SERVER_COMMAND="npm run dev -- --hostname 127.0.0.1 --port 3000"

If PLAYWRIGHT_DEMO_WEB_SERVER_COMMAND set, Playwright starts app for demo run.

Run

Set DEMO_SLUG to the work being recorded (for example, eng-2637):

DEMO_SLUG=eng-2637 npm run demo:flow

For separate ad-hoc full-suite test and render steps, run DEMO_SLUG=eng-2637 npm run demo:test followed by npm run demo:render. These two steps are not protected by one lock; use the single-feature command below for a shareable feature recording. The recording lock covers the entire demo:flow test and render. Another recording fails fast with the current slug and start time. The OS releases the lock after the recorder exits, even if its wrapper crashes, so there is no stale timeout. Keep the wrapper alive until capture completes; never delete the lock file (~/Library/Caches/playwright-demo-kit/recording.lock). This coordinates runs on the same machine, not recordings on other machines or commands that bypass the wrapper.

For a single feature when multiple demo specs exist, select its spec and keep capture and rendering under one lock:

DEMO_SLUG=eng-2637 python3 scripts/with-recording-lock.py -- sh -c 'npx playwright test -c playwright.demo.config.ts tests/demo/<feature>.demo.spec.ts && npm run demo:render'

Replace <feature> with the actual spec name. npm run demo:flow runs every demo spec and renders the newest video. Do not use it for a single-feature recording unless that is the only selected spec. Confirm that both the frontend URL and its backend are safe for demo actions; do not record credentials or secrets.

Files to copy into another app

  • playwright.demo.config.ts
  • tests/demo/helpers.ts
  • scripts/render-demo-video.mjs
  • scripts/with-recording-lock.py

Then replace tests/demo/example.demo.spec.ts with app-specific walkthrough.

Verification

npm run typecheck
DEMO_SLUG=check npm run demo:test -- --list

GitHub Actions runs same checks on push and pull request.

Viewport (desktop default)

Default capture is a maximized desktop window (null viewport, 1440x900 video), suited to dashboards and wide apps. For a tall phone frame instead, set:

export DEMO_DEVICE=mobile    # 430x932 frame, Pixel 7 UA, touch
export DEMO_SLOWMO=120       # optional, ms of slow motion

Hiding dev-server error overlays

Create React App / webpack-dev-server / Vite show a full-screen error overlay (e.g. "Uncaught runtime errors") that will dominate a demo. Kill it before the app loads:

import { dismissDevOverlays } from "./helpers";
await dismissDevOverlays(page); // call before page.goto

It uses !important CSS (to beat the overlay's inline display:block) plus a short interval (because webpack-dev-server re-shows the overlay via a style flip that a childList MutationObserver misses). If you control the dev server, also consider disabling it at the source (devServer.client.overlay = false).

Skipping login (auth injection)

Seed localStorage before load so the demo starts authenticated. Read secrets from env, never hardcode them:

import { injectLocalStorage } from "./helpers";
await injectLocalStorage(page, {
  TOKEN: process.env.DEMO_TOKEN ?? "",
  CURRENT_LOCATION: process.env.DEMO_LOCATION ?? "",
});

Safety gate

Stop a demo from firing against the wrong environment:

import { isSafeDemoTarget } from "./helpers";
test.beforeEach(() => test.skip(!isSafeDemoTarget(), "set DEMO_CONFIRM=1 or use a localhost base URL"));

Safe when DEMO_CONFIRM=1 or PLAYWRIGHT_BASE_URL is localhost. It only checks the frontend URL, not which database the backend points at, so still confirm the backend target yourself.

Caption vs locator collisions

Captions render real text into the page, so page.getByText("…") can match a caption as well as the element you meant. Prefer role/exact locators (getByRole("button", { name: "Save" }), getByText("Save", { exact: true })) in steps that run while a caption is visible.

Demo pattern

  1. open page
  2. wait for load + breathing room
  3. show caption with one idea
  4. move demo cursor to next target
  5. click or type slowly
  6. pause long enough for viewer to understand
  7. repeat until story complete

Notes

  • Default capture is a maximized desktop window; set DEMO_DEVICE=mobile for tall phone demos.
  • ffmpeg required for HQ mp4 render.
  • Template spec intentionally skipped until customized.
  • Repo intended to be used as GitHub template or copy source for other apps.

About

Human-paced Playwright demo harness for polished product walkthroughs

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages