Puppeteer vs Playwright: choose the library and verify the task

Choose Puppeteer for a focused JavaScript browser script, or Playwright when the job also needs a test runner and checks across browser engines. Both libraries can run in visible browsers and let you encode the steps and checks in a script.
ego(lite) is useful when you want to watch an agent work through the page and take over an uncertain step. In the book-catalogue task below, its named Space kept both source pages open while the agent read titles and prices, visited a detail page and checked the catalogues after reload. Take over and Stop controls let you intervene from the same view.
This guide compares the two libraries and uses an independent ego(lite) run to check their results. All three accepted outputs contain the same forty books and agree across six fields. The library scripts needed separate corrections, and the ego(lite) run retained its own setup issues. The runs and official documentation checks date to October 16, 2026; their different browsers and sessions are documented below.
What are you choosing between?
Puppeteer exposes a JavaScript API for browser control. Its official introduction describes Chrome and Firefox support and explains the difference between the browser-downloading package and puppeteer-core. Check the supported browser version for the package you actually install.
Playwright offers a browser automation library and a separate test-runner workflow. The library guide distinguishes those entry points. The browser APIs are useful in a script; a test project adds a place to organize repeated checks and their execution settings.
| Decision | Puppeteer question | Playwright question |
|---|---|---|
| Browser requirement | Does the supported Chrome or Firefox path cover the task? | Do you need Chromium, Firefox or WebKit coverage? |
| Existing application | Can the team maintain the current Node.js program? | Does the surrounding project already use Playwright? |
| Repeated verification | What assertions, artifacts and runner will you maintain? | Do you need Playwright Test rather than just its library? |
| Agent interaction | Which actual server or adapter exposes the library? | Which actual server or adapter exposes the library? |
The final row is deliberately the same question. An MCP tool returning a successful response does not prove that its downstream browser produced the right record. For a protocol-specific comparison, use the existing Playwright MCP and Puppeteer scraping guide.
Which browsers does the task require?
Browser coverage can decide the choice before code style matters. Playwright's browser documentation covers Chromium, Firefox and WebKit as well as supported branded browser channels. That matters when the acceptance contract names multiple engines.
Puppeteer's supported browser list maps releases to browser versions. Do not repeat an old claim that Puppeteer only operates Chrome without checking the supported Firefox path. Conversely, Firefox support does not imply WebKit support or identical behavior across all engines.
For a same-task Chrome demonstration, record the executable and full build used by each route. If both packages support the same installed build, sharing it removes one source of variation. If they need different supported builds, retain that difference in the result rather than forcing a configuration neither project recommends.
A passing Chrome example proves only that example. It does not establish Safari coverage, a mobile-device result or an advantage on an engine that was never run.
How do waiting and locator choices differ?
Puppeteer's page-interaction guide recommends locators and documents checks before interaction. It also exposes lower-level element handles and selector APIs. A comparison that uses current Playwright locators against deliberately brittle Puppeteer coordinates is comparing implementations as well as libraries.
Playwright's locator guide describes queries that resolve against current page state. Its locator syntax and Puppeteer's selector syntax are different APIs. Copying a getByRole call into Puppeteer, or copying a Puppeteer pseudo-selector into Playwright, does not preserve the same program.
These illustrative snippets show the difference without claiming to have executed either route:
// Puppeteer: a visible link with a known accessible name.
await page.locator('::-p-aria(Next)').click();// Playwright: the corresponding role and exact name.
await page.getByRole('link', { name: 'Next', exact: true }).click();The surrounding page must actually expose a unique matching link. When a result list contains several similar titles, scope the action to the intended card and verify its identity. Neither snippet can decide which business record the task meant.
For list replacement, duplicate names and frame boundaries, see stable Playwright locators. Keep those mechanics separate from the library-selection decision here.
When does a script need a testing workflow?
A browser script is often enough for a bounded job with a known output and an owner who can inspect failures. A repeated release check needs more structure: reset state, assertions, failure artifacts, execution limits and a clear decision about what stops the build.
Playwright Test is relevant when those recurring checks belong together in a test project. Using the Playwright library alone does not automatically provide the project's complete test workflow. A Puppeteer application can also use a separate runner and assertions; the team then owns how those pieces fit together.
| Work you must maintain | Evidence to inspect before choosing |
|---|---|
| Browser startup | Exact package, executable, launch arguments and profile ownership |
| Page readiness | An application condition, not an arbitrary sleep |
| Correct output | Parsed records checked against the source |
| Failure diagnosis | Original error, relevant state and preserved artifacts |
| Repetition | Reset procedure and retained failed attempts |
| Maintenance | A reviewer who understands the script and its acceptance checks |
Migration is justified when it addresses a real requirement. Replacing a working library while preserving the same wrong target and missing verification does not solve the task.
How should you compare the same real task?
A small catalogue task can reveal setup, navigation and output-association differences without purchasing anything. The October 16 runs read Books to Scrape page one and page two, then visits the first collected detail link.
Both routes used a headed browser, a fresh isolated context, a 1280 × 800 viewport, en-US locale and sequential navigation. Readiness checked document completion and existing card fields. Puppeteer saved its output without reading other results. Playwright read Puppeteer environment metadata first, then saved its independent records before comparing the outputs. Both observed twenty cards per page.
Each record should keep these fields:
{
"title": "Full title read from the source",
"price_raw": "Original currency string",
"availability_raw": "Original availability text",
"rating_raw": "Original rating representation",
"detail_url": "Absolute book detail URL",
"source_url": "Actual catalogue page URL"
}The card title may be visually shortened. Read its full source value and preserve the original strings before any normalization. Resolve the detail link against its source page, and check that the detail page describes that same book.
A useful acceptance contract includes both pages, unique detail URLs, required fields, ten distributed source checks and a detail-page title/price check. Return to the catalogue and reload to verify the observed source again. Report duplicates and missing records before deduplicating or repairing anything.
For a controlled stopping check, search for an impossible exact title containing the run ID. Record zero matches and issue no click. The absent title is a deliberate guard, not evidence that either product failed naturally.
The unchanged first script, raw fields, normalized JSON, logs and later corrections should remain separate. If a launch problem occurs, preserve it even when the corrected attempt succeeds. A clean result without its discarded setup failure tells only part of the maintenance story.
What did the independent catalogue runs verify?
The accepted Puppeteer correction and Playwright attempt 02 each returned forty records with forty unique absolute detail URLs. A later independent ego(lite) run also collected forty records before opening either library result. Rechecking all three saved outputs by detail URL found no missing identities or disagreements across the six requested fields. This comparison checks the delivered records; it does not establish which library is generally more accurate.
| Observation | Puppeteer | Playwright | ego(lite) reference |
|---|---|---|---|
| Recorded package / runtime | Puppeteer 25.14.0 | Playwright 1.64.1 | ego / helper 0.6.0.5; skill 2.1.0 |
| Recorded browser | Chrome for Testing 155.0.8059.79 | Chrome for Testing 156.0.8078.4 | Chromium 154.0.8037.44 |
| Viewport and session | 1280 × 800, DPR 1; fresh isolated context | 1280 × 800, DPR 1; fresh isolated context | 888 × 797, DPR 2; named Space in existing session |
| Observed source cards | 20 + 20; 40 unique detail URLs | 20 + 20; 40 unique detail URLs | 20 + 20; 40 unique detail URLs |
| Source checks | 10 distributed cards, repeated after reload | 10 distributed cards; 37 recorded checks passed | 12 distributed cards; all checks passed |
| Detail and reload | First title and price agree; all 40 records agree after reload | First title and price agree; all 40 records agree after reload | First title and price agree; both pages reloaded and source checks passed |
| Attempts and retries | 2 manual attempts; 0 automatic application retries | 2 manual attempts; 0 automatic application retries | 1 reference workflow; 0 source-operation retries |
| Absent exact title | 0 matches on both pages; 0 clicks | 0 matches on both pages; 0 clicks | 0 matches on both pages; 0 clicks |
The first detail page was A Light in the Attic at £51.77. Each route checked its title and price against the saved catalogue card. The distributed checks covered card positions 1, 5, 10, 15 and 20 on each page. Both corrected workflows returned to the catalogue and reloaded both requested URLs, then compared all forty saved records with the source again.
Puppeteer: complete values behind the table
The Puppeteer viewer keeps full title attributes, raw prices, availability strings, rating classes and resolved links. For example, the first saved rating remains “star-rating Three”. Its separately saved raw fields retain shortened display text and source card HTML, so a reviewer can check how normalization produced the six-field record. The local viewer was also checked against the saved JSON.

