Menu
Sign up

Playwright vs Puppeteer: the same jobs timed on one machine, and what actually differs

Playwright 1.64 vs Puppeteer 25.7: the same screenshot, PDF and element jobs timed on one machine, plus headless modes, browsers, languages and waiting.

Published November 8, 2026 · 4 min read

Playwright and Puppeteer are the two libraries people choose between to drive a browser from code, and for everyday jobs their code looks almost the same. So instead of a feature checklist, we ran the same jobs in both on one machine and compared the numbers, then went through the differences that actually decide the choice.

How we measured. On 2026-11-08: Puppeteer 25.7.0 in the official ghcr.io/puppeteer/puppeteer image (Chrome 152.0.7977.42) and Playwright 1.64.1 in the official mcr.microsoft.com/playwright:v1.64.1-noble image (Chromium 156.0.8078.80), on the same computer — an AMD Ryzen 9 7950X3D under WSL2 — against the same local test page. Each run: five cold launches, then ten rounds on one browser of open the page → viewport screenshot → full-page screenshot → A4 PDF → element screenshot; the numbers are medians, from three runs each. The machine was shared with other work (load average 10–66 during the runs), so read differences under about 20 % as noise. Browser versions differ too: each library ships the build it is tested with.

The numbers

Median, ms Puppeteer (default: new headless) Puppeteer (headless: 'shell') Playwright (default: headless shell) Playwright (channel: 'chromium')
Launch the browser 246–315 118–137 43–61 90–98
Open page (load) 30–31 23–30 17–20 26–28
Viewport screenshot 66–72 60–61 48–50 46–56
Full-page screenshot 129–142 96–113 113–128 115–120
A4 PDF 34–38 13–16 6–8 22–23
Element screenshot 70–73 57–68 87–93 84–85
Whole job 324–365 257–280 272–291 287–304

What the table says:

  • Launch time is where they differ — and it is mostly the headless mode, not the library. Playwright's default for headless Chromium is the separate chromium-headless-shell build, as its browsers page explains; Puppeteer's default is Chrome's new headless mode, which is the full browser. Put Puppeteer on the shell (headless: 'shell') and its launch fell from ~300 to ~130 ms; put Playwright on the full browser (channel: 'chromium') and its launch rose from ~50 to ~95 ms.
  • Per job, they are close. With the same kind of headless browser, a whole job took 257–280 ms (Puppeteer shell) and 272–291 ms (Playwright shell). Playwright was faster at viewport screenshots and PDFs, Puppeteer at full-page and element screenshots.
  • If speed matters, reuse the browser. A launch costs as much as a job or more; both libraries let you keep one browser open and open pages in it. Our speed guide measured that pattern in detail.

Outputs were essentially the same size: a 13.7 KB and a 13.2 KB viewport PNG, 29.7 and 29.2 KB full pages, 28.8 and 22.8 KB PDFs.

The code, side by side

// Puppeteer
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 800 });
await page.goto(url, { waitUntil: 'load' });
await page.screenshot({ path: 'page.png', fullPage: true });
await (await page.$('ul')).screenshot({ path: 'list.png' });
await browser.close();
// Playwright
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
await page.goto(url, { waitUntil: 'load' });
await page.screenshot({ path: 'page.png', fullPage: true });
await page.locator('ul').screenshot({ path: 'list.png' });
await browser.close();

These are the scripts we timed, minus the timers. Moving a script from one to the other is mostly renaming:

Puppeteer Playwright
puppeteer.launch() chromium.launch() (or firefox, webkit)
page.setViewport({ width, height }) browser.newPage({ viewport: { width, height } })
page.$(selector) / page.$$(selector) page.locator(selector)
page.waitForSelector(selector) page.locator(selector).waitFor()
page.evaluate(fn) page.evaluate(fn)

The waiting model is the one change that needs thought: Playwright's locators wait on every action, so explicit waits copied over from Puppeteer can often be deleted.

What actually differs

Puppeteer Playwright
Languages JavaScript / TypeScript JavaScript / TypeScript, Python, Java, .NET
Browsers Chrome, Firefox Chromium, Firefox, WebKit
Default headless Chromium New headless (full Chrome) chromium-headless-shell
Waiting waitForSelector, locators that wait, waitUntil Auto-waiting actions and locators, waitUntil
Testing Bring your own test runner Playwright Test: runner, parallelism, toHaveScreenshot
  • Languages. Playwright's docs: it "is available in multiple languages that share the same underlying implementation", with all core features in each. Puppeteer is JavaScript only; in Python people use Playwright or the unofficial pyppeteer (Python screenshots).
  • Browsers. WebKit — the engine behind Safari — is a common reason to pick Playwright: of the two, only it gets close to how a page renders in Safari. Puppeteer drives Firefox over WebDriver BiDi, with some features missing (Puppeteer tutorial).
  • Waiting. Playwright's auto-waiting runs "a range of actionability checks on the elements before making actions" and fails with a TimeoutError if they do not pass. Puppeteer's locators wait too, but most Puppeteer code still waits explicitly. Neither knows when your data has arrived; see waiting for the page in Puppeteer.
  • Testing. Playwright Test, with screenshot comparison built in, is the strongest argument for Playwright in a test suite (visual regression).

Which to pick

  • Pick Puppeteer if you work in Node.js, need only Chrome (screenshots, PDFs, scraping), and want the library maintained by the Chrome team. Use headless: 'shell' when start-up time matters.
  • Pick Playwright if you need Python, Java or .NET, need WebKit or all three engines, or are writing tests and want the runner and screenshot assertions.
  • Keep what you have if a working script already does the job: our numbers give no reason to rewrite it for speed.

When to use neither

Both are libraries: you also run the browsers — containers, fonts, memory, crashes, updates. If the job is screenshots of public pages or images from HTML, our screenshot API and HTML templates do it in one HTTP request, from any language, with cookie banners hidden and full-page captures handled. For tests, pages behind a login and local development, Playwright or Puppeteer on your own machine remain the right tools.

FAQ

Is Playwright faster than Puppeteer?

For the work itself, barely: on our machine one job (load, screenshot, full page, PDF, element) took 272–304 ms in Playwright and 257–365 ms in Puppeteer. The big difference was launch time, and it came mostly from the default headless mode: Playwright starts the lighter chrome-headless-shell by default (43–61 ms), Puppeteer the full Chrome (246–315 ms), or 118–137 ms with headless: 'shell'.

What is the main difference between Playwright and Puppeteer?

Playwright drives Chromium, Firefox and WebKit from JavaScript, Python, Java and .NET, and comes with a test runner with auto-waiting and screenshot assertions. Puppeteer is a JavaScript library from the Chrome team for Chrome and Firefox. For browser automation in Node.js both do the same jobs with very similar code.

Should I switch from Puppeteer to Playwright?

Only for a reason: you need WebKit (Safari's engine), Python or Java, or Playwright's test runner with screenshot comparison. For an existing Puppeteer script that takes screenshots or PDFs in Chrome, a rewrite gains little.

Which headless mode do Playwright and Puppeteer use?

Different ones by default. Puppeteer runs Chrome's new headless mode (the full browser) unless you pass headless: 'shell'. Playwright runs chromium-headless-shell for headless Chromium unless you choose the 'chromium' channel, which is the new headless mode.