How to Record Screencasts in ego-browser Using `page.screencast.start()`
The ego-browser library exposes a Playwright-style page facade that provides page.screencast.start() to capture the viewport as a silent VP8 WebM video, returning a disposable object that automatically stops recording when disposed.
ego-browser, part of the citrolabs/ego-lite repository, is a lightweight browser automation library that wraps Chrome DevTools Protocol (CDP) interactions. Unlike standalone recording tools, it integrates screencast recording directly into the page execution context, allowing you to capture user journeys as video files without external dependencies.
Understanding the Screencast API Architecture
The screencast functionality is implemented across two primary modules that bridge the CDP with a convenient JavaScript API.
Core Implementation in src/driver/screencast.ts
The low-level recording logic lives in src/driver/screencast.ts. This module exports two primary functions:
startScreencast(options): Validates the output path, creates aVideoRecorderinstance, subscribes toPage.screencastFrameevents, and sends thePage.startScreencastCDP command.stopScreencast(): Signals the end of recording viaPage.stopScreencast, fetches a final screenshot if no frames were captured, and finalizes the WebM container.
Page Facade Binding in src/helpers.ts
These core functions are exposed to script authors through the page object in src/helpers.ts:
export const helpers = {
screencast: {
start: startScreencast, // → page.screencast.start()
stop: stopScreencast, // → page.screencast.stop()
},
};
This binding mirrors Playwright's API conventions, making page.screencast.start() and page.screencast.stop() available on every page instance.
Using page.screencast.start()
The page.screencast.start() method accepts an options object and returns a disposable handle that manages the recording lifecycle.
Method Signature and Parameters
await page.screencast.start({
path: string; // Required: Output file path, must end with .webm
size?: { // Optional: Viewport dimensions
width: number; // Min 2px, rounded to even
height: number; // Min 2px, rounded to even
};
quality?: number; // Optional: JPEG compression (0-100), defaults to 90
});
Validation rules enforced by the API:
- Path requirement: The
pathargument must end with.webm. Providing a different extension throws the error:page.screencast.start path must end with .webm. - Quality constraints: Must be an integer between 0 and 100. Higher values preserve more detail but increase file size.
- Size defaults: If omitted, the viewport scales to a maximum of 800 pixels on the longest side while maintaining aspect ratio. Custom sizes must be at least 2 pixels in each dimension and are rounded to even numbers.
The Disposable Return Value
The method returns an object implementing the AsyncDisposable pattern:
{
dispose: () => Promise<void>;
[Symbol.asyncDispose]: () => Promise<void>;
}
Calling await recording.dispose() triggers stopScreencast() internally, ensuring the WebM file is properly finalized even if your script throws an exception.
Practical Code Examples
Basic Screencast Recording
Record the full viewport and save to a WebM file:
await page.goto('https://example.com');
const recording = await page.screencast.start({
path: 'output.webm'
});
// Perform actions while recording
await page.click('button.submit');
await page.waitForLoadState('networkidle');
// Stop and save
await recording.dispose();
Recording with Custom Dimensions and Quality
Capture a specific resolution with reduced quality for smaller file sizes:
await page.screencast.start({
path: 'small.webm',
size: { width: 640, height: 360 },
quality: 70
});
Using Async Disposal Patterns
Leverage explicit resource management for automatic cleanup:
await (async () => {
const recording = await page.screencast.start({ path: 'demo.webm' });
await page.goto('https://example.com');
await page.click('a.expand');
// Recording automatically stops when the block exits
})();
Key Behaviors and Constraints
Understanding the runtime characteristics prevents common errors:
- Single concurrent recording: Only one screencast can run per CDP session. Attempting to call
page.screencast.start()while another recording is active throws:Screencast is already started. - VP8 WebM output: The
VideoRecorderclass insrc/video-recorder.tsuses FFmpeg to encode frames as a silent VP8 video stream. - Frame capture: The subscription to
Page.screencastFrameevents captures JPEG screenshots at the configured quality level, which are then piped to the video encoder in real-time. - Finalization: If no frames were received during the recording session,
stopScreencast()fetches a final screenshot to ensure the video file contains at least one frame.
Summary
page.screencast.start()is exposed via the page facade insrc/helpers.tsand implemented insrc/driver/screencast.ts.- The API requires a
.webmoutput path and supports optionalsizeandqualityparameters. - It returns a disposable object that ensures
Page.stopScreencastis called and the video file is finalized when disposed. - Only one recording can be active per page instance; subsequent calls trigger an error.
- Recordings are captured through CDP's
Page.screencastFrameevents and encoded as VP8 WebM videos.
Frequently Asked Questions
What video format does ego-browser use for screencasts?
ego-browser records screencasts as VP8-encoded WebM videos without audio. The VideoRecorder class handles the containerization in src/video-recorder.ts, ensuring compatibility with modern browsers and video players.
Can I record multiple pages simultaneously?
No. The CDP session allows only one active screencast per page instance. If you need to record multiple browser contexts simultaneously, you must create separate page instances and manage their recordings independently, as each page.screencast.start() call validates that no other recording is currently active on that specific page.
Why does my script throw a path validation error?
The screencast API strictly requires the output path to end with the .webm extension. If you provide paths like output.mp4 or recording, the startScreencast function in src/driver/screencast.ts throws: page.screencast.start path must end with .webm. Always ensure your filename includes the correct extension before calling the method.
How do I ensure the recording stops if my script crashes?
The disposable object returned by page.screencast.start() implements [Symbol.asyncDispose], allowing you to use it within async blocks or explicitly call await recording.dispose(). If you're using Node.js 14+ with --experimental-abort-controller or modern async disposal patterns, the recording finalizes automatically when the scope exits, even if an unhandled exception occurs.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →