Skip to main content

Screen Recording

Screen Recording generates on-demand WebM video files from your Puppeteer or Playwright automation scripts using CDP commands. It supports both video and audio capture, and requires a paid plan. This feature is not to be confused with Session Replay, which records DOM events for playback in your dashboard.

warning

The Screen Recording feature is not available on the /chrome route due to restrictions from Chrome's runtime environment. Use the /stealth endpoint instead.

Basic Usage

Add record=true to your WebSocket connection string and use CDP commands (Browserless.startRecording / Browserless.stopRecording) to control capture. Screen Recording works with Puppeteer, Playwright, and any library that supports CDP connections.

Each example below shows the complete workflow: connecting to Browserless with recording enabled, starting a recording, performing automation tasks, stopping the recording, and saving the resulting WebM file.

import fs from "fs";
import puppeteer from "puppeteer-core";

const sleep = (ms) => new Promise((res) => setTimeout(res, ms));
const token = "YOUR_API_TOKEN_HERE";

const wsEndpoint = `wss://production-sfo.browserless.io?token=${token}&headless=false&stealth&record=true`;
const browser = await puppeteer.connect({ browserWSEndpoint: wsEndpoint });
const page = await browser.newPage();

// Set custom viewport for recording dimensions
await page.setViewport({ width: 1280, height: 720 });
await page.goto("https://browserless.io");

// Start recording session
const cdp = await page.createCDPSession();
await cdp.send("Browserless.startRecording");

// Perform your automation actions here
await sleep(15000);

// Stop recording and save
const response = await cdp.send("Browserless.stopRecording");
const file = Buffer.from(response.value, "binary");
await fs.promises.writeFile("./recording.webm", file);

await browser.close();

Key Features

  • High-quality WebM output with audio captured automatically
  • Video dimensions inherited from the browser viewport at recording start
  • Works with Puppeteer, Playwright, and other CDP-compatible libraries
  • Can be combined with LiveURL for human-in-the-loop recording workflows

Video Dimensions Configuration

The resulting WebM dimensions match the page viewport size at the moment recording starts. If you run await page.setViewport({ width: 2048, height: 720 }); before Browserless.startRecording, the video will be 2048x720 px.

You can set viewport dimensions in several ways:

  • Puppeteer: Use page.setViewport({ width: 1280, height: 720 })
  • URL Parameters: Add --window-size=1280,720 and headless=false to your connection string
  • Launch Arguments: Configure viewport in browser launch options
CDP Fallback for Viewport Sizing

If your automation library does not expose a page.setViewport or page.setViewportSize method, you can set dimensions directly with the CDP Emulation.setDeviceMetricsOverride method. Most libraries use this CDP command under the hood when updating viewport size. See the DevTools Protocol reference.

Avoid Resizing During Recording

Do not change the viewport size while recording is in progress. Mid-recording viewport changes can distort rendered content and produce a visibly stretched or otherwise unusual recording.

Recording with LiveURL

You can combine Screen Recording with LiveURL to create human-in-the-loop recording workflows. This captures a WebM video file while a human interacts with the browser in real time. For full LiveURL documentation, see Hybrid Automation.

import puppeteer from 'puppeteer-core';
import fs from 'fs';

const token = 'YOUR_API_TOKEN_HERE';

const queryParams = new URLSearchParams({
token,
timeout: 180000,
headless: true,
}).toString();

(async () => {
let browser = null;

try {
// Connect with recording enabled
browser = await puppeteer.connect({
browserWSEndpoint: `wss://production-sfo.browserless.io?record=true&${queryParams}`,
});

const page = await browser.newPage();
await page.goto('https://example.com/login', {
waitUntil: 'networkidle2'
});

// Initialize CDP session
const cdp = await page.createCDPSession();

// Start recording
await cdp.send("Browserless.startRecording");

// Generate LiveURL for user interaction
const { liveURL } = await cdp.send('Browserless.liveURL', {
timeout: 30000
});
console.log('Share this URL:', liveURL);

// Wait for user interaction
await new Promise(resolve => setTimeout(resolve, 30000));

// Stop recording and save
const response = await cdp.send("Browserless.stopRecording");
const recordingBuffer = Buffer.from(response.value, "binary");
await fs.promises.writeFile("./session-recording.webm", recordingBuffer);

} catch (error) {
console.error('Recording failed:', error);
} finally {
if (browser) await browser.close();
}
})();

Advanced Session Monitoring

For workflows that need visual feedback, you can implement monitoring logic that notifies users when a task completes:

// Enhanced monitoring with visual feedback
await page.waitForFunction(() => {
const currentUrl = window.location.href;
const isSuccess = currentUrl.includes('logged-in') ||
currentUrl.includes('dashboard') ||
document.querySelector('.post-login') !== null;

if (isSuccess) {
// Create success notification
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed; top: 20px; left: 50%;
transform: translateX(-50%); background-color: #4CAF50;
color: white; padding: 15px 30px; border-radius: 5px;
z-index: 9999; font-family: Arial, sans-serif;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
`;
notification.textContent = 'Login successful - you can close this tab';
document.body.appendChild(notification);
return true;
}
return false;
}, { timeout: 60000 });

Custom Recording Triggers

Start recording only after specific conditions are met:

await page.waitForSelector('.login-form');
await cdp.send("Browserless.startRecording");

Conditional Recording

Record only if a specific element is present:

const hasLoginForm = await page.$('.login-form');
if (hasLoginForm) {
await cdp.send("Browserless.startRecording");
}

Next Steps