Implementation:Puppeteer Puppeteer ScreenRecorder
| Property | Value |
|---|---|
| sources | packages/puppeteer-core/src/node/ScreenRecorder.ts |
| domains | Node, Video Recording, Screencast |
| last_updated | 2026-02-12 00:00 GMT |
Overview
Description
The ScreenRecorder class provides video recording capabilities for Puppeteer pages by leveraging the CDP Page.screencastFrame event and ffmpeg as an external encoding process. It extends Node.js PassThrough stream, allowing recorded video data to be piped to files or other writable streams.
The recording pipeline works as follows:
- CDP screencast frames are received as base64-encoded PNG images with timestamps.
- Frames are acknowledged back to the browser via Page.screencastFrameAck to maintain the frame flow.
- The RxJS pipeline buffers consecutive frame pairs, computes the number of duplicate frames needed based on timestamp deltas and the target FPS, and writes the appropriate number of copies of each frame.
- Frames are piped to ffmpeg via stdin as PNG images (
image2pipeinput format). - ffmpeg encodes the frames into the target format (webm, mp4, or gif) and outputs to stdout, which is piped through the PassThrough stream.
The class supports configurable options including video format, frame rate, playback speed, cropping, scaling, quality (CRF), GIF-specific settings (loop count, delay, color palette size), and an optional output file path. The stop method gracefully terminates recording by flushing remaining frames, closing ffmpeg's stdin, and waiting for the process to exit.
Frame writes are serialized using the @guarded() decorator to prevent concurrent writes to ffmpeg's stdin.
Usage
ScreenRecorder is instantiated internally by Page.screencast() and is returned to the user as a readable stream. Users typically pipe it to a file or call stop() when recording is complete.
Code Reference
Source Location
packages/puppeteer-core/src/node/ScreenRecorder.ts
Signature
export interface ScreenRecorderOptions {
ffmpegPath?: string;
speed?: number;
crop?: BoundingBox;
format?: VideoFormat;
fps?: number;
loop?: number;
delay?: number;
quality?: number;
colors?: number;
scale?: number;
path?: `${string}.${VideoFormat}`;
overwrite?: boolean;
}
export class ScreenRecorder extends PassThrough {
constructor(
page: Page,
width: number,
height: number,
options?: ScreenRecorderOptions,
);
async stop(): Promise<void>;
async [asyncDisposeSymbol](): Promise<void>;
}
Import
import {ScreenRecorder} from '../node/ScreenRecorder.js';
I/O Contract
| Parameter | Type | Default | Description |
|---|---|---|---|
| page | Page |
-- | The Puppeteer page to record |
| width | number |
-- | Width of the recording viewport in pixels |
| height | number |
-- | Height of the recording viewport in pixels |
| options.ffmpegPath | string |
'ffmpeg' |
Path to the ffmpeg binary |
| options.format | VideoFormat |
'webm' |
Output format: 'webm', 'mp4', or 'gif' |
| options.fps | number |
30 |
Target frames per second |
| options.speed | number |
-- | Playback speed multiplier |
| options.crop | BoundingBox |
-- | Crop region {x, y, width, height} |
| options.scale | number |
-- | Scale factor (e.g., 0.5 for half size) |
| options.quality | number |
30 |
CRF quality value (lower is better) |
| options.path | string |
-- | Output file path (triggers file output) |
| options.overwrite | boolean |
true |
Whether to overwrite existing output files |
| Output | Type | Description |
|---|---|---|
| Stream data | ReadableStream |
Encoded video data piped through the PassThrough stream |
| stop() | Promise<void> |
Stops recording and waits for ffmpeg to finish |
Usage Examples
import fs from 'node:fs';
const page = await browser.newPage();
await page.goto('https://example.com');
// Start recording
const recorder = await page.screencast({
format: 'webm',
fps: 30,
path: 'recording.webm',
});
// Perform page interactions...
await page.click('button');
await page.waitForTimeout(3000);
// Stop recording
await recorder.stop();
// Pipe to a custom writable stream
const recorder = await page.screencast({ format: 'mp4' });
const outputStream = fs.createWriteStream('output.mp4');
recorder.pipe(outputStream);
// ... perform actions ...
await recorder.stop();