Open-source project
feder-cr/invisible_playwright avatar
feder-cr/invisible_playwright

invisible_playwright: a stealth Playwright fork with the fingerprint inside Firefox

Undetected Playwright automation in Python. Stealth-patched Firefox, anti-detect browser fingerprint in the engine, not injected. Passes bot detection.

2,978 stars289 forksPythonMIT

At a glance

What is it?
invisible_playwright swaps Playwright's own Firefox build for a patched one and humanizes input in the driver. The two-line switch is real; the 238 MB engine download and the loss of macOS are the price.
Who is it for?
Adopt invisible_playwright if you already run Playwright against sites that score browsers and you can accept a patched Firefox, a one-time engine download, and Windows or Linux only. Do not adopt it if you need Chromium behaviour, macOS, or a browser you can audit as upstream Firefox.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem invisible_playwright is aimed at

Standard Playwright launches a stock browser. Anti-bot systems ask two questions of that browser: whether it is a real browser, and whether a real person is driving it. A default Playwright session answers the first question badly, because the automation layer leaves traces in the page environment, and the second question badly, because the cursor teleports between coordinates.

The project targets engineers doing Python web scraping against sites that score visitors, and anyone who has already been told their traffic is being challenged. The README frames the second half of the problem bluntly: once the browser is handled it stops being the variable, and around 90% of proxies are public, meaning the same address is rented to multiple customers and already sits on blocked-IP lists. That framing is useful because it tells you where the project believes its responsibility ends.

It is not a general browser automation library. It is Playwright with a different engine underneath, which is why the API surface is unchanged.

Fingerprint set in the C++ engine, not injected into the page

The mechanism is the part worth understanding before you install anything. Most stealth tooling patches the page after the browser starts: it overrides navigator properties, rewrites the WebGL vendor string, or wraps canvas methods in JavaScript. That leaves a seam, because the override itself is readable from the page.

invisible_playwright takes the other route. Firefox is patched at the C++ source level, and the fingerprint values for navigator, screen, GPU and WebGL, canvas, fonts, audio, WebRTC, timezone and network are set inside the engine. The README states there is no JS shim, no override, and no seam to read. The same values apply whether you run headless or headed, which matters because a common detection signal is a mismatch between what the browser claims and how it renders.

The second half is the driver. Clicks, hovers and drags follow a natural mouse path with human timing, and each input event is described as byte-identical to a real mouse, with a real input source and trusted events. The engine work is maintained in a separate repository, feder-cr/firefox_antidetect_patch, which the README links for a full breakdown.

Installing invisible_playwright and running a first page

The package is on PyPI. Installation is one pip command, followed by a separate fetch step that downloads the patched engine. The README gives the size as roughly 238 MB downloaded and about 544 MB unpacked, and says the archive is sha256-verified.

bash
pip install invisible-playwright
python -m invisible_playwright fetch      # one-time ~238 MB download (~544 MB unpacked), sha256-verified

After that, the switch from plain Playwright is two lines. The README shows the diff directly: instead of importing sync_playwright and launching p.firefox, you import InvisiblePlaywright and use it as the context manager.

python
from invisible_playwright import InvisiblePlaywright

with InvisiblePlaywright(proxy={"server": "socks5://...", "username": "u", "password": "p"}) as browser:
    page = browser.new_page()
    page.goto("https://example.com")
    page.click("#submit")   # mouse arcs to the button on a Bezier curve

The object returned is a playwright.sync_api.Browser, so every Playwright method works as-is. There is an async variant under invisible_playwright.async_api with the same shape. If you need to reproduce a run, the instance exposes a seed attribute that you can print and pass back in later.

python
with InvisiblePlaywright(seed=42) as browser:
    ...   # same GPU, same canvas hash, same audio context, every run

There is also a CLI. The installed command is invisible-playwright with a hyphen, and python -m invisible_playwright behaves identically. The fetch subcommand downloads the engine if missing and checks every cached copy against the seal; version prints the wrapper, core and engine versions plus the cache location.

Platform support and the macOS removal

Supported platforms are Windows x86_64 and Linux x86_64 and arm64. macOS is no longer supported, and the README is specific about why: releases stopped at firefox-20, and on a Mac the package refuses at launch with a clear message rather than downloading a binary that no longer exists.

That is a better failure mode than a silent broken download, but it is still a hard boundary. If your CI runs on macOS runners, this project is not an option without changing the runner image. The same applies to anyone on Apple silicon who was hoping for a local development loop that matches production.

The Python requirement is 3.11 or newer, from the project metadata. The classifiers list Development Status as Beta, which matches the release cadence: v0.7.2, v0.7.3 and v0.7.4 all landed within August 2026, and the last push to the repository was on 2026-08-27. The version string in pyproject.toml reads 0.15.2, which does not line up with the v0.7.x release tags. Treat the release tags as the thing to pin against and check which one you actually get.

Proxy, timezone and pinned fingerprint fields

Proxy configuration accepts a dict with server, username and password. Supported schemes are socks5, socks4, http and https. DNS is routed through the proxy by default, which the README describes as no local leak. That default is the right one for scraping, but it also means a proxy that cannot resolve names will fail at the DNS step rather than at connect, which changes where you look when something breaks.

Timezone behaviour has two modes. By default it is derived from the egress IP, using the proxy egress when a proxy is set and otherwise the host's own public IP. An explicit IANA zone passed as timezone= always wins, and the README calls that the only way to force a specific zone. If you are matching a proxy in one country to a browser claiming another, the default will fight you.

Pinning is the third control. By default every fingerprint field comes from the seed. The pin argument forces specific values while the rest stays seed-derived, using dotted keys.

python
with InvisiblePlaywright(
    seed=42,
    pin={
        "gpu.renderer": "ANGLE (NVIDIA, NVIDIA GeForce RTX 4090 Direct3D11)",
        "gpu.vendor":   "Google Inc. (NVIDIA)",
        "screen.width":  2560,
        "screen.height": 1440,
        "hardware.concurrency": 16,
    },
) as browser:
    ...

The README points to docs/pinning.md for the full list of pinnable keys and how pinning interacts with what it calls the Bayesian sampler. That interaction is the part I would read before pinning anything: forcing a GPU renderer while the rest of the profile stays random can produce an internally inconsistent machine, which is the opposite of the goal.

Where invisible_playwright is the wrong tool

The project is honest about the browser-versus-IP split, and that honesty is a limitation. If your traffic is challenged because of the address it comes from, no amount of engine patching fixes it. The README says a perfect browser on a known IP still loses. You need a proxy you control, and the project does not supply one.

The second limitation is the engine itself. You are running a patched Firefox build, not upstream Firefox, and not Chromium. Anything that depends on Chromium-specific behaviour, including sites that branch on browser family, will see Firefox. The README argues for Firefox over Chromium in its comparisons documentation, but that is a design position, not a neutral fact.

The third is auditability. The fingerprint lives in C++ inside a downloaded binary. You cannot read it from the page, which is exactly the point, but it also means you cannot inspect it the way you would inspect a JavaScript patch. The fetch step verifies a sha256 seal, so you can confirm the download is the intended one; you cannot confirm from the Python side what the binary does at runtime. For a regulated environment that is likely disqualifying.

Finally, the README does not document a rollback path for the engine. The fetch subcommand checks cached copies against the seal, but there is no described command for reverting to a previous engine version. If a new engine build breaks your target site, the documented recovery is not in the README.

How it differs from Camoufox, Patchright and playwright-stealth

The README's comparisons document names Camoufox, Patchright, nodriver and playwright-stealth. The meaningful axis is where the fingerprint is applied.

playwright-stealth is the oldest pattern in this space: a JavaScript patch applied to the page after load. It is easy to read and easy to modify, and it leaves the override visible to any detector that enumerates property descriptors. That is the seam invisible_playwright is built to avoid.

