# How the Snapshot Mechanism Works in ego-browser: Configuration and Implementation

> Discover how ego-browser's snapshot mechanism captures semantic DOM views and maintains numeric references. Learn about configuration options like maxResultLength and includeRefs.

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

---

**The ego-browser snapshot mechanism captures a semantic DOM view via the ego bridge, processes it through `observe.snapshotRaw` in [`src/driver/observe.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/observe.ts), and maintains numeric reference mappings (`@N`) through automatic ref-map synchronization, with configurable options like `maxResultLength` and `includeRefs` to control payload size and content.**

The **snapshot mechanism in ego-browser** serves as the primary interface for delivering structured, semantic representations of web pages to automation agents. As implemented in the **citrolabs/ego-lite** repository, this architecture bridges the JavaScript runtime with a closed-source **ego-lite** binary to capture DOM state, compute stable locators, and manage ephemeral numeric references. Understanding how `snapshotRaw` processes requests and which configuration options tune the output is essential for building efficient browser automation scripts.

## Core Components of the Snapshot Architecture

The snapshot workflow relies on three tightly-coupled components that manage data capture, public API exposure, and reference state maintenance.

### The Observation Layer ([`src/driver/observe.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/observe.ts))

At the lowest level, `observe.snapshotRaw` calls the underlying ego bridge via `browserEgo().snapshot(options)`. This method returns the low-level snapshot object containing `content`, `refs`, and metadata. The bridge communicates with the CDP-style **ego-lite** binary that captures the complete DOM structure and assigns stable numeric identifiers to nodes.

### Public Helper Interface ([`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts))

The `helpers.snapshot` and `helpers.snapshotRaw` methods expose snapshot functionality to agent scripts. While `snapshotRaw` returns the full structured payload including reference mappings, `snapshot` extracts only the human-readable text content from the `content` field that agents most commonly consume.

### Reference State Management ([`src/ref-state.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/ref-state.ts))

The `ensureRefMapForRef` function guarantees that the **ref map** (numeric `@N` identifiers) remains populated and synchronized. If a script requests a reference before any snapshot exists, this routine triggers a fresh snapshot via the callback registered with `registerSnapshotForRefRefresh`, ensuring references always point to current DOM nodes.

## How the Snapshot Execution Flow Works

When an agent requests a snapshot, the runtime executes a three-step pipeline:

1. **Invoke the Bridge** – The system awaits `browserEgo().snapshot(options)`, which communicates with the ego-lite binary to capture the current DOM state, compute stable locators, and assign numeric snapshot refs (`@1`, `@2`, etc.).

2. **Refresh the Ref Map** – The raw snapshot passes to `registerSnapshotForRefRefresh`, which stores a re-executable callback. This mechanism keeps the reference map synchronized whenever the page navigates or mutates.

3. **Return Processed Results** – `snapshotRaw` forwards the complete object, while the `snapshot` helper extracts the `content` field for text-only consumption.

## Configuration Options for ego-browser Snapshots

The `observe.snapshotRaw` method accepts an optional **`SnapshotOptions`** argument that passes directly to the ego bridge. These controls determine payload size, content inclusion, and performance characteristics.

### Size and Performance Controls

- **`maxResultLength`** (`number`, default: `Infinity`): Truncates the returned text content to a specified character count, preventing oversized payloads on large pages.
- **`maxBytes`** (`number`, default: `Infinity`): Caps the total byte size of the raw snapshot, including the refs map. The bridge drops excess data rather than throwing exceptions.

### Content Inclusion Flags

- **`includeRefs`** (`boolean`, default: `true`): When set to `false`, the snapshot omits the `refs` map, producing a lighter payload when numeric references are unnecessary.
- **`includeInvisible`** (`boolean`, default: `false`): Controls whether invisible DOM nodes appear in the snapshot. Setting to `true` includes hidden elements, while the default filters to visible content only.
- **`includeScreenshot`** (`boolean`, default: `false`): Reserved for future extensions, this option requests a base64-encoded screenshot of the page (currently ignored by the bridge).

These options are documented in the public helper signatures located in [`src/format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/format.ts), which powers the runtime's `help()` command.

## Understanding Snapshot References (Ref Map)

**Ref identifiers** (`@1`, `@2`, etc.) are short-lived numeric handles that point to stable DOM nodes across multiple script execution rounds. The `RefMap` rebuilds whenever a snapshot occurs, but `ensureRefMapForRef` provides automatic refresh guarantees. If a script attempts to use a reference after navigation or DOM mutation without an intervening snapshot, the system implicitly triggers a fresh snapshot to maintain reference validity.

## Practical Implementation Examples

Basic text extraction:

```javascript
// Simple text snapshot – the most common use case
const txt = await page.snapshot();               // → string with page text
console.log(txt);

```

Structured snapshot with custom limits:

```javascript
// Structured snapshot with custom limits
const raw = await page.snapshotRaw({
  maxResultLength: 2000,    // return only the first 2 k characters
  maxBytes: 50000,          // abort if the raw object would exceed 50 kB
  includeRefs: true,        // keep numeric @N refs for later reuse
});
console.log(raw.content);    // human-readable text
console.log(raw.refs);       // map of @N → element handles

```

Reference refresh after navigation:

```javascript
// Refreshing a ref after navigation
await page.navigate('https://example.com');
const link = await page.$('a.some-link');   // uses a locator, not a ref
await link.click();                         // triggers navigation
// The next line implicitly forces a new snapshot because we will request a ref
await page.$('@12');                         // numeric ref – ensures fresh snapshot

```

Visibility filtering:

```javascript
// Limiting to visible elements only (default)
const visibleSnap = await page.snapshotRaw({
  includeInvisible: false,
});

```

## Summary

- The **snapshot mechanism in ego-browser** relies on `observe.snapshotRaw` in [`src/driver/observe.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/driver/observe.ts) to interface with the ego-lite binary via `browserEgo().snapshot()`.
- **Configuration options** include `maxResultLength`, `maxBytes`, `includeRefs`, `includeInvisible`, and `includeScreenshot` to control payload size and content.
- The **ref map** system in [`src/ref-state.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/ref-state.ts) automatically maintains numeric `@N` identifiers through `ensureRefMapForRef` and `registerSnapshotForRefRefresh`.
- Public helpers in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) provide both full structured data (`snapshotRaw`) and text-only extraction (`snapshot`).
- References remain stable across navigation because the system implicitly refreshes snapshots when stale refs are accessed.

## Frequently Asked Questions

### What is the difference between `page.snapshot()` and `page.snapshotRaw()`?

The `page.snapshot()` helper returns a string containing only the human-readable text content of the page, while `page.snapshotRaw()` returns the complete structured object including the `content` field, `refs` map, and metadata. According to the implementation in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts), `snapshot` extracts the text field from the raw result that `snapshotRaw` provides.

### How does ego-browser handle stale references after page navigation?

The system uses `ensureRefMapForRef` in [`src/ref-state.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/ref-state.ts) to guarantee reference validity. When a script accesses a numeric ref (`@N`) after navigation or DOM mutation, this function automatically triggers a fresh snapshot through the callback registered with `registerSnapshotForRefRefresh`, rebuilding the ref map to point to current DOM nodes.

### Can I exclude invisible DOM elements from the snapshot?

Yes. Set the `includeInvisible` option to `false` (the default) when calling `snapshotRaw`. This filters out hidden elements and returns only visible DOM nodes, keeping the snapshot focused on content that users actually see. Pass `true` only when automation scripts require access to hidden elements.

### Where are the snapshot configuration options documented in the source code?

The `SnapshotOptions` interface and helper signatures are documented in [`src/format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/format.ts), which powers the runtime's `help()` command. The options—`maxResultLength`, `maxBytes`, `includeRefs`, `includeInvisible`, and `includeScreenshot`—are passed directly from `observe.snapshotRaw` to the underlying ego bridge.