From narrated-demo
Generate narrated demo videos with synchronized audio using ElevenLabs TTS and Playwright
How this skill is triggered — by the user, by Claude, or both
Slash command
/narrated-demo:skills/narrated-demoThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Generate narrated demo videos with synchronized audio using ElevenLabs TTS and Playwright browser automation.
Generate narrated demo videos with synchronized audio using ElevenLabs TTS and Playwright browser automation.
Use this skill when you need to:
Before using this skill, ensure:
export ELEVENLABS_API_KEY="your-key"# macOS
brew install ffmpeg
# Ubuntu/Debian
sudo apt install ffmpeg
The demos-not-memos library must be installed from GitHub:
# Clone the repository
git clone https://github.com/markng/demos-not-memos.git
cd demos-not-memos
# Install dependencies
npm install
# (Optional) Install Playwright browsers if not already installed
npx playwright install chromium
Node.js 18.0.0 or higher is required.
cd /path/to/demos-not-memos
# Create a demo script
cat > demos/my-demo.ts << 'EOF'
import { NarratedDemo } from '../src/demo-builder';
async function run() {
const demo = new NarratedDemo({
baseUrl: 'http://localhost:8000',
output: './output/my-demo.mp4'
});
await demo.start();
await demo.narrate("Welcome to the demo.");
await demo.page.click('#button');
await demo.narrate("The demo is complete.");
await demo.finish();
}
run().catch(console.error);
EOF
# Run the demo
npm run dev narrate --script demos/my-demo.ts
const demo = new NarratedDemo({
baseUrl: string; // Required: Base URL for the demo
output: string; // Required: Output file path (.mp4)
viewport?: { width, height }; // Default: 1280x720
voice?: string; // Default: 'Rachel'
model?: string; // Default: 'eleven_v3'
sounds?: boolean; // Default: false - enable UI sounds
});
await demo.start(); // Launch browser and start recording
demo.page // Playwright Page for browser actions
await demo.narrate(text); // Generate TTS and wait for completion
await demo.finish(); // Merge audio/video and save
demo.getElapsedTime(); // Milliseconds since start
When sounds: true, demo.page returns a SoundEnabledPage wrapper that records click and keystroke sounds automatically.
await demo.page.goto('/products');
await demo.page.click('#submit'); // Records click sound
await demo.page.type('#email', '[email protected]'); // Records keystrokes
await demo.page.fill('#password', 'secret'); // No keystroke sounds
await demo.page.locator('.card').first().click();
await demo.page.waitForSelector('.loaded');
The narrate() method returns a Narration object:
const narration = await demo.narrate("Welcome to our product.");
narration.getDuration(); // Duration in milliseconds
For "watch as I..." scenarios, use concurrent narration:
// Method 1: doWhileNarrating convenience method
await demo.doWhileNarrating(
"Watch as I fill in the form and submit",
async () => {
await demo.page.type('#email', '[email protected]');
await demo.page.click('#submit');
}
);
// Method 2: narrateAsync with whileDoing
const narration = await demo.narrateAsync("Watch as I click the button...");
await narration.whileDoing(async () => {
await demo.page.click('#button');
});
Use bracket-enclosed audio tags for expressive narration:
await demo.narrate("[excited] Check out this amazing feature!");
await demo.narrate("[whispers] Here's a secret tip.");
await demo.narrate("[curious] What happens if we click here? [laughs] Perfect!");
Supported Tags:
| Category | Tags |
|---|---|
| Emotions | [excited], [curious], [sarcastic], [mischievously] |
| Voice | [whispers], [sighs], [laughs], [crying] |
| Sounds | [applause], [gunshot], [gulps] |
| Accents | [strong French accent], [strong British accent] |
Built-in voice mappings:
You can also use any ElevenLabs voice ID directly:
const demo = new NarratedDemo({
voice: 'pNInz6obpgDQGcFmaJgB', // Voice ID
// ...
});
See ElevenLabs Voice Library for more options.
import { NarratedDemo } from '../src/demo-builder';
async function run() {
const demo = new NarratedDemo({
baseUrl: 'https://your-product.com',
voice: 'Rachel',
model: 'eleven_v3',
sounds: true,
output: './output/product-tour.mp4'
});
await demo.start();
// Homepage
await demo.narrate("[excited] Welcome to our product!");
// Navigate to features
await demo.page.click('a[href="/features"]');
await demo.page.waitForLoadState('networkidle');
await demo.narrate("[curious] Let me show you what makes us special...");
// Scroll through features
await demo.page.locator('#key-features').scrollIntoViewIfNeeded();
await demo.narrate("These features save our customers hours every week.");
// Call to action
await demo.page.click('.cta-button');
await demo.narrate("[whispers] Getting started takes just a minute.");
// Form demo
await demo.page.type('#email', '[email protected]');
await demo.narrate("[excited] Thanks for watching!");
await demo.finish();
}
run().catch(console.error);
Ensure you call await demo.start() before accessing demo.page or calling demo.narrate().
Install ffmpeg and ensure it's in your PATH:
which ffmpeg # Should output a path
echo $ELEVENLABS_API_KEYThe DSL uses real-time timing. If sync issues occur:
See the demos-not-memos README for complete API reference and examples.
npx claudepluginhub roaming-panda-llc/claude-plugins --plugin narrated-demoRecords polished UI demo videos of web applications using Playwright, following a three-phase discover-rehearse-record process. Best for creating walkthroughs, tutorials, or feature showcase videos for documentation or presentations.
Generates a ~60-80s branded video explainer for any URL with browser walkthrough, avatar lipsync, and macOS frame compositing. Handles GitHub repos (README + live-demo) and generic pages.
Turns screen recordings into polished app demo videos with AI voiceover, device frames, background music, and FFmpeg-based assembly.