How to Monitor Puppeteer Performance with the Inspector
Capture Puppeteer traces, inspect browser work in Chrome DevTools, and debug the Node script that drives the browser.
To monitor Puppeteer performance, record a trace around the slow page load or interaction with page.tracing.start() and page.tracing.stop(), then inspect the resulting timeline in Chrome DevTools. For interactive inspection, use browser DevTools to pause page JavaScript and Node’s inspector to pause the Puppeteer script. Add page.metrics() when you need summary measurements alongside the trace. A trace shows what work happened during a reproduction; it does not identify the cause by itself.
This guide covers current Puppeteer APIs documented in the sources below. Documentation snapshots can differ, so check the API reference for the Puppeteer version installed in your project.
1. Capture and inspect a performance trace
A trace records browser activity over a chosen interval. Start it before the navigation or interaction you want to study, stop it when that behavior finishes, and open the trace file in Chrome DevTools or a timeline viewer. Puppeteer allows only one active trace per browser at a time. See the Puppeteer Tracing API.
Runnable example
Save this as trace.mjs. Install Puppeteer in the project with npm install puppeteer, then run node trace.mjs. The script records navigation to the example URL and writes trace.json in the current directory.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.tracing.start({ path: 'trace.json' });
try {
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
} finally {
await page.tracing.stop();
}
} finally {
await browser.close();
}
The nested finally blocks stop tracing and close the browser even if navigation fails. That matters for repeatable investigations: a failed reproduction should not leave the browser process or trace session open. Choose the navigation condition that matches the behavior you are investigating; waiting for network idle can be inappropriate for pages with persistent requests.
Open the trace
- Run the script from the project directory and confirm that
trace.jsonwas created. - Open the file in Chrome DevTools’ Performance tooling or another timeline viewer that supports the trace format.
- Find the interval corresponding to navigation or the interaction. Inspect the recorded work in that interval and look for activity that overlaps the user-visible delay.
- Change one relevant part of the page or test setup, reproduce the same scenario, and compare traces. Keep the URL, interaction, and environment as consistent as practical.
The trace is evidence about a particular run and its recorded interval. Puppeteer provides the capture mechanism; interpreting the timeline and deciding which work dominates the slow interval remains part of the investigation.
2. Configure tracing for the question you are asking
page.tracing.start() accepts options including an output path, trace categories, screenshot capture, and buffer size. With a path, Puppeteer writes the trace to disk. Without a path, the trace is not written to disk; tracing.stop() returns it as a Uint8Array. Consult the version-specific TracingOptions reference.
| Option or behavior | When to use it | Practical note |
|---|---|---|
path |
Save a trace for later inspection or comparison. | Use a distinct filename per run if you need to preserve multiple reproductions. |
categories |
Choose trace event categories relevant to the investigation. | Use categories supported by the Chromium version you run; category choices affect what appears in the trace. |
screenshots |
Include screenshots in the trace when visual context for the timeline is useful. | Additional recorded data can increase trace size and collection overhead. |
bufferSize |
Set the trace buffer size when the capture needs a deliberate bound. | The documented Chromium default is 200 MB (200,000 KB) when omitted or set to zero. A long or noisy capture can still be difficult to interpret. |
No path |
Retrieve trace bytes in memory for programmatic handling. | tracing.stop() returns a Uint8Array; write or process it in your script if needed. |
Keep captures bounded to the load or interaction under investigation. This makes the relevant timeline easier to find and limits the amount of instrumentation data collected. A trace and its viewer are diagnostic tools; their measurements should not be treated as an exact user-experienced duration.
3. Use Chrome DevTools to inspect page JavaScript
When the question is what the page is doing at a particular point, launch a visible browser with DevTools enabled. Puppeteer’s LaunchOptions documents devtools: true; enabling it forces headless to false.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({
headless: false,
devtools: true,
});
try {
const page = await browser.newPage();
await page.goto('https://example.com');
await page.evaluate(() => {
debugger;
return document.title;
});
} finally {
await browser.close();
}
Run this from a terminal with a graphical desktop available. Execution pauses at the page-side debugger statement; use the opened DevTools panel to inspect page state and resume. Remove or disable the breakpoint for unattended runs, since a paused page can make an automation job appear hung.
4. Attach Node’s inspector to the Puppeteer script
Browser DevTools inspects code running in the page. Node’s inspector is for the automation code running in Node: the script that launches the browser, waits, clicks, evaluates expressions, or handles errors. Puppeteer’s debugging guide documents running Node with --inspect-brk and connecting through chrome://inspect/#devices.
- Put a
debugger;statement at the server-side line you want to inspect, or use--inspect-brkto pause at startup. - Start the script with
node --inspect-brk trace.mjs. - Open
chrome://inspect/#devicesin Chrome and choose the inspect action for the Node target. - Step through the Puppeteer script, inspect variables, and resume execution.
Keep browser and Node debugging distinct: a Node breakpoint does not expose page JavaScript scope, and a page breakpoint does not show the automation script’s local variables. Puppeteer’s guide also notes that page.click() cannot be run directly from the DevTools console because of a Chromium bug; put automation actions in the Puppeteer script.
5. Add summary metrics with page.metrics()
page.metrics() returns a metrics object that can help describe a run alongside its trace. Its timestamps are monotonic seconds from an arbitrary point in the past, not calendar timestamps. Do not label them as wall-clock times or compare them as dates. See the Puppeteer Page API.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com');
const metrics = await page.metrics();
console.log(metrics);
} finally {
await browser.close();
}
Metrics are a summary, while a trace is a timeline. Use the trace to inspect when browser work occurred and metrics as supporting measurements; neither should be mistaken for a complete explanation of a slowdown.
6. Choose the right inspection path
| Need | Use | What it tells you |
|---|---|---|
| See browser work across a slow load or interaction | Trace capture and timeline viewer | A timeline for the recorded reproduction. |
| Inspect page code and state at a specific point | Chrome DevTools and a page-side breakpoint | The paused page execution context. |
| Step through the script driving the browser | Node inspector | The automation code, its variables, and control flow. |
| Record a small set of summary measurements | page.metrics() |
A metrics object to accompany the trace. |
These methods can be combined, but they answer different questions. Start with a trace for timeline diagnosis; add a breakpoint when you need to inspect code or state at a particular point.
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Starting a trace fails because another trace is active | A prior capture was not stopped, or another page in the same browser is tracing. | Stop the active trace before starting another. Use try/finally so exceptions still reach page.tracing.stop(). |
| No trace file appears | The start options omitted path, the script wrote to a different working directory, or execution failed before stopping. |
Set an explicit path, check the process working directory, and inspect the original navigation error. Without a path, collect the bytes returned by tracing.stop(). |
| The trace is empty or misses the slow action | Tracing began after the work or stopped before it completed. | Start tracing before the relevant navigation or interaction and stop after it. Reproduce the same sequence that exhibits the delay. |
Navigation never reaches networkidle2 |
The page may keep requests open or continue background network activity. | Use a navigation condition suited to the page, then explicitly wait for the selector, event, or application state that represents the action you need to measure. Keep the trace interval focused. |
devtools: true does not open a panel in the expected environment |
The browser is running without a usable graphical desktop, or the launch configuration is still headless. | Run in an environment with a desktop and launch visibly. Puppeteer documents that enabling DevTools forces headless to false. |
| The script appears stuck during debugging | Execution is paused at a breakpoint and is waiting for you to resume it. | Continue execution in the relevant inspector and remove the breakpoint for unattended runs. |
| A Node breakpoint does not show page variables | The breakpoint is in the Node execution context. | Use a page-side breakpoint in browser code for page state; use Node’s inspector for the Puppeteer driver. |
| Trace and metrics do not match a reported wall-clock time | Instrumentation, run conditions, and monotonic timestamps describe different things. | Use metrics timestamps as monotonic values, keep reproduction conditions consistent, and treat traces as diagnostic recordings rather than exact user timing. |
8. Performance, reliability, and cost considerations
Tracing and interactive debugging add instrumentation and can change how a run behaves. Keep the recorded interval as short as the question allows, avoid unnecessary screenshots or categories, and compare like-for-like reproductions. Do not present instrumented timings as a benchmark or assume one trace proves a general performance result.
For reliable investigations, make the reproduction explicit: use the same URL, navigation path, and interaction; record the same interval; ensure tracing stops in error paths; and keep trace files associated with the code and environment that produced them. A trace can help locate work in time, but determining which work matters requires interpreting the timeline.
The Puppeteer workflow uses your browser process and local or hosted compute. Trace storage and inspection have ordinary storage and compute costs in your environment; the cited Puppeteer references do not specify service pricing or benchmark results. Puppeteer’s own overview names trace capture as a performance-diagnosis use case: What is Puppeteer?
9. Or skip the browser setup
If your goal is a clean screenshot of a page rather than a Puppeteer performance trace, ScreenshotNeo is a website screenshot API and MCP server. A single GET request returns a screenshot or PDF, and its API documentation covers the available options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and responses identify the page verdict and billing status in headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. This screenshot service does not capture Puppeteer traces or replace the inspector workflow above.
Sign up free for 1,000 screenshots a month, with no card required.
10. FAQ
Can I run more than one Puppeteer trace at once?
No. Puppeteer documents one active trace per browser. Stop the current trace before starting another.
Does a trace tell me exactly what caused the slowdown?
No. It records a timeline for the captured reproduction. You still need to inspect the relevant interval and identify which work is associated with the delay.
Are trace timestamps calendar times?
No. The timestamps in page.metrics() are monotonic elapsed-time values from an arbitrary starting point.
Can I inspect the page without opening DevTools?
Yes. Capture a trace to a file and inspect it afterward in Chrome DevTools or a compatible timeline viewer. Interactive breakpoints require an attached inspector.


