demowright
v2.8.0
Published
Playwright video production plugin — cursor overlay, keystroke badges, TTS narration, and narration-driven video scripts for test recordings
Readme
demowright
Playwright HUD plugin — renders a visible mouse cursor, keystroke display, auto-slowdown, TTS narration, and subtitles into test video recordings, making them readable by humans and AI (e.g. Gemini video analysis).
Demos
6 runnable examples in
examples/— see Examples guide
| # | Demo | What it shows |
|---|------|---------------|
| 01 | Dashboard | Cursor, clicks, Ctrl+K search, modal form typing |
| 02 | Monaco Editor | Real VS Code editor — typing, Ctrl+S/Z/A, tab switching |
| 03 | E-commerce Checkout | Browse, add to cart, fill payment form with annotate() |
| 04 | Narrated Tour | SaaS landing page tour — heavy TTS + subtitles |
| 05 | Kanban Board | Move cards between columns, add tasks |
| 06 | Native API | Zero helpers — page.click(), page.fill() only |
npx playwright test --config examples/playwright.config.ts # run all 6Problem
Playwright's video recording doesn't capture the browser cursor or keyboard input. Tests run too fast for meaningful video review.
Solution
demowright injects a lightweight overlay into every page during test execution:
- 🖱️ Visible cursor — SVG pointer follows mouse with click ripple effects
- ⌨️ Keystroke display — keys shown as HUD badges; modifier keys (Shift/Ctrl/Alt) as persistent blue badges
- 🐢 Auto-slowdown — configurable delays after actions for human-readable recordings
- 🗣️ TTS narration — spoken annotations via pluggable providers (OpenAI, ElevenLabs, espeak, URL template)
- 💬 Subtitles — visual text overlays that fade in/out during recordings
- 🎵 Audio capture — record browser audio to WAV via Web Audio API tap
- 🔌 Non-invasive — 4 integration methods, from zero-change config to full programmatic control
Quick Start
npm install demowrightZero-change setup (recommended) ⭐
Add one line to your playwright.config.ts — no test files need to change:
// playwright.config.ts
import { defineConfig } from "@playwright/test";
import { withDemowright } from "demowright/config";
export default withDemowright(
defineConfig({
use: { video: "on" },
}),
);Your existing tests keep using import { test } from '@playwright/test' — the HUD is injected automatically.
Alternative: CLI flag (zero code changes at all)
NODE_OPTIONS="--require demowright/register" npx playwright testAlternative: Import replacement
// Change this:
import { test, expect } from "@playwright/test";
// To this:
import { test, expect } from "demowright";Alternative: Programmatic (full control)
import { test as base } from "@playwright/test";
import { applyHud } from "demowright";
const test = base.extend({
context: async ({ context }, use) => {
await applyHud(context, { cursor: true, keyboard: true, actionDelay: 150 });
await use(context);
},
});Configuration
// Via withDemowright (recommended)
export default withDemowright(defineConfig({ ... }), {
actionDelay: 200,
cursorStyle: 'dot',
});
// Via test.use (import replacement approach)
test.use({
qaHud: {
cursor: true, // show cursor overlay (default: true)
keyboard: true, // show keystroke display (default: true)
cursorStyle: 'default', // 'default' | 'dot' | 'crosshair'
keyFadeMs: 1500, // key label fade time in ms
actionDelay: 120, // delay after each action for readability (ms)
audio: false, // path to save WAV, or false to disable
tts: false, // TTS provider: URL template, function, or false
},
});
// Via env vars (CLI approach)
QA_HUD_CURSOR=0 QA_HUD_DELAY=200 NODE_OPTIONS="--require demowright/register" npx playwright test| Env Var | Description | Default |
| ------------------------- | ------------------------- | --------- |
| QA_HUD=0 | Disable HUD entirely | enabled |
| QA_HUD_CURSOR=0 | Disable cursor overlay | enabled |
| QA_HUD_KEYBOARD=0 | Disable keyboard display | enabled |
| QA_HUD_DELAY=200 | Action delay in ms | 120 |
| QA_HUD_CURSOR_STYLE=dot | Cursor style | default |
| QA_HUD_KEY_FADE=2000 | Key label fade time in ms | 1500 |
| QA_HUD_TTS=url | TTS URL template (%s) | disabled |
Helpers — Recording-Only Convenience Functions
import { clickEl, typeKeys, narrate, annotate, hudWait } from "demowright/helpers";
await annotate(page, "Welcome to the product tour"); // subtitle + TTS
await clickEl(page, "#get-started"); // animated cursor + ripple + click
await typeKeys(page, "[email protected]", 60, "#email"); // char-by-char with key badges
await hudWait(page, 500); // waits only during recording
await narrate(page, "Now let's submit the form"); // TTS narrationAll helpers are no-ops when HUD is inactive — safe to leave in production tests.
TTS Narration
Configure a TTS provider for spoken narration in recordings:
export default withDemowright(defineConfig({ ... }), {
tts: "http://localhost:5000/tts?text=%s", // URL template
audio: "test-audio.wav", // capture audio to WAV
});Or use a function for APIs requiring auth (OpenAI, ElevenLabs, etc.):
await applyHud(context, {
tts: async (text) => {
const res = await fetch("https://api.openai.com/v1/audio/speech", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ model: "tts-1", voice: "alloy", input: text }),
});
return Buffer.from(await res.arrayBuffer());
},
});How It Works
- Event listeners are injected via
context.addInitScript()— captures mouse/keyboard events before DOM exists, survives navigations - DOM overlay is injected via
page.evaluate()after each navigation (goto,reload,setContent, etc.) - The overlay uses
pointer-events: noneand max z-index — never interferes with test interactions - Page actions (
click,fill,type, etc.) are wrapped with configurable delays for video readability
Documentation
| Guide | Description |
|-------|-------------|
| Getting Started | Installation, 4 integration methods, configuration |
| Helpers API | clickEl, typeKeys, moveTo, hudWait reference |
| Narration & Subtitles | narrate(), subtitle(), annotate() |
| TTS Setup | Configuring text-to-speech providers |
| Cursor & Keyboard | Cursor styles, key badges, click ripples, auto-slowdown |
| Audio Capture | Recording browser audio to WAV |
| Examples | 6 runnable demo scenarios |
| Wrapper Strategies | Making native Playwright calls show the HUD |
License
MIT
