Skip to main content
Record and replay your browser sessions to debug failures, analyze behavior, and share reproducible bug reports. Hyperbrowser supports both web recordings (using rrweb) that capture DOM changes and interactions in a lightweight format, and traditional MP4 video recordings for easy sharing. Session recording playback demo

Recording Types

Hyperbrowser supports two types of recordings:
  • Web Recording (rrweb): Captures DOM changes, interactions, and network requests in a lightweight JSON format that can be replayed with the rrweb player
  • Video Recording (MP4): Records the session as a standard video file for easy sharing and viewing

Enabling Session Recording

To record a session, set enableWebRecording to true when creating a new session. This will record all browser interactions, DOM changes, and network requests for the duration of the session.
The rrweb session recording is enabled by default, so you don’t need to explicitly set the enableWebRecording/enable_web_recording parameter to true/True.
import { Hyperbrowser } from "@hyperbrowser/sdk";

const client = new Hyperbrowser({
  apiKey: process.env.HYPERBROWSER_API_KEY,
});

const session = await client.sessions.create({
  enableWebRecording: true,
});

console.log(`Session ID: ${session.id}`);

Enabling Video Recording

To create a video screen recording (MP4 format), set both enableWebRecording and enableVideoWebRecording to true. Both must be set to true/True.
import { Hyperbrowser } from "@hyperbrowser/sdk";

const client = new Hyperbrowser({
  apiKey: process.env.HYPERBROWSER_API_KEY,
});

const session = await client.sessions.create({
  enableWebRecording: true,
  enableVideoWebRecording: true,
});

console.log(`Session ID: ${session.id}`);
enableWebRecording must be true for video recording to work.

Retrieving Recordings

To retrieve a recording, you need to:
  1. Note the session id when you create the session
  2. Ensure the session has been stopped before fetching the recording
  3. Poll the recording URL endpoint until the status is completed or failed

Get Web Recording URL

Retrieve the URL for the rrweb recording:
import { Hyperbrowser } from "@hyperbrowser/sdk";

const client = new Hyperbrowser({
  apiKey: process.env.HYPERBROWSER_API_KEY,
});

// Poll until recording is ready
let recordingData = await client.sessions.getRecordingURL("session-id");

while (recordingData.status === "pending" || recordingData.status === "in_progress") {
  console.log(`Recording status: ${recordingData.status}, waiting...`);
  await new Promise(resolve => setTimeout(resolve, 1000));
  recordingData = await client.sessions.getRecordingURL("session-id");
}

console.log(recordingData.status); // "not_enabled" | "completed" | "failed"

if (recordingData.status === "completed" && recordingData.recordingUrl) {
  // Fetch the recording data
  const response = await fetch(recordingData.recordingUrl);
  const recordingEvents = await response.json();
  console.log("Recording events:", recordingEvents);
} else if (recordingData.status === "failed") {
  console.error("Recording failed:", recordingData.error);
}

Get Video Recording URL

Retrieve the URL for the video recording (MP4):
import { Hyperbrowser } from "@hyperbrowser/sdk";

const client = new Hyperbrowser({
  apiKey: process.env.HYPERBROWSER_API_KEY,
});

// Poll until video recording is ready
let recordingData = await client.sessions.getVideoRecordingURL("session-id");

while (recordingData.status === "pending" || recordingData.status === "in_progress") {
  console.log(`Video recording status: ${recordingData.status}, waiting...`);
  await new Promise(resolve => setTimeout(resolve, 1000));
  recordingData = await client.sessions.getVideoRecordingURL("session-id");
}

console.log(recordingData.status); // "not_enabled" | "completed" | "failed"

if (recordingData.status === "completed" && recordingData.recordingUrl) {
  console.log(`Download video: ${recordingData.recordingUrl}`);
} else if (recordingData.status === "failed") {
  console.error("Video recording failed:", recordingData.error);
}

Response Format