Playwright: independently saved, then compared
Playwright saved its accepted result at 06:20:34.948 UTC before reading Puppeteer records. Only Puppeteer runtime settings had been read beforehand. The later comparison joined forty detail URLs and found zero missing records in either direction and zero field disagreements. The recorded hashes of the normalized files match, and a fresh read-only comparison for this article confirmed the same forty-record agreement.

What failed before the corrected runs passed?
Both workflows needed a separate correction. The original attempts and scripts were preserved, so the accepted result can be read alongside the actual setup and verification friction. Neither path used an automatic application retry.
Puppeteer: a reload assertion rejected cached content
The first Puppeteer script collected forty cards and passed the first detail check, then stopped on the first catalogue reload with HTTP 304. The saved run attributes that stop to an assertion using HTTPResponse.ok(). The source content was available through the browser cache; the assertion treated the reload response as unsuccessful.
Puppeteer documents HTTPResponse.ok() as a check for status codes from 200 through 299. The correction disabled page HTTP cache before navigation and ran a separately frozen script in another fresh browser and context. All requested return and reload comparisons then passed. This fix changes cache behavior; it does not show that cached content is inherently unusable.

Setup also required approving Puppeteer’s postinstall under the local npm policy and checking its installed APIs before freezing the script. The corrected run retained seven mixed-content console messages for the site’s insecure jQuery resource. Source-field checks passed without bypassing browser security.
Playwright: metadata collection needed a launch flag
The first Playwright attempt stopped before visiting the catalogue. Its Browser.getBrowserCommandLine metadata query was rejected because --enable-automation was absent. A separate attempt added that launch flag, which was also present in the recorded Puppeteer environment. The accepted run then passed all 37 recorded checks. This failure belongs to the run’s browser-metadata collection path.

