Use Puppeteer’s performance tracing when you need to understand a page’s rendering process. Start page.tracing before navigation or an interaction, stop it afterward, and open the resulting JSON trace in Chrome DevTools. Add deterministic screenshots for visual checkpoints, or use page.record() when a replayable MP4 is more useful than browser-timing data.
A screenshot alone is only one visual checkpoint; it cannot show every script, layout, paint, network event, or frame between two moments. The workflow below shows how to collect the right artifact, synchronize it reliably, and diagnose common failures.
Choose the capture level that answers your question
“Capture the rendering process” can mean three different things. Pick the artifact before writing the test:
| Method | Artifact | Temporal coverage | Best use | Analysis and overhead |
|---|---|---|---|---|
| Performance trace | JSON trace | Navigation and interactions across the recording window | Diagnosing scripting, style recalculation, layout, painting, loading, and frame timing | Deepest browser detail; tracing adds instrumentation work and can produce large files. Chrome DevTools can analyze it directly. |
| Screenshots | PNG, JPEG, or WebP images | Single checkpoints that you choose | Visual regression tests, before/after states, and documenting a component | Low complexity; no timeline of intermediate work. Full-page and element captures can take additional time. |
| Video | MP4 from page.record(); older screencast code usually produces WebM/VP9 |
Continuous visual replay while recording | Showing what a user saw during navigation or an interaction | Easy to review visually, but it does not expose the browser’s internal timing detail. Encoding and storage add work. |
For a performance bug, begin with a trace. For a visual checkpoint, take a screenshot after the application signals readiness. For a human-reviewable replay, record a video as well as (or instead of) the trace.
#1 Best Overall
Install Puppeteer and make the run reproducible
The regular puppeteer package installs a compatible Chrome during npm i puppeteer. If your environment blocks install scripts, install the browser explicitly with npx puppeteer browsers install. puppeteer-core is the alternative when you already manage a Chrome or Chromium binary; it does not download one for you, so provide that executable in your launch configuration.
Before collecting any artifact, fix the variables that can change rendering:
- Record the Puppeteer and browser versions.
- Set a known viewport and device scale factor.
- Choose the color scheme, media emulation, locale, timezone, fonts, and test data deliberately.
- Decide whether the run uses a warm or empty cache and what network conditions apply.
- Define an application-specific readiness signal, such as
[data-render-ready="true"], instead of relying only on a generic delay. - Save console messages, page errors, failed requests, and the capture artifact together.
These controls matter when comparing two runs. A changing font, timezone, browser build, cache, or asynchronous data response can look like a rendering regression even when the code did not change.
Capture a Chrome performance trace
Call page.tracing.start() before the navigation or interaction you want to inspect. Stop it after the page reaches the state of interest. Setting screenshots: true embeds visual snapshots in the trace, which helps correlate a paint with the timeline; it does not turn the trace into a video.
Recommended Free Tools
import puppeteer from 'puppeteer';
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({
width: 1440,
height: 900,
deviceScaleFactor: 1
});
await page.tracing.start({
path: 'render-trace.json',
screenshots: true
});
await page.goto('https://example.com', { waitUntil: 'networkidle0' });
// Perform the interaction whose rendering you want to inspect.
// Example: await page.click('#open-menu');
// Example: await page.locator('[data-render-ready="true"]').wait();
await page.tracing.stop();
await browser.close();
})();
networkidle0 means that Puppeteer observed no active network connections for its idle window. It is useful, but it is not proof that an application has finished rendering: a client-side request can start later, an animation can still be moving, or a page can be visually ready while a telemetry connection remains open. Prefer a known selector, data attribute, or settled animation when the application provides one.
Open and interpret the trace
Open render-trace.json in Chrome DevTools or the Chrome timeline viewer. Look for long tasks, script execution, style and layout work, paint and raster activity, network timing, and gaps between frames. The embedded screenshots help you identify what was on screen at a particular event. A trace explains work performed by the browser; it is not a user-monitoring report and it does not guarantee that every visual intermediate state was captured.
Rank #2
Trace a specific interaction
Start tracing immediately before the action, then stop as soon as the resulting state is ready. For example, navigate first, wait for the baseline selector, start tracing, click the control, wait for the post-click selector, and stop. This keeps unrelated startup work out of the file and makes the interaction’s scripting, layout, and paint costs easier to isolate.
Take deterministic screenshots at rendering checkpoints
Use screenshots when the question is “what did the page look like at this state?” Set the viewport and state, wait for readiness, and then call page.screenshot(). The following captures the complete page:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
import puppeteer from 'puppeteer';
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const url = 'https://example.com';
await page.setViewport({
width: 1440,
height: 900,
deviceScaleFactor: 1
});
await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.locator('[data-render-ready="true"]').wait();
await page.screenshot({
path: 'rendered.png',
fullPage: true
});
await browser.close();
})();
For a single component, capture the element rather than the entire document:
const card = page.locator('.product-card');
await card.screenshot({ path: 'product-card.png' });
Keep the viewport, device scale factor, color scheme, locale, timezone, fonts, and browser version fixed when comparing images. If lazy-loaded content is part of the result, wait for the application’s loaded state or scroll the relevant region before capturing; otherwise the screenshot may faithfully record an intentionally incomplete state.
Record a visual replay as MP4
Current Puppeteer exposes page.record(), backed by Chrome’s Page.startScreenRecording, and writes an MP4 stream. Start the recorder before navigation or the interaction:
import puppeteer from 'puppeteer';
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({
width: 1440,
height: 900,
deviceScaleFactor: 1
});
const recorder = await page.record({ path: 'render.mp4' });
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.locator('[data-render-ready="true"]').wait();
await recorder.stop();
await browser.close();
})();
If you maintain older code that calls page.screencast(), treat it as deprecated. Its documented default is WebM/VP9 at 30 FPS and it requires ffmpeg. Move new code to page.record() for the current MP4 workflow, and verify your installed Puppeteer and Chrome versions before relying on a recording feature in CI.
Free tools Windows power users keep installed
One-click scans. No signup required.
Use the Chrome DevTools Protocol when Puppeteer’s wrapper is not enough
Create a CDP session for protocol-level controls such as Page.captureScreenshot, Page.startScreencast, or Page.startScreenRecording:
const client = await page.createCDPSession();
await client.send('Page.enable');
// Protocol-level operations can now be issued, for example:
// const image = await client.send('Page.captureScreenshot', { format: 'png' });
// await client.send('Page.startScreencast', { format: 'png' });
A CDP screencast emits screencastFrame events. Your collector must process each frame and send a matching Page.screencastFrameAck; failing to acknowledge frames can stop the stream or cause dropped data. Use this route when you need protocol options or a frame-by-frame collector that the high-level API does not expose.
Synchronize captures with the application, not just the network
Use explicit readiness signals
Add a stable marker such as data-render-ready="true" after data binding, image decoding, and the relevant layout work complete. Waiting for that marker is more meaningful than guessing with a fixed sleep. If no marker exists, wait for a selector that only appears in the finished state and, where necessary, wait for a known animation to settle.
Separate navigation from the state under test
For a trace of a button, menu, or route transition, first reach a stable baseline, then start tracing or recording, perform the action, and stop at the post-action marker. This keeps the artifact focused and makes repeated runs comparable.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Capture diagnostics alongside the artifact
Register handlers for console messages, page errors, and failed requests. Persist those logs with the trace, image, or video. A blank screenshot paired with a failed request is actionable; a blank screenshot without the surrounding browser errors is not.
Troubleshoot common capture failures
| Symptom | Likely cause | Fix |
|---|---|---|
| The trace contains only part of the load. | Tracing started after navigation or stopped before the interaction completed. | Start tracing before the target navigation/action and stop only after the application-specific readiness condition. |
The script hangs at networkidle0. |
A persistent connection, polling request, or analytics stream prevents network idleness. | Use domcontentloaded followed by a readiness selector, or define a test-only condition that indicates settled rendering. |
| The screenshot is blank or missing content. | The page failed to load, content is asynchronous, or the capture ran before the component was ready. | Record failed requests and page errors, wait for the component’s marker, and confirm the test data and viewport. |
| Lazy images are absent. | The images load only after entering the viewport or after a later application event. | Trigger the application’s image-loading condition (often by scrolling), wait for completion, then capture. |
| Two “identical” screenshots differ. | Fonts, locale, timezone, color scheme, device scale, browser version, cache, network, or test data changed. | Fix and record those inputs; use the same browser build and readiness rule for both runs. |
page.record() is unavailable or fails. |
The installed Puppeteer/Chrome combination does not expose the current recording API. | Update the compatible pair, or use the documented CDP recording path. For legacy page.screencast(), install and verify ffmpeg. |
| CDP screencast stops after a few frames. | Frame events were not acknowledged. | Handle each screencastFrame and send Page.screencastFrameAck with its session identifier. |
| Chrome cannot start in CI. | The browser binary was not downloaded because install scripts were blocked, or puppeteer-core has no executable configured. |
Run npx puppeteer browsers install for Puppeteer, or configure the managed Chrome path when using puppeteer-core. |
Plan storage, runtime, and evidence quality
Traces, screenshots, and videos answer different questions, so do not collect all three blindly on every run. Use a trace for a focused diagnostic window, screenshots for stable checkpoints or visual diffs, and video when a reviewer needs to watch the sequence. Traces with embedded screenshots and MP4 files can be large; write them to a dedicated artifact directory and retain them with the browser logs that explain failures.
Rank #4
Tracing and video recording add work to the browser. Keep the same capture settings when comparing runs, but do not treat a traced run’s absolute timing as identical to an uninstrumented production visit. Use the trace to locate relative hotspots and frame delays, then validate important changes with a consistent, separately defined benchmark process.
When sharing artifacts, check whether URLs, query strings, console output, screenshots, or trace payloads contain private test data. Remove or restrict those files according to your project’s security policy.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Or skip the browser setup
If you only need a clean rendered image or PDF rather than browser timing diagnostics, ScreenshotNeo provides a one-request website screenshot API. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and each response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf tools to Claude, Cursor, and other MCP clients.
Use the API documentation at https://screenshotneo.com/docs/ for authentication and options. This cURL request saves a WebP image:
curl -G "https://api.screenshotneo.com/v1/shot"
-d access_key=YOUR_API_KEY
--data-urlencode url=https://example.com
-o shot.webp
The same request in Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
And in Node.js:
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', image);
ScreenshotNeo supports full-page and element captures, dark mode, device presets or custom viewports, retina scale, PDF paper and margin controls, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify migration.
The Free plan includes 1,000 screenshots each month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. Create a free ScreenshotNeo account to try the service.
FAQ
Is a Puppeteer trace the same thing as a Lighthouse report?
No. A Puppeteer trace is a Chrome timeline artifact generated around the navigation or interaction you recorded. It is intended for DevTools timeline analysis, not as a scored audit or a web-analytics export.
Best Value
- Used Book in Good Condition
What does screenshots: true change in a trace?
It adds visual snapshots to the trace so you can correlate browser work with what was displayed. It does not capture every frame as a standalone movie; use page.record() when continuous replay is the requirement.
Can I publish a trace from a production page?
Review it first. Trace and console artifacts can expose URLs, parameters, text, or test data. Share only the sanitized portion needed to diagnose the issue.
Frequently Asked Questions
Is a Puppeteer trace the same thing as a Lighthouse report?
No. A Puppeteer trace is a Chrome timeline artifact generated around the navigation or interaction you recorded. It is intended for DevTools timeline analysis, not as a scored audit or a web-analytics export.
What does screenshots: true change in a trace?
It adds visual snapshots to the trace so you can correlate browser work with what was displayed. It does not capture every frame as a standalone movie; use page.record() when continuous replay is the requirement.
Can I publish a trace from a production page?
Review it first. Trace and console artifacts can expose URLs, parameters, text, or test data. Share only the sanitized portion needed to diagnose the issue.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

