# How to Use the Screencast API for Real-Time Browser Observation in Ego Lite

> Learn how to use the screencast API in Ego Lite for real-time browser observation. Capture WebM video of browser viewports using the Chrome DevTools Protocol.

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

---

**The Ego Lite browser harness exposes a Playwright-style screencast API through the `page` façade, enabling real-time WebM video capture of browser viewports via Chrome DevTools Protocol (CDP).**

The **citrolabs/ego-lite** repository provides a lightweight browser automation framework designed for agent-based interactions. Using the **screencast API for real-time browser observation**, developers can record viewport activity as VP8-encoded videos, creating visual audit trails for debugging complex automation flows or documenting browser behavior.

## Starting a Screencast Session

The entry point for recording is `page.screencast.start()`, exposed through [`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). This method accepts a configuration object with strict validation rules:

- **path** (string): Output file path. Must end with `.webm` or the validator throws an error immediately.
- **size** (optional): Object with `width` and `height` properties. Both dimensions must be even integers ≥ 2. If omitted, the implementation derives dimensions from the current viewport (scaled to a maximum of 800 px).
- **quality** (optional): JPEG compression level from 0–100. Defaults to 90.

Internally, the method calls `Page.startScreencast` over the CDP session and subscribes to `Page.screencastFrame` events. Each incoming frame is written to a `VideoRecorder` instance that produces silent VP8 WebM output. The implementation acknowledges every frame with `Page.screencastFrameAck` to prevent browser buffer overflow.

The method returns a disposable control object containing:
- `stop()`: Explicitly ends the recording session
- `dispose()`: Alias for cleanup that finalizes the video
- `[Symbol.asyncDispose]`: Enables automatic resource management with `await using` syntax

## Stopping and Finalizing Capture

To terminate recording, invoke either `page.screencast.stop()` or the disposable's `dispose()` method. The shutdown sequence defined in [`src/driver/screencast.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/screencast.ts) performs the following operations:

1. Unsubscribes from `Page.screencastFrame` events to halt incoming data
2. Awaits pending frame writes to complete flushing to disk
3. Validates that at least one frame exists, falling back to a screenshot if no frames arrived (ensuring valid video output)
4. Calls `Page.stopScreencast` on the CDP session
5. Finalizes the `VideoRecorder` and propagates any capture errors

This guarantees that the resulting WebM file is playable even if the target page was static or the script terminated unexpectedly.

## Implementation Architecture

According to the source code in `citrolabs/ego-lite`, the screencast functionality spans two critical files:

**[`src/driver/screencast.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/screencast.ts)** contains the core implementation managing CDP session lifecycle, frame processing, and the `VideoRecorder` integration. It handles the low-level `Page.startScreencast` and `Page.stopScreencast` commands while managing frame acknowledgments.

**[`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts)** exposes the public façade, attaching `page.screencast.start` and `page.screencast.stop` to the page object available to agent scripts. This layer handles parameter validation and constructs the disposable return object.

The **VideoRecorder** (instantiated within the screencast module) acts as a low-level wrapper that aggregates VP8 frames into a WebM container without audio tracks, ensuring minimal file size for automated observation tasks.

## Code Examples

Basic one-shot recording with explicit stop:

```javascript
await page.goto('https://example.com');
const screencast = await page.screencast.start({
  path: 'example.webm',              // Required: must end with .webm
  size: { width: 640, height: 480 }, // Optional: even integers ≥ 2
  quality: 80                        // Optional: 0-100, default 90
});

await page.click('a.more-info');
await page.waitForLoadState('networkidle');

await screencast.stop();

```

Using the disposable pattern for automatic cleanup:

```javascript
const rec = await page.screencast.start({ path: 'session.webm' });
try {
  await page.goto('https://news.ycombinator.com');
  await page.waitForTimeout(3000);
} finally {
  await rec.dispose(); // Ensures finalization even if errors occur
}

```

Capturing complex navigation flows:

```javascript
const rec = await page.screencast.start({ path: 'navigation.webm' });
await page.goto('https://github.com');
await page.getByRole('search').fill('ego-lite');
await page.getByRole('search').press('Enter');
await page.waitForLoadState('load');
await page.screencast.stop();

```

## Summary

- The **screencast API** in Ego Lite provides real-time browser observation through a Playwright-compatible interface defined in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts).
- Recording is initiated via `page.screencast.start()`, which validates that the output path ends with `.webm` and that optional dimensions are even integers ≥ 2.
- The implementation in [`src/driver/screencast.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/screencast.ts) manages CDP frame subscription, acknowledgment, and VP8 encoding through a `VideoRecorder`.
- Cleanup supports both explicit `stop()` calls and the disposable pattern, with automatic fallback to screenshot capture if no frames are received.

## Frequently Asked Questions

### What file format does the screencast API use?

The API exclusively generates **WebM files** encoded with VP8 video. The `path` parameter must end with `.webm`; otherwise, the validation logic in [`src/driver/screencast.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/screencast.ts) rejects the request before initializing the CDP session. The output contains no audio track.

### Can I specify custom video dimensions?

Yes. Pass a `size` object with `width` and `height` properties to `page.screencast.start()`. Both values must be even integers greater than or equal to 2. If omitted, the implementation automatically scales the current viewport dimensions to a maximum width or height of 800 px while maintaining the aspect ratio.

### What is the difference between `stop()` and `dispose()`?

Both methods terminate the recording, but `dispose()` implements the `[Symbol.asyncDispose]` interface, enabling use with resource management syntax like `await using` for automatic cleanup when scopes exit. The `stop()` method provides explicit imperative control and is functionally equivalent when called directly.

### What happens if the page does not change during recording?

If no screencast frames are received from the browser during the session, the implementation in [`src/driver/screencast.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/screencast.ts) automatically captures a single screenshot before finalizing the WebM file. This ensures the output video always contains at least one valid frame and remains playable in standard media players.