Playwright used its installed Chrome for Testing 156.0.8078.4 instead of forcing Puppeteer’s older 155.0.8059.79 build. Its report retained nine mixed-content request failures and a setup working-directory invocation that never launched. The shared viewport, locale, sequential work and disabled cache were recorded, while the browser difference remains visible in the comparison.
Which measurements are comparable?
Elapsed time needs a start and finish. Separate installation, browser startup, script authoring, actual navigation, source checks and artifact packaging. Reasoning time outside a frozen script is not part of that script's execution interval, but it still contributes to the work required to prepare the example.
If an agent chooses operations at runtime, report its reasoning-inclusive task duration separately from a browser-call subtotal. Do not compare that full task duration with one library's navigation-only measurement.
One accepted run is a functional observation. A broader speed or reliability conclusion needs repeated runs, retained failures, declared resets and matching boundaries. Keep the sample and browser differences beside any result table.
The corrected Puppeteer execution took 16.344 seconds; its failed original took 7.700 seconds. The accepted Playwright execution took 24.030 seconds. Both measured intervals began in the script body before browser launch and included source verification, screenshots and artifact writes. Playwright also included trace saving and additional validation work. Imports, Node startup, setup, authoring and viewer packaging were outside those execution intervals.
These are recorded durations with different work and browser builds. They are useful for understanding what each run included, and do not support a faster-library conclusion. Puppeteer separately recorded 54.762 seconds of setup and 53.631 seconds of first-script authoring. Playwright recorded 103.785 seconds of initial setup after its run-directory creation; earlier discovery was unmeasured. Those setup boundaries also differ.
Where does ego(lite) fit?
A visible ego(lite) Space is useful when a person needs to inspect the source, follow the agent's decisions or take over an uncertain step. Its browser connection and Space controls provide a separate review route around the same task.
The independent reference used the installed ego-browser helper in a visible Space named Puppeteer and Playwright comparison: ego. All source navigation, DOM reads, detail checks and reloads went through that route. The dataset and validation checkpoint were saved at 06:36:28.052 UTC before either library output was opened. This preserved an independent source observation for the later comparison.

