Recording website videos — capturing page loads, animations, interactions, and scrolling as MP4 files — is increasingly useful for documentation, QA, marketing demos, and monitoring. This guide covers Playwright's built-in recording, custom ffmpeg pipelines, and SnapAPI's video endpoint that handles everything in one API call.
Playwright Built-in Video Recording
Playwright can record videos of browser sessions natively. Configure it per-context to capture everything that happens in the browser:
import { chromium } from 'playwright';
async function recordPageLoad(url, outputDir = './videos') {
const browser = await chromium.launch();
const context = await browser.newContext({
recordVideo: {
dir: outputDir,
size: { width: 1280, height: 720 },
},
viewport: { width: 1280, height: 720 },
});
const page = await context.newPage();
await page.goto(url, { waitUntil: 'networkidle' });
// Wait for animations to complete
await page.waitForTimeout(3000);
// Scroll to capture full page content
await page.evaluate(async () => {
const delay = (ms) => new Promise(r => setTimeout(r, ms));
for (let i = 0; i < document.body.scrollHeight; i += 200) {
window.scrollTo(0, i);
await delay(100);
}
});
await page.waitForTimeout(1000);
await context.close(); // Video is saved on context close
const video = page.video();
const path = await video.path();
console.log(`Video saved: ${path}`);
await browser.close();
return path;
}
await recordPageLoad('https://example.com');
Recording User Interactions
Capture specific user flows — form submissions, navigation, feature demos — by scripting the interactions:
import { chromium } from 'playwright';
async function recordUserFlow(outputDir = './videos') {
const browser = await chromium.launch();
const context = await browser.newContext({
recordVideo: { dir: outputDir, size: { width: 1280, height: 720 } },
viewport: { width: 1280, height: 720 },
});
const page = await context.newPage();
// Step 1: Visit homepage
await page.goto('https://yourapp.com');
await page.waitForTimeout(2000);
// Step 2: Click sign-up
await page.getByRole('link', { name: 'Get Started' }).click();
await page.waitForTimeout(1500);
// Step 3: Fill registration form
await page.getByLabel('Email').fill('demo@example.com');
await page.waitForTimeout(500);
await page.getByLabel('Password').fill('DemoPass123!');
await page.waitForTimeout(500);
// Step 4: Submit
await page.getByRole('button', { name: 'Create Account' }).click();
await page.waitForTimeout(3000);
// Step 5: Show dashboard
await expect(page).toHaveURL('/dashboard');
await page.waitForTimeout(2000);
await context.close();
const videoPath = await page.video().path();
console.log(`Flow recorded: ${videoPath}`);
await browser.close();
}
Custom ffmpeg Pipeline
For advanced control over frame rate, encoding, and output format, combine Playwright screenshots with ffmpeg:
import { chromium } from 'playwright';
import { execSync } from 'child_process';
import fs from 'fs';
import path from 'path';
async function recordWithFrames(url, options = {}) {
const {
duration = 5000,
fps = 30,
width = 1280,
height = 720,
output = 'recording.mp4',
} = options;
const framesDir = '/tmp/frames';
fs.mkdirSync(framesDir, { recursive: true });
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width, height } });
await page.goto(url, { waitUntil: 'networkidle' });
// Capture frames
const frameInterval = 1000 / fps;
const totalFrames = Math.ceil(duration / frameInterval);
for (let i = 0; i < totalFrames; i++) {
const framePath = path.join(framesDir, `frame-${String(i).padStart(5, '0')}.png`);
await page.screenshot({ path: framePath });
await page.waitForTimeout(frameInterval);
}
await browser.close();
// Encode with ffmpeg
execSync(
`ffmpeg -y -framerate ${fps} -i ${framesDir}/frame-%05d.png ` +
`-c:v libx264 -pix_fmt yuv420p -crf 23 ${output}`
);
// Cleanup frames
fs.rmSync(framesDir, { recursive: true });
console.log(`Video encoded: ${output}`);
return output;
}
SnapAPI Video API
SnapAPI's /v1/video endpoint records website videos without any browser or ffmpeg infrastructure. One API call captures page loads, animations, and scrolling as MP4:
import SnapAPI from 'snapapi-js';
const snap = new SnapAPI('sk_live_your_key');
// Record a page load
const result = await snap.video({
url: 'https://yourapp.com',
duration: 10, // seconds
width: 1280,
height: 720,
format: 'mp4',
scroll: true, // auto-scroll through page
block_ads: true,
});
console.log(result.url); // CDN-hosted MP4 URL
// Record with device emulation
const mobileVideo = await snap.video({
url: 'https://yourapp.com',
duration: 8,
device: 'iPhone 14 Pro',
scroll: true,
});
Use Cases
- Marketing demos. Record product walkthroughs and landing page animations for social media, pitch decks, and documentation.
- QA and testing. Capture test failures as videos to debug visual regressions and interaction bugs. Playwright's
video: 'retain-on-failure'config does this automatically. - Documentation. Generate GIFs or short videos showing how features work — embed in READMEs, wikis, and onboarding flows.
- Monitoring. Record periodic page loads to detect performance degradation, layout shifts, or broken animations in production.
- Competitive analysis. Capture competitor product flows for internal review without manual screen recording.
Approach Comparison
| Approach | Output | Control | Infrastructure | Best For |
|---|---|---|---|---|
| Playwright recordVideo | WebM | Context-level | Browser server | Test artifacts |
| Screenshots + ffmpeg | MP4/GIF/WebM | Full (frame-level) | Browser + ffmpeg | Custom encoding |
| CDP screencast | Frames (JPEG) | Low-level | Browser server | Real-time streaming |
| SnapAPI /v1/video | MP4 | API params | None (managed) | Everything — zero setup |
Record Website Videos — No Infrastructure Required
SnapAPI captures page loads, scrolling, and animations as MP4. Plus screenshots, scraping, PDFs, and AI analysis in the same API. Free tier: 200 captures/month.
Start Free — No Credit Card Required