Skip to main content
Playwriter’s screen recording uses Chrome’s native tabCapture API for high-quality video recording.
Recording happens in the extension context and survives page navigation. Videos are saved as WebM with VP9 codec.

Setup

For automated recording without user interaction, Chrome must be restarted with special flags:
import { getChromeRestartCommand } from 'playwriter'

const restartCmd = getChromeRestartCommand()
console.log(restartCmd)
// macOS: osascript -e 'quit app "Google Chrome"' && sleep 1 && open -a "Google Chrome" --args --allowlisted-extension-id=... --auto-accept-this-tab-capture
This will close all Chrome windows. Save your work first!
Or manually click the Playwriter extension icon on the tab once to grant permission.

Functions

startRecording

Starts recording a tab using Chrome’s tabCapture API.
import { startRecording } from 'playwriter'
import type { RecordingState } from 'playwriter'

const state: RecordingState = await startRecording({
  page,
  outputPath: './recording.webm',
  frameRate: 30,
  videoBitsPerSecond: 2500000,
  audio: false,
})

console.log(state.isRecording)  // true
console.log(state.startedAt)    // timestamp
console.log(state.tabId)        // Chrome tab ID
Parameters:
page
Page
required
Playwright Page instance to record
outputPath
string
required
Path where the video file will be saved (relative paths resolved from cwd)
sessionId
string
CDP tab session ID (pw-tab-* format). Auto-detected from page.sessionId() if not provided.
frameRate
number
default:"30"
Frame rate in fps
videoBitsPerSecond
number
default:"2500000"
Video bitrate in bps (2.5 Mbps default)
audioBitsPerSecond
number
default:"128000"
Audio bitrate in bps (128 kbps default)
audio
boolean
default:"false"
Include audio from the tab
relayPort
number
default:"19988"
Relay server port
Returns: Promise<RecordingState>
export interface RecordingState {
  isRecording: boolean
  startedAt?: number    // Timestamp when recording started
  tabId?: number        // Chrome tab ID being recorded
}
Throws: Error if Chrome wasn’t restarted with required flags and extension icon wasn’t clicked

stopRecording

Stops recording and saves the video file.
import { stopRecording } from 'playwriter'

const result = await stopRecording({ page })

console.log(result.path)      // '/path/to/recording.webm'
console.log(result.duration)  // Duration in milliseconds
console.log(result.size)      // File size in bytes
Parameters:
page
Page
required
Page that is being recorded
sessionId
string
CDP tab session ID. Auto-detected from page.sessionId() if not provided.
relayPort
number
default:"19988"
Relay server port
Returns: Promise<{ path: string; duration: number; size: number }>

isRecording

Checks if recording is currently active for a tab.
import { isRecording } from 'playwriter'

const state = await isRecording({ page })

if (state.isRecording) {
  console.log('Started at:', state.startedAt)
  console.log('Tab ID:', state.tabId)
}
Parameters:
page
Page
required
Page to check
sessionId
string
CDP tab session ID
relayPort
number
default:"19988"
Relay server port
Returns: Promise<RecordingState>

cancelRecording

Cancels recording without saving the video.
import { cancelRecording } from 'playwriter'

await cancelRecording({ page })
Parameters:
page
Page
required
Page being recorded
sessionId
string
CDP tab session ID
relayPort
number
default:"19988"
Relay server port

Complete Example

import { chromium } from 'playwright-core'
import {
  startPlayWriterCDPRelayServer,
  getCdpUrl,
  startRecording,
  stopRecording,
  isRecording,
} from 'playwriter'

const server = await startPlayWriterCDPRelayServer()
const browser = await chromium.connectOverCDP(getCdpUrl())
const context = browser.contexts()[0]
const page = context.pages()[0]

// Start recording
await startRecording({
  page,
  outputPath: './demo.webm',
  frameRate: 30,
  videoBitsPerSecond: 2500000,
})

// Perform actions
await page.goto('https://example.com')
await page.click('button')
await page.fill('input', 'test')

// Check status
const status = await isRecording({ page })
console.log('Recording:', status.isRecording)

// Stop and save
const result = await stopRecording({ page })
console.log(`Saved: ${result.path} (${result.size} bytes, ${result.duration}ms)`)

server.close()

Recording Quality

Playwriter recording is 100x more efficient than Playwright’s video recording:
  • Playwright: Sends base64-encoded images for every frame over CDP
  • Playwriter: Uses native chrome.tabCapture API with efficient WebM/VP9 encoding
Recommended settings:
await startRecording({
  page,
  outputPath: './recording.webm',
  frameRate: 30,              // 30fps for smooth playback
  videoBitsPerSecond: 2500000, // 2.5 Mbps for good quality
  audio: false,                // Enable if you need tab audio
})
For higher quality:
await startRecording({
  page,
  outputPath: './recording.webm',
  frameRate: 60,
  videoBitsPerSecond: 5000000, // 5 Mbps
})

Troubleshooting

If recording fails with “Extension has not been invoked” or “activeTab” error:
  1. Manual method: Click the Playwriter extension icon on the tab once
  2. Automated method: Restart Chrome with the command from getChromeRestartCommand()
The recording survives page navigation and continues until you call stopRecording() or cancelRecording().

Build docs developers (and LLMs) love