The reference observed twenty cards per page and forty unique detail URLs. It checked A Light in the Attic at £51.77, returned to the catalogues and reloaded both pages. Twelve distributed checks covered card positions 1, 4, 8, 12, 16 and 20 on each page. The exact impossible title had zero matches on both pages and received no click. No Add to basket action was used.
After saving independently, the reference compared each available library output by absolute detail URL. Each comparison matched forty identities and 240 field values, with no missing records or disagreements. Original outputs were preserved. A separate read-only check for this article confirmed the same six-field agreement across the three accepted datasets.

The live helper reported ego 0.6.0.5 with Chromium 154.0.8037.44 and skill guide 2.1.0. The installed application bundle on disk was 0.6.0.12; no upgrade or restart occurred. The source viewport was 888 × 797 CSS pixels at DPR 2. The workflow used an existing visible session in a dedicated Space, while the two libraries used fresh isolated contexts at 1280 × 800 and DPR 1. Cache and locale were not forced in ego(lite).
Setup exposed a stale bootstrap path with an extra directory component. The controller located the installed packaged skill and loaded its guide without changing the version link. A Browser.getVersion protocol query was unavailable, so the report used the helper version output and observed user agent. These metadata issues were retained; source operations were not replayed and no human intervention was recorded.
The observed reference interval was 260.785 seconds from its first recorded task clock through final packaging, including setup, reasoning, browser work, comparison and evidence creation. An instrumented subtotal covered 23 helper calls totaling 3.683 seconds, with other browser operations outside that instrumented set. Complete browser-operation time and exact full-task duration remain unknown. Neither number can be ranked against the frozen library-script intervals.
The native Space and actual controller establish how that reference was collected. A locally generated result table only summarizes saved data. An ego run does not certify Puppeteer or Playwright configuration, and it does not replace a maintained CI test.
FAQ
The following questions address decisions that affect a real migration or new browser task.
Is Puppeteer limited to Chrome?
Puppeteer's current documentation includes Chrome and Firefox. Check the supported version and protocol for the release being installed instead of relying on a historical comparison.
Is Playwright the same thing as Playwright Test?
Playwright's browser library and its test runner are different entry points. Compare library with library, then account for the runner and reporting work your project needs.
Does Puppeteer have automatic waiting?
Puppeteer locators document interaction preconditions and waiting. Lower-level handles have different responsibilities, so identify the API used before comparing behavior.
Can both libraries use the same Chrome build?
They may be able to use a supported shared executable. Verify support for the actual package versions and retain any mismatch rather than assuming arbitrary builds are interchangeable.
Should a working Puppeteer script be rewritten?
A rewrite should satisfy a concrete requirement such as browser coverage or a maintained test-project workflow. First check whether the existing failure comes from a wrong target or an incomplete result check.
Does a successful click prove the task finished?
A dispatched action is only one observation. Verify the resulting application record, source identity and required state independently.
Is either library always faster?
A universal ranking does not follow from a single task. Browser builds, workloads, startup state and measurement boundaries can change the observed result.
Where should an AI agent enter the workflow?
Use runtime interpretation where the next step is genuinely uncertain and keep a visible review path. Move a stable repeated acceptance check into maintained code when that is the requirement.
What should you do next?
Choose the required browser and the operating workflow first, then run the same acceptance contract through the actual library. Keep original output and failure evidence before comparing results.
For scraping-specific protocol choices, continue with Playwright MCP vs Puppeteer. For repeated test behavior, use the Playwright locator guide. When the next step is a supervised source review, download ego(lite) and keep the live page in a dedicated Space.


