paulirish/chrome-capture
Media & DesignCapture Chrome tab video and audio at 60–120fps up to 2160p using Chrome's native AV1 MP4 recorder.
Project overview
chrome-capture records a Chrome tab to an AV1 .mp4 at 60–120fps and up to 2160p, including tab audio, without an external ffmpeg step. It relies on the new Page.startScreenRecording CDP command, where frames go from the compositor to an out-of-process encoder over shared memory and Chrome writes AV1 into the MP4 as recording happens Page.startScreenRecording. This differs from the older Page.startScreencast approach used by Playwright recordVideo and Puppeteer page.screencast(), which ships JPEG frames over CDP and pipes them into ffmpeg Page.startScreencast.
The tool suits developers who want to automate browser-flow recordings at high frame rate or resolution, including Playwright/Puppeteer test videos and raw CDP scripts. It is available as a CLI and a TypeScript library.
Quick start
Requirements: Chrome 153+ for the native MP4 tab recorder, and Node.js with npm for the CLI.
To record 4 seconds of a page with tab audio at the default 720p/60fps:
npx chrome-capture https://paulirish.github.io/chrome-capture/ demo.mp4 4s
Higher frame rates require a matching display and headful Chrome. For example, 1080p at 120fps on a 120Hz display:
npx chrome-capture https://paulirish.github.io/chrome-capture/ 120fps.mp4 5s --fps=120 --width=1920 --height=1080 --headful
For programmatic use with Playwright, Puppeteer, or a CDP session, startRecording() returns an async-disposable recorder and writes the MP4 when the block exits:
import {startRecording} from 'chrome-capture';
{
await using rec = await startRecording(page, {
outputPath: 'flow.mp4',
frameRate: 60,
maxWidth: 1280,
maxHeight: 720,
audio: false,
});
await page.goto('https://paulirish.github.io/chrome-capture/');
}
The package also ships a @playwright/test fixture and a raw CDP CdpScreenRecorder. See the README for full examples.
Usage constraints
- Chrome 153+ is required for
Page.startScreenRecording. - The CLI runs headless for frame rates at or below 60fps and switches to headful on macOS when
--fpsexceeds 60, so Chrome can sync to a ProMotion 120Hz display. - 120fps requires a 120Hz display and headful Chrome.
- Video quality and bitrate are not user-configurable yet: Chrome encodes with libaom AV1 in realtime 1-pass VBR and scales target bitrate with pixel rate. The documented sweet spot is 1080p60 and above; if you need lossless 1px text fidelity at roughly 10–15fps, an offline
Page.startScreencast({format: 'png'})+ ffmpeg pipeline remains an option. - On GPU-accelerated macOS, do not pass
--disable-frame-rate-limit --disable-gpu-vsync; it floods the Metal command queue and can drop 2160p@60 capture to 0.3fps. - Measured resolution/frame-rate ceilings on Windows and Linux with a discrete GPU are not documented.
Benchmarks
On an M4 Max, a 6.4s replay at 1280×800 recorded with chrome-capture at 60fps finished in 11s wall-clock versus 21s for Playwright recordVideo, with stop+finalize taking 17ms versus 3,800ms. WebSocket traffic was 450KB versus 10.5MB, and the final file was 330KB versus 480KB. PSNR was 34dB versus 32dB against a lossless screenshot. Methodology is in benchmarks/README.md.
HotRepo's take
chrome-capture appeared on HotRepo's new-project TypeScript list on 2026-11-10, gaining 7 stars in a one-hour window observed from 02:45 to 03:45 UTC, with 108 stars total. There is no daily all-language rank or HN discussion recorded. The technical premise is specific and testable: relying on Chrome's compositor-to-encoder path removes the JPEG-over-CDP and ffmpeg step, and the reported stop time of 17ms contrasts sharply with the 3.8s ffmpeg finalize. The project is small enough that the README points readers at src/index.ts to reuse the implementation directly, and the stated compatibility limits mean the highest settings are only confirmed on specific hardware.
Written by AI from the project's README, updated . The project's own docs are the source of truth.