How to Use the Screencast API for Real-Time Browser Observation in Ego Lite
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 and implemented in src/driver/screencast.ts. This method accepts a configuration object with strict validation rules:
- path (string): Output file path. Must end with
.webmor the validator throws an error immediately. - size (optional): Object with
widthandheightproperties. 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 sessiondispose(): Alias for cleanup that finalizes the video[Symbol.asyncDispose]: Enables automatic resource management withawait usingsyntax
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 performs the following operations:
- Unsubscribes from
Page.screencastFrameevents to halt incoming data - Awaits pending frame writes to complete flushing to disk
- Validates that at least one frame exists, falling back to a screenshot if no frames arrived (ensuring valid video output)
- Calls
Page.stopScreencaston the CDP session - Finalizes the
VideoRecorderand 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 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 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:
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:
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:
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. - Recording is initiated via
page.screencast.start(), which validates that the output path ends with.webmand that optional dimensions are even integers ≥ 2. - The implementation in
src/driver/screencast.tsmanages CDP frame subscription, acknowledgment, and VP8 encoding through aVideoRecorder. - 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 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 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.
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 →