# How to Record Screencasts in ego-browser Using `page.screencast.start()`

> Learn to record ego-browser screencasts using page.screencast.start() capture viewport as a silent VP8 WebM video. Get automatic stop on dispose.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: how-to-guide
- Published: 2026-07-26

---

**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`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/screencast.ts)

The low-level recording logic lives in **[`src/driver/screencast.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/screencast.ts)**. This module exports two primary functions:

- **`startScreencast(options)`**: Validates the output path, creates a `VideoRecorder` instance, subscribes to `Page.screencastFrame` events, and sends the `Page.startScreencast` CDP command.
- **`stopScreencast()`**: Signals the end of recording via `Page.stopScreencast`, fetches a final screenshot if no frames were captured, and finalizes the WebM container.

### Page Facade Binding in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts)

These core functions are exposed to script authors through the page object in **[`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts)**:

```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

```typescript
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 `path` argument 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:

```typescript
{
  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:

```javascript
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:

```javascript
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:

```javascript
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 `VideoRecorder` class in [`src/video-recorder.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/video-recorder.ts) uses FFmpeg to encode frames as a silent VP8 video stream.
- **Frame capture**: The subscription to `Page.screencastFrame` events 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 in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) and implemented in [`src/driver/screencast.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/screencast.ts).
- The API requires a `.webm` output path and supports optional `size` and `quality` parameters.
- It returns a disposable object that ensures `Page.stopScreencast` is 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.screencastFrame` events 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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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.