← Back to list
🛠️

playwright-recording Checked

Record browser interactions as video using Playwright. Use for capturing demo videos, app walkthr…
🛠️ Developer Tools skills By calesthio Version v1.0.0 Added 2026-09-07 Updated 2026-09-07 Source ↗
7.8Overall rating
2Views
5Downloads
56.5kStars

Installation

🤖 Install via AI

Copy the prompt below and send it to your AI assistant (e.g. Claude Code) — it will follow the instructions and install automatically.

Install the "playwright-recording" skill by following the instructions at https://skill123.me/install/playwright-recording.
⌨️ Command line install

Run in your terminal — downloads and installs to ~/.claude/skills/.

curl -fsSL https://skill123.me/install/playwright-recording.sh | bash
📦 Download ZIP

Download the zip and extract it into your skills directory (e.g. ~/.claude/skills/), then restart your session.

⬇ Download v1.0.0 · 7 KB
📊 Static rating
7.8/10
Trigger
7.0
Structure
7.3
Workflow
6.8
Content
9.0
Engineering
8.0
Security
9.5

Six-dimension rubric with reviewer rationale.

View scorecard ↓
🛡 Dynamic test
—

Not live-tested yet — this skill is rated on static analysis only.

About this skill

Record browser interactions as video using Playwright. Use for capturing demo videos, app walkthroughs, and UI flows for Remotion videos. Triggers include recording a demo, capturing browser video, screen recording a website, or creating walkthrough footage.

Playwright Video Recording

Playwright can record browser interactions as video - perfect for demo footage in Remotion compositions.

Documentation

Record browser interactions as video using Playwright. Use for capturing demo videos, app walkthroughs, and UI flows for Remotion videos. Triggers include recording a demo, capturing browser video, screen recording a website, or creating walkthrough footage.

Playwright Video Recording

Playwright can record browser interactions as video - perfect for demo footage in Remotion compositions.

Quick Start

### Installation


# In your video project
npm init -y
npm install -D playwright @playwright/test
npx playwright install chromium

### Basic Recording Script


// scripts/record-demo.ts
import { chromium } from 'playwright';

async function recordDemo() {
  const browser = await chromium.launch();
  const context = await browser.newContext({
    viewport: { width: 1920, height: 1080 },
    recordVideo: {
      dir: './recordings',
      size: { width: 1920, height: 1080 }
    }
  });

  const page = await context.newPage();

  // Your recording actions
  await page.goto('https://example.com');
  await page.waitForTimeout(2000);
  await page.click('button.demo');
  await page.waitForTimeout(3000);

  // Close to save video
  await context.close();
  await browser.close();

  console.log('Recording saved to ./recordings/');
}

recordDemo();

Run with:


npx ts-node scripts/record-demo.ts
# or
npx tsx scripts/record-demo.ts

Recording Configuration

### Viewport Sizes


// Standard 1080p (recommended for Remotion)
viewport: { width: 1920, height: 1080 }

// 720p (smaller files)
viewport: { width: 1280, height: 720 }

// Square (social media)
viewport: { width: 1080, height: 1080 }

// Mobile
viewport: { width: 390, height: 844 } // iPhone 14

### Video Quality Settings


const context = await browser.newContext({
  viewport: { width: 1920, height: 1080 },
  recordVideo: {
    dir: './recordings',
    size: { width: 1920, height: 1080 } // Match viewport for crisp output
  },
  // Slow down for visibility
  // Note: slowMo is on browser launch, not context
});

// For slow motion, launch browser with slowMo
const browser = await chromium.launch({
  slowMo: 100 // 100ms delay between actions
});

Recording Patterns

### Form Submission Demo


import { chromium } from 'playwright';

async function recordFormDemo() {
  const browser = await chromium.launch({ slowMo: 50 });
  const context = await browser.newContext({
    viewport: { width: 1920, height: 1080 },
    recordVideo: { dir: './recordings', size: { width: 1920, height: 1080 } }
  });
  const page = await context.newPage();

  await page.goto('https://myapp.com/form');
  await page.waitForTimeout(1000);

  // Type with realistic speed
  await page.fill('#name', 'John Smith', { timeout: 5000 });
  await page.waitForTimeout(500);

  await page.fill('#email', 'john@example.com');
  await page.waitForTimeout(500);

  // Click submit
  await page.click('button[type="submit"]');
View full documentation

// Wait for result

await page.waitForSelector('.success-message');

await page.waitForTimeout(2000);

await context.close();

await browser.close();

}



### Multi-Page Navigation

async function recordNavDemo() {

const browser = await chromium.launch({ slowMo: 100 });

const context = await browser.newContext({

viewport: { width: 1920, height: 1080 },

recordVideo: { dir: './recordings', size: { width: 1920, height: 1080 } }

});

const page = await context.newPage();

// Page 1

await page.goto('https://myapp.com');

await page.waitForTimeout(2000);

// Navigate to page 2

await page.click('nav a[href="/features"]');

await page.waitForLoadState('networkidle');

await page.waitForTimeout(2000);

// Navigate to page 3

await page.click('nav a[href="/pricing"]');

await page.waitForLoadState('networkidle');

await page.waitForTimeout(2000);

await context.close();

await browser.close();

}



### Scroll Demo

async function recordScrollDemo() {

const browser = await chromium.launch();

const context = await browser.newContext({

viewport: { width: 1920, height: 1080 },

recordVideo: { dir: './recordings', size: { width: 1920, height: 1080 } }

});

const page = await context.newPage();

await page.goto('https://myapp.com/long-page');

await page.waitForTimeout(1000);

// Smooth scroll

await page.evaluate(async () => {

const delay = (ms: number) => new Promise(r => setTimeout(r, ms));

for (let i = 0; i < 10; i++) {

window.scrollBy({ top: 200, behavior: 'smooth' });

await delay(300);

}

});

await page.waitForTimeout(1000);

await context.close();

await browser.close();

}



### Login Flow

async function recordLoginDemo() {

const browser = await chromium.launch({ slowMo: 75 });

const context = await browser.newContext({

viewport: { width: 1920, height: 1080 },

recordVideo: { dir: './recordings', size: { width: 1920, height: 1080 } }

});

const page = await context.newPage();

await page.goto('https://myapp.com/login');

await page.waitForTimeout(1000);

await page.fill('#email', 'demo@example.com');

await page.waitForTimeout(300);

await page.fill('#password', '••••••••');

await page.waitForTimeout(500);

await page.click('button[type="submit"]');

// Wait for dashboard

await page.waitForURL('**/dashboard');

await page.waitForTimeout(3000);

await context.close();

await browser.close();

}



## Cursor Highlighting

Playwright doesn't show cursor by default. Add visual indicators:

### CSS Cursor Highlight

// Inject cursor visualization

await page.addStyleTag({

content: `

* { cursor: none !important; }

.playwright-cursor {

position: fixed;

width: 24px;

height: 24px;

background: rgba(255, 100, 100, 0.5);

border: 2px solid rgba(255, 50, 50, 0.8);

border-radius: 50%;

pointer-events: none;

z-index: 999999;

transform: translate(-50%, -50%);

transition: transform 0.1s ease;

}

.playwright-cursor.clicking {

transform: translate(-50%, -50%) scale(0.8);

background: rgba(255, 50, 50, 0.8);

}

`

});

// Add cursor element

await page.evaluate(() => {

const cursor = document.createElement('div');

cursor.className = 'playwright-cursor';

document.body.appendChild(cursor);

document.addEventListener('mousemove', (e) => {

cursor.style.left = e.clientX + 'px';

cursor.style.top = e.clientY + 'px';

});

document.addEventListener('mousedown', () => cursor.classList.add('clicking'));

document.addEventListener('mouseup', () => cursor.classList.remove('clicking'));

});



### Click Ripple Effect

// Add click ripple visualization

await page.addStyleTag({

content: `

.click-ripple {

position: fixed;

width: 40px;

height: 40px;

border-radius: 50%;

background: rgba(234, 88, 12, 0.4);

pointer-events: none;

z-index: 999998;

transform: translate(-50%, -50%) scale(0);

animation: ripple 0.4s ease-out forwards;

}

@keyframes ripple {

to {

transform: translate(-50%, -50%) scale(2);

opacity: 0;

}

}

`

});

// Custom click function with ripple

async function clickWithRipple(page, selector) {

const element = await page.locator(selector);

const box = await element.boundingBox();

await page.evaluate(({ x, y }) => {

const ripple = document.createElement('div');

ripple.className = 'click-ripple';

ripple.style.left = x + 'px';

ripple.style.top = y + 'px';

document.body.appendChild(ripple);

setTimeout(() => ripple.remove(), 400);

}, { x: box.x + box.width / 2, y: box.y + box.height / 2 });

await element.click();

}



## Output for Remotion

### Move Recording to public/demos/

import { chromium } from 'playwright';

import * as fs from 'fs';

import * as path from 'path';

async function recordForRemotion(outputName: string) {

const browser = await chromium.launch({ slowMo: 50 });

const context = await browser.newContext({

viewport: { width: 1920, height: 1080 },

recordVideo: { dir: './temp-recordings', size: { width: 1920, height: 1080 } }

});

const page =

Score breakdown

Trigger
7.0
What/When plus explicit trigger phrases; no When-Not boundary
Structure
7.3
469-line SKILL.md borderline long and the shipped reference.md is orphaned, never referenced
Workflow
6.8
Good patterns with navigation-error guards and test-before-capture tip, but no explicit workflow or output verification
Content
9.0
Complete runnable TypeScript snippets with real selectors and parameters
Engineering
8.0
Valid frontmatter; reference.md exists but its purpose is undiscoverable from SKILL.md
Security
9.5
Local browser automation only, example.com targets, cookie dismissal is legitimate UI cleanup
Practical Playwright video-recording guide with strong trigger phrasing, cursor/ripple injection patterns, cookie-banner dismissal, and navigation-error handling, but the 342-line reference.md is never linked from SKILL.md and there is no stepwise workflow.