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.
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:
- Note the session
id when you create the session
- Ensure the session has been stopped before fetching the recording
- 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);
}
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
- Always enable recordings for debugging: Recordings are invaluable when troubleshooting automation failures
- Poll for completion: After stopping a session, poll the recording URL endpoint until the status is
completed
- Handle failures gracefully: Check the
error field if the status is failed
- Use video recordings for sharing: MP4 videos are easier to share with non-technical stakeholders
Next Steps