Both recording endpoints return the same response structure:
{
  "status": "completed",
  "recordingUrl": "https://...",
  "error": null
}
Status values:
  • not_enabled - Recording was not enabled for this session
  • pending - Recording is queued for processing
  • in_progress - Recording is being processed
  • completed - Recording is ready (check recordingUrl)
  • failed - Recording failed (check error for details)

Replaying rrweb Recordings

Using the rrweb Player

Once you have the recording data, you can replay it using rrweb’s player:
<!-- Include rrweb player -->
<script src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/index.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css" />

<!-- Player container -->
<div id="player"></div>

<script>
  // If using rrweb npm package
  import rrwebPlayer from "rrweb-player";
  import "rrweb-player/dist/style.css";

  const recordingData = YOUR_RECORDING_DATA;

  const replayer = new rrwebPlayer({
    target: document.getElementById('player'),
    props: {
      events: recordingData,
      showController: true,
      autoPlay: true,
    },
  });
</script>
This launches an interactive player UI that allows you to play, pause, rewind, and inspect the recorded session.

Building a Custom Player

You can also use rrweb’s APIs to build your own playback UI. Refer to the rrweb documentation for details on customizing the Replayer.

Complete Example

Here’s a full workflow that creates a session, performs automation, and retrieves the recording:
import { Hyperbrowser } from "@hyperbrowser/sdk";
import { chromium } from "playwright-core";
import { config } from "dotenv";

config();

const client = new Hyperbrowser({
  apiKey: process.env.HYPERBROWSER_API_KEY,
});

async function sleep(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function runWithRecording() {
  // Create session with recording enabled
  const session = await client.sessions.create({
    enableWebRecording: true,
    enableVideoWebRecording: true,
  });

  console.log(`Session created: ${session.id}`);

  try {
    // Connect and run automation
    const browser = await chromium.connectOverCDP(session.wsEndpoint);
    const defaultContext = browser.contexts()[0];
    const page = defaultContext.pages()[0];

    await page.goto("https://example.com");
    await page.click("a");
    await page.goto("https://hackernews.com");
  } catch (error) {
    console.error("Error during automation:", error);
  } finally {
    // Stop the session
    await client.sessions.stop(session.id);
  }

  // Poll for web recording
  console.log("Waiting for web recording to be processed...");
  let webRecording = await client.sessions.getRecordingURL(session.id);
  while (
    webRecording.status === "pending" ||
    webRecording.status === "in_progress"
  ) {
    await sleep(1000);
    webRecording = await client.sessions.getRecordingURL(session.id);
  }
  if (webRecording.status === "completed") {
    console.log(`Web recording: ${webRecording.recordingUrl}`);
  } else if (webRecording.status === "failed") {
    console.error("Web recording failed:", webRecording.error);
  }

  // Poll for video recording
  console.log("Waiting for video recording to be processed...");
  let videoRecording = await client.sessions.getVideoRecordingURL(session.id);
  while (
    videoRecording.status === "pending" ||
    videoRecording.status === "in_progress"
  ) {
    await sleep(1000);
    videoRecording = await client.sessions.getVideoRecordingURL(session.id);
  }
  if (videoRecording.status === "completed") {
    console.log(`Video recording: ${videoRecording.recordingUrl}`);
  } else if (videoRecording.status === "failed") {
    console.error("Video recording failed:", videoRecording.error);
  }
}

runWithRecording();

Storage and Retention

Session recordings are stored securely in Hyperbrowser’s cloud infrastructure. Recordings are retained according to your plan’s data retention policy.

Limitations

  • Session recordings capture only the visual state of the page. They do not include server-side logs, database changes, or other non-DOM modifications.
  • Recordings may not perfectly reproduce complex WebGL or canvas-based animations.

Best Practices

  1. Always enable recordings for debugging: Recordings are invaluable when troubleshooting automation failures
  2. Poll for completion: After stopping a session, poll the recording URL endpoint until the status is completed
  3. Handle failures gracefully: Check the error field if the status is failed
  4. Use video recordings for sharing: MP4 videos are easier to share with non-technical stakeholders

Next Steps