When an automated test fails, the error message alone is often insufficient to diagnose the root cause.
You're usually left asking:
- What actions happened before the failure?
- Which element was being interacted with?
- Where exactly did the app start behaving differently than expected?
- Can a teammate understand the failure without re-running the test themselves?
Playwright 1.59 introduced page.screencast, a programmatic recording API that answers these questions by turning a test run into a narrated, annotated video — instead of just a pass/fail line in a report.
With screencast-based reporting, a test can produce:
- A recording of the full execution
- Visual annotations on every interacted element
- Chapter markers that break the run into named stages
- Custom HTML overlays for context or metadata
- Real-time JPEG frame capture for external tooling
- "Video receipts" for AI-driven test and coding agents
The goal is simple: make failures faster to understand, and make passing runs easier to trust.
Why Screencasts Matter
Traditional artifacts already carry useful information:
| Artifact | What it tells you |
|---|---|
| Logs | What the framework reported |
| Screenshots | The app's state at one moment |
| Traces | DOM snapshots, network activity, console output |
A screencast adds a fourth layer that none of these provide well on their own: the full user journey, as a video.
Instead of staring at this:
Expected: "Order completed"
Received: "Payment failed"
...you watch the actual sequence: checkout opens, a product is added, payment details are entered, submit is clicked, and the error appears, all annotated in place.
Screencast vs. Playwright Trace
These two tools solve different problems, and a mature framework should use both.
Trace Viewer is built for deep technical debugging:
- Network activity
- DOM snapshots
- Console messages
- Step-by-step execution inspection
Screencast is built for fast, visual understanding:
- A timeline of what a user (or agent) actually did
- Visible, annotated interactions
- Context that non-technical stakeholders can follow without opening dev tools
In practice: reach for traces when you need to diagnose why something broke, and for screencasts when you need to show what happened quickly, and to a wider audience than just engineers.
Setting Up Screencast Recording
The cleanest way to roll this out across a suite is a shared fixture, so every test gets recording, annotations, and reporting for free — no boilerplate in individual test files.
// fixtures.ts
import { test as base } from '@playwright/test';
export const test = base.extend({
page: async ({ page }, use, testInfo) => {
const videoPath = testInfo.outputPath('screencast.webm');
await page.screencast.start({ path: videoPath });
await page.screencast.showActions({ position: 'top-right' });
await use(page);
await page.screencast.stop();
await testInfo.attach('Screencast', {
path: videoPath,
contentType: 'video/webm',
});
},
});
Every test that imports test from this file now automatically records a screencast and attaches it to the report, including in CI.
Adding Narration Inside a Test
Once the fixture is in place, use showChapter() to label the stages of a test as it runs:
import { test } from './fixtures';
test('checkout flow', async ({ page }) => {
await page.screencast.showChapter('Login', {
description: 'User authentication flow',
});
await page.goto('/login');
// ...login steps
await page.screencast.showChapter('Checkout', {
description: 'Complete purchase flow',
});
// ...checkout steps
});
The resulting video reads like a labeled walkthrough rather than raw, unexplained footage.
Screencast API Reference
page.screencast.start(options)
Starts a screencast session. Pass path to save a WebM recording, or onFrame to receive JPEG frames in real time — both can be used together.
await page.screencast.start({
path: 'execution.webm',
size: { width: 1280, height: 720 },
quality: 90,
});
Because it's called explicitly, you control exactly when recording starts and stops, unlike the older, always-on video config option, which records for the entire test lifecycle regardless of what you actually need.
page.screencast.stop()
Stops the active session and finalizes the video file at the path given to start(). Typically called right after the test body finishes:
await use(page);
await page.screencast.stop();
page.screencast.showActions(options)
Annotates interacted elements as they're clicked, filled, or hovered, so a reviewer can see exactly which element was touched and when.
await page.screencast.showActions({
position: 'top-right',
duration: 1500, // ms each annotation stays visible
fontSize: 16,
cursor: 'pointer', // animates a cursor between actions; use 'none' to disable
});
Valid position values: top-left, top, top-right, bottom-left, bottom, bottom-right.
To turn annotations off mid-test:
await page.screencast.hideActions();
Suite-wide alternative: if you want action annotations everywhere without touching a fixture, enable them directly in playwright.config.ts:
export default defineConfig({
use: {
video: {
mode: 'on',
show: {
actions: { position: 'top-left' },
test: { position: 'top-right' },
},
},
},
});
page.screencast.showChapter(title, options)
Displays a centered, blurred-backdrop overlay to mark a new stage of the recording. Disappears automatically after duration (defaults to a couple of seconds).
await page.screencast.showChapter('Payment validation', {
description: 'Submitting card details and confirming charge',
duration: 4000,
});
Chaining a few of these gives you a scannable timeline:
Login → Search product → Add to cart → Checkout → Payment validation
Reviewers can jump straight to the stage they care about instead of scrubbing a long recording blindly.
page.screencast.showOverlay(html, options) / showOverlays() / hideOverlays()
Adds arbitrary HTML on top of the recording — handy for environment info, build numbers, or debug messages.
await page.screencast.showOverlay(
'<div style="color:red">Running checkout validation</div>',
{ duration: 3000 }
);
Omit duration and the overlay stays until you explicitly hide or remove it. showOverlays() / hideOverlays() toggle visibility of overlays already on screen without discarding them.
Real-Time Frame Capture
Pass onFrame to start() to stream JPEG-encoded frames while the test runs, instead of (or alongside) writing a video file:
await page.screencast.start({
onFrame: ({ data, timestamp, viewportWidth, viewportHeight }) => {
processFrame({ data, timestamp, viewportWidth, viewportHeight });
},
});
This opens the door to live dashboards, vision-model analysis, or custom monitoring pipelines that react to what's on screen during execution, not after.
Agentic Video Receipts
Screencasts are also a good fit for AI-driven testing and coding agents, where a plain text summary ("verified checkout — passed") is much less convincing than a video showing exactly what the agent did.
await page.screencast.start({ path: 'receipt.webm' });
await page.screencast.showActions({ position: 'top-right' });
await page.screencast.showChapter('Verifying checkout flow', {
description: 'Validating payment completion',
});
// Agent performs its verification steps here
await page.locator('#coupon').fill('SAVE20');
await page.locator('#apply-coupon').click();
await expect(page.locator('.discount')).toContainText('20%');
await page.screencast.showChapter('Done', {
description: 'Checkout validation completed',
});
await page.screencast.stop();
The output is a self-contained "receipt": chapter titles explain intent, action annotations show exactly what was clicked or filled, and a human can review the whole thing in the time it takes to watch a short clip, no re-running the agent required.
CI/CD Recommendations
Recording every single test run adds up fast in storage and CI time. A few practical guardrails:
- Record failures, not everything. Attach the screencast only when a test doesn't match its expected status:
if (testInfo.status !== testInfo.expectedStatus) {
await testInfo.attach('Screencast', {
path: videoPath,
contentType: 'video/webm',
});
}
- Record critical paths deliberately. Reserve always-on recording for your highest-value flows (checkout, sign-up, payment) rather than every spec.
- Clean up old artifacts on a schedule so recordings don't silently pile up in CI storage.
Official Resources
This post covers the common patterns, but the official docs are the source of truth for full option lists, edge cases, and future updates:
- Screencast API reference — full method and options list
-
Playwright release notes — see the v1.59 entry for the original
page.screencastannouncement, and later releases for additions like thecursoroption - Playwright documentation — general guides, including video/trace configuration If you hit something this post doesn't cover, those are the pages to check first.
Final Thoughts
Test automation is moving past a simple pass/fail line. Screencast-based reporting gives teams:
- Faster failure diagnosis
- A visual record of execution flow anyone can follow, technical or not
- Better handoffs between QA and development
- A foundation for reviewing AI-driven testing and coding agents
A mature framework shouldn't just report that a test failed, it should show what happened, where it happened, and give you what you need to fix it.

Top comments (0)