Skip to content
On this page

PatchRight is a browser-automation library that edits Playwright's own Python code before Chrome launches, instead of injecting JavaScript into the page after it loads. Why does that matter? Anti-bot systems like Kasada can ask the browser to show the source of its built-in functions using Function.prototype.toString() — and if they see hand-written JavaScript there, they can tell the function was modified at runtime. PatchRight leaves nothing in the JavaScript to read, because its changes happen at the Python-to-Chrome bridge, below any code the page can inspect. This makes it a common choice for browser automation on JavaScript-heavy sites you are permitted to access, where runtime-injection tools like playwright-stealth leave a detectable signature.

Quick facts

Patch levelPython source (Playwright bindings), pre-Chrome
Why it worksNo JS runtime modifications — Function.toString() finds nothing
Main use caseSites that inspect function source (where runtime-patch signatures are visible)
Drop-in?Mostly — playwright API compatible with minor adjustments
Installpip install patchright

The architectural difference

playwright-stealth hides a bot by injecting JavaScript into the page (via Page.addInitScript) that rewrites built-in browser properties — navigator.webdriver, WebGLRenderingContext.prototype.getParameter, HTMLCanvasElement.toDataURL, and dozens more. The trick works, until an anti-bot script asks to see the source of one of those rewritten functions: Function.prototype.toString.call(navigator.__lookupGetter__("webdriver")). A genuine browser function answers [native code]; a patched one shows the injected JavaScript instead. At that point the patch itself becomes the giveaway.

PatchRight takes a different route. It edits Playwright's Python source so the underlying CDP commands (CDP - the Chrome DevTools Protocol, the channel Playwright uses to steer Chrome) are sent in a way that achieves the same result without ever touching JavaScript. The browser's built-in functions are left exactly as they were, so Function.toString() still returns [native code] — there was no JavaScript patch to expose.

Where PatchRight fits

JavaScript-heavy sites that inspect function source. Because PatchRight avoids runtime JavaScript injection, it does not leave a Function.toString() signature the way runtime patches do. This is the main reason it exists.

Cloudflare with active Turnstile/JS challenges. On sites you are authorized to automate, a consistent browser configuration matters, and PatchRight's source-level approach is one way to keep the function-source surface consistent.

Any case where you have confirmed playwright-stealth leaves a detectable runtime signature. Switching is easy — PatchRight is a near drop-in replacement with very few API changes.

It is not the right tool for everything. It addresses only the function-source surface; it does not change the canvas/WebGL/audio fingerprint layer. For those, CloakBrowser (Chromium) or Camoufox (Firefox) work at the canvas/WebGL/audio layer instead.

What PatchRight does not address

PatchRight covers the toString-detectable surface in Playwright Python. It does not handle:

  • Canvas / WebGL / AudioContext fingerprinting — these read your GPU and hardware. Use CloakBrowser or Camoufox.
  • TLS fingerprinting — TLS is the encryption layer behind https, and it sits below the browser. Whatever TLS signature your Chromium ships with is what sites see.
  • Behavioural detection — input timing and interaction patterns. This is handled by other layers such as Botasaurus.
  • IP reputation — that is the proxy's job. Use residential or ISP static.

Think of PatchRight as one layer in a stack, addressing only the function-source surface.

Code example

python
# PatchRight is a near-drop-in replacement for Playwright Python
# Note the import path change: patchright.sync_api instead of playwright.sync_api

from patchright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    context = browser.new_context(
        proxy={"server": "http://user:pass@residential:port"},
        # ... otherwise identical to Playwright
    )
    page = context.new_page()
    page.goto("https://kasada-protected.com/")
    # PoW solved naturally by real browser execution
    html = page.content()

End of path

That is all 7 entries in Driver and patch tooling.

The ecosystem of patched drivers and browser builds, and what each one actually changes.

Browse all topics

Related terms

Concept map

Concept map

How PatchRight connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Web Scraping APIs
Building map…

Frequently asked questions

Is PatchRight a fork of Playwright?

Not really a fork — it is better described as a patched build of Playwright. It uses the same upstream codebase, with targeted edits to the Python source in the spots where Playwright would otherwise leak a toString-detectable signature. Updates from the official Playwright team still flow through.

How is this different from undetected-chromedriver?

undetected-chromedriver does one focused thing: it removes the navigator.webdriver flag from Selenium-driven Chrome, which is a small patch surface. PatchRight applies the same idea across the much larger Playwright API, where Function.toString() checks are more common. Both have their place; PatchRight covers a wider surface.

Does PatchRight handle CAPTCHAs?

No — it only deals with the function-source layer. CAPTCHAs are handled separately, for example by a solver service or by a real-browser setup that scores well on reCAPTCHA v3.

Should I use PatchRight or Camoufox?

Use PatchRight when you specifically need Chromium and the obstacle is Kasada or playwright-stealth detection. Use Camoufox when you need Firefox (for Cloudflare, or multi-vector coherence with geoip=True) or when the obstacle is canvas/WebGL fingerprinting. They are not mutually exclusive — large scraping operations often run both.

Last updated: 2026-05-31