How to Record a Browser Screencast with Puppeteer
Record browser interactions to MP4 with Puppeteer’s experimental `page.record()` API. See runnable code, cleanup patterns, troubleshooting, and a still-image alternative.
Use Puppeteer’s experimental page.record() API to record browser activity to an MP4 file. Navigate to the page, start recording, perform the interactions you want to show, call recorder.stop(), and only then close the browser. The API is experimental, so check the reference for the Puppeteer version you install.
1. What you need to know
The current Puppeteer Page API marks record() experimental and directs users away from the deprecated screencast() method. The documented record() example writes an MP4 video stream to a path. The reference does not establish a minimum Chrome version, codec controls, frame-rate controls, or an ffmpeg requirement for record(); do not assume legacy options carry over.
Use recording when movement and interaction matter. Use page.screenshot() when you need a still image; it supports options such as fullPage and clip, but does not produce video. See the Puppeteer Page API and the screenshot guide.
2. Install Puppeteer
Start a Node.js project and install Puppeteer. Its launch guide guarantees operation with the bundled browser. A custom executablePath is at your own risk, so use the bundled browser unless you specifically need to test another installation.
npm init -y
npm install puppeteer
Save the following as record.mjs. The example uses the bundled browser and a page with a button; change the target URL and selector to match the flow you need to capture.
3. Record a browser flow
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', {waitUntil: 'networkidle2'});
const recorder = await page.record({path: 'recording.mp4'});
try {
// Perform the interactions that should appear in the video.
// Replace this selector with one present on your target page.
await page.click('button');
} finally {
await recorder.stop();
}
} finally {
await browser.close();
}
Run it with node record.mjs. The output path is relative to the process’s current working directory in this example. Use an absolute path if a scheduler or service might start the process from a different directory.
The nested try/finally blocks ensure the recording is stopped even if a page action throws, and the browser is closed even if recording cleanup fails. Keep recorder.stop() before browser.close() so the recording can be finalized.
4. Make the recording useful
Choose when recording begins
The sample navigates before starting the recorder, so the video focuses on the interaction rather than initial page loading. Start recording before navigation if the load itself is part of the demonstration:
const page = await browser.newPage();
const recorder = await page.record({path: 'page-load-and-flow.mp4'});
try {
await page.goto('https://example.com', {waitUntil: 'networkidle2'});
await page.click('button');
} finally {
await recorder.stop();
}
Pick a navigation wait condition that fits the site. Pages with persistent network activity may never become network-idle; for those, use a different supported goto wait condition and explicitly wait for the page state your action needs. If a control appears asynchronously, wait for it before clicking:
await page.waitForSelector('button', {visible: true});
await page.click('button');
Set the viewport before capture
Set the viewport before recording when the output needs a particular layout. This uses Puppeteer’s page viewport API and does not add recording-specific tuning options:
await page.setViewport({width: 1440, height: 900, deviceScaleFactor: 1});
Keep actions deterministic
- Wait for the specific selector or state required by the next action rather than relying on arbitrary delays.
- Use stable selectors and a known test account or page state for repeatable demonstrations.
- Keep credentials and private data out of pages that may appear in the video.
- Use the same viewport and browser version across captures when visual consistency matters.
5. Recording options and the deprecated API
The reviewed record() reference demonstrates a path option. It does not document the crop, scale, speed, quality, format, or FPS controls found on the legacy ScreencastOptions page. Check the installed version’s record() reference before depending on additional options.
page.screencast() is deprecated. Its documentation describes WebM with VP9 at 30 FPS by default, an ffmpeg requirement, and Chrome 153 or later. Those are legacy-method notes, not documented requirements for page.record(). Do not switch to the deprecated API solely to obtain those settings without confirming your version’s behavior and requirements.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
page.record is not a function |
The installed Puppeteer version does not expose the experimental API, or a different package/version is being run. | Check the installed package and that version’s Page API reference. Upgrade to a version whose reference documents record(), then rerun with the browser Puppeteer supports. |
| The MP4 is missing or incomplete | The recorder was not stopped, the process ended early, or the path is not where expected. | Await recorder.stop() before closing the browser or exiting. Log or resolve the output path, and use finally cleanup. |
| The script hangs during navigation | A network-idle condition may not occur on a page with persistent requests. | Choose a suitable navigation wait condition and then wait for the exact selector or state needed for the interaction. |
| The click fails because the element is absent | The page has not rendered the element yet, or the selector does not match this page. | Verify the selector and wait for it with page.waitForSelector() before clicking. |
| The recording differs across machines | A different browser executable, viewport, page state, or timing may be in use. | Prefer Puppeteer’s bundled browser, set a consistent viewport, and wait for page state explicitly. Puppeteer only guarantees compatibility with its bundled browser; custom executables are at your own risk. |
| Legacy screencast instructions mention ffmpeg or Chrome 153+ | Those requirements come from the deprecated screencast() documentation. |
Do not apply them to record() unless its reference for your installed version says they apply. |
7. Performance, reliability, and cost
Recording captures activity over time, so keep the browser open until the recorder is stopped and the file is finalized. The reviewed reference does not publish performance figures, resource limits, or a recording cost; those depend on your runtime and infrastructure. For long flows, keep the recorded interval focused on the material you need and ensure the process has enough time to stop cleanly.
For repeatable output, use Puppeteer’s bundled browser, set a fixed viewport, wait for explicit page conditions, and put both recorder shutdown and browser closure in cleanup blocks. Because record() is experimental, verify its behavior when upgrading Puppeteer.
8. Still screenshots with ScreenshotNeo
If you need a clean still image rather than a video, ScreenshotNeo is a website screenshot API and MCP server for developers. It takes a URL and returns PNG, JPEG, WebP, or PDF. For Puppeteer documentation, for example:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://pptr.dev -o shot.webp
See the ScreenshotNeo API documentation for the request options and formats.
ScreenshotNeo captures still images and PDFs; it does not replace Puppeteer’s browser screencast when the deliverable must show motion over time. Its clean-shot flow accepts cookie banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots, and every feature is on every plan.
Sign up for 1,000 free screenshots a month, with no card required.
9. FAQ
Does Puppeteer record audio?
The reviewed page.record() reference documents an MP4 video stream and does not establish audio capture. Do not rely on it for audio without a version-specific reference that confirms support.
Can I use this for a still screenshot?
Use page.screenshot() for a still image. It supports full-page and clipped captures; recording is for motion.
Is page.record() stable?
No. The current Page API marks it experimental, so check the docs for the Puppeteer version you use.
Which Chrome version does record() require?
The reviewed record() reference does not specify a minimum. The Chrome 153+ note belongs to deprecated screencast() and should not be carried over.