Patchright patches the Playwright client itself, keeping the browser as the stock build. That means you keep Chromium and the Playwright version you already run, and you inherit whatever the underlying browser leaks. invisible_playwright goes further down, replacing the browser binary, which is why it needs a 238 MB download and why macOS support dropped.

Camoufox is the closest in spirit: it also ships a custom Firefox build with fingerprint values set below the page. The difference is the input layer. invisible_playwright humanizes mouse movement in the driver, with Bezier-curve paths and per-event input metadata, rather than leaving motion to Playwright's default. If your detection problem is behavioural rather than environmental, that is the part that matters.

nodriver takes a different route entirely, driving the browser over the DevTools protocol without the automation layer's usual markers. It does not give you the Playwright API, so migrating to it is a rewrite rather than a two-line change.

Licence, maintenance and upgrade cost

The project declares MIT AND Apache-2.0, not MIT alone. The pyproject.toml comment explains why: src/invisible_playwright/_pw/ is a fork of Playwright's Python client, which is Apache-2.0, and that directory carries Microsoft's copyright headers and is redistributed. Declaring the whole package MIT would misstate what you receive. The project also notes that a second Apache-2.0 directory, _driver/, was removed on 2026-08-28 along with its LICENSE, NOTICE and ThirdPartyNotices.txt, but that _pw/ remains and still carries the obligation. A test file asserts the surviving obligation by name.

For you the practical consequence is that if you vendor or redistribute this package, the Apache-2.0 terms attach to that subdirectory. If you only install it from PyPI and use it, the distinction rarely surfaces. This is not legal advice; read _pw/LICENSE if you plan to ship it inside your own product.

Upgrade cost is dominated by the engine. The wrapper is a small Python package, but the fetch step pulls a large binary that changes independently of the Python version. The README does not describe an engine rollback command, so pinning the wrapper version does not by itself pin the engine. The last push to the repository was on 2026-08-27, and the newest release tag is v0.7.4 from the same date. There is a documented CLI for checking what you have: invisible-playwright version prints wrapper, core and engine versions and the cache path.

Editorial conclusion

Adopt invisible_playwright if you already run Playwright against sites that score browsers and you can accept a patched Firefox, a one-time engine download, and Windows or Linux only. Do not adopt it if you need Chromium behaviour, macOS, or a browser you can audit as upstream Firefox. Before committing, run the fetch step on your target machine and confirm the engine unpacks, check that your proxy scheme is one of socks5, socks4, http or https, and read docs/pinning.md if you depend on exact fingerprint values.

Frequently asked questions

How do I make Playwright undetectable?

The README's position is that a stock Playwright browser fails on two questions: whether it is a real browser, and whether a real person is driving it. invisible_playwright answers both by patching Firefox at the C++ source level so fingerprint values live in the engine rather than in a page-level override, and by humanizing mouse motion in the driver. Its documentation also insists the browser is only half the problem, and that a known proxy IP still loses.

Is Microsoft Playwright free?

The published metadata does not state Playwright's pricing. What it does show is that invisible_playwright forks Playwright's Python client into src/invisible_playwright/_pw/, which is Apache-2.0 licensed and carries Microsoft's copyright headers, while the project's own code is MIT. The package as a whole is declared MIT AND Apache-2.0.

What is Playwright stealth?

In this project's framing, stealth is split into browser identity and driver behaviour. The browser side covers navigator, screen, GPU and WebGL, canvas, fonts, audio, WebRTC, timezone and network values; the driver side covers click, hover and drag paths with human timing. The README contrasts this with tools that patch the page after load, which it says leaves a seam that can be read.

What are some undetectable browsers?

The README only describes one in detail: the patched Firefox engine that invisible_playwright downloads, maintained in feder-cr/firefox_antidetect_patch. It also names Camoufox, Patchright and nodriver in its comparisons documentation as alternative approaches, but it does not present a list of browsers it considers undetectable.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
For maintainers

Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/feder-cr-invisible-playwright.svg)](https://hysenlabs.com/projects/feder-cr-invisible-playwright)
Community notes

Community notes