> ## Documentation Index
> Fetch the complete documentation index at: https://docs.surfsky.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Screencast

> Watch a running browser in the built-in viewer or receive image frames over WebSocket.

Screencast streams a page's screen over WebSocket. It is view-only. Use [DevTools](/debugging) when you need to inspect elements or interact with the browser.

<span id="getting-started" />

## Get the stream URL

Starting a session returns `inspector.screencast` alongside `ws_url` and `internal_uuid`. Use that value directly; it includes the page target selected for the stream.

A stream URL looks like this:

```text theme={null}
wss://YOUR_API_HOST/screencast/INTERNAL_UUID/devtools/page/PAGE_ID
```

The session and target page must remain open. Closing the viewer does not stop the browser, and an open viewer does not keep it running.

After the connection opens, the server sends `{"type": "started"}`, then a `frame` message for each captured frame, and `{"type": "stopped"}` when the browser side ends. The client does not need to send anything.

## Quick view

Open the built-in viewer at your API base URL with the stream URL in its `ws` query parameter. This browser JavaScript constructs the URL from the start response:

```javascript theme={null}
// baseUrl is your API base URL; session is the parsed start response.
const viewer = new URL("/screencast", baseUrl);
viewer.searchParams.set("ws", session.inspector.screencast);
console.log(viewer.href);
```

Copy the resulting URL into your browser. `URLSearchParams` handles encoding the nested WebSocket URL.

Keep the viewer URL private: anyone who can use it can see the live page.

<span id="examples" />

## Embed a stream

This HTML displays frames at their natural dimensions. Replace `SCREENCAST_URL_FROM_START_RESPONSE` with the returned URL before opening the file:

```html theme={null}
<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>Surfsky screencast</title></head>
<body>
  <p id="status" role="status">Connecting…</p>
  <canvas id="screen" aria-label="Live browser view" style="max-width:100%;height:auto"></canvas>
  <script>
    const canvas = document.getElementById("screen");
    const context = canvas.getContext("2d");
    const status = document.getElementById("status");
    const ws = new WebSocket("SCREENCAST_URL_FROM_START_RESPONSE");
    ws.onopen = () => { status.textContent = "Connected"; };
    ws.onclose = () => { status.textContent = "Disconnected"; };
    ws.onerror = () => { status.textContent = "Connection failed"; };
    ws.onmessage = (event) => {
      const frame = JSON.parse(event.data);
      if (frame.type === "stopped") { status.textContent = "Stream ended"; return; }
      if (frame.type !== "frame") return;
      const img = new Image();
      img.onload = () => {
        canvas.width = img.naturalWidth;
        canvas.height = img.naturalHeight;
        context.drawImage(img, 0, 0);
      };
      img.src = "data:image/jpeg;base64," + frame.data;
    };
    window.addEventListener("pagehide", () => ws.close());
  </script>
</body>
</html>
```

Each `frame` message contains a Base64 JPEG in `data` and the CDP screencast frame `metadata`, such as the device dimensions and timestamp. Streaming provides live frames; this example does not store a recording.

## If the stream stops

Watching a screencast is not activity. The browser still stops after `browser_settings.inactive_kill_timeout` seconds without CDP, ChromeDriver, or scraping traffic, 30 seconds by default, and the stream ends with a `stopped` message. Check that the session appears in `GET /profiles/active` before reconnecting. See [the browser stopped by itself](/troubleshooting#the-browser-stopped-by-itself).

The stream follows one page. `inspector.screencast` points at the first page open at start; if your automation opened a new tab, the stream keeps showing the original one. Open `inspector.list` to get the current pages with their `page_id`, then build the stream URL for the page you want:

```text theme={null}
wss://YOUR_API_HOST/screencast/INTERNAL_UUID/devtools/page/PAGE_ID
```
