Anti-Detect Browser that passes every bot detection test. Drop-in Playwright replacement. https://github.com/feder-cr/invisible_playwright#why-its-powerful
Find a file
2026-05-15 20:01:56 -07:00
docs feat: initial public release 2026-05-12 22:32:58 -07:00
examples feat: initial public release 2026-05-12 22:32:58 -07:00
scripts feat: initial public release 2026-05-12 22:32:58 -07:00
src/invisible_playwright feat: initial public release 2026-05-12 22:32:58 -07:00
tests tests: add/update tests/test_pin.py 2026-05-15 20:01:56 -07:00
.gitignore feat: initial public release 2026-05-12 22:32:58 -07:00
LICENSE feat: initial public release 2026-05-12 22:32:58 -07:00
pyproject.toml feat: initial public release 2026-05-12 22:32:58 -07:00
README.md docs: tighten technical claims (mDNS, Chromium open-source layers, competitor list, font metrics) 2026-05-15 19:52:25 -07:00

invisible_playwright

License: MIT Python 3.11+ Firefox 150.0.1 GitHub release GitHub stars

A patched Firefox 100% Playwright-compatible that passes the hardest browser-fingerprint detectors in the wild.

Results

Google reCAPTCHA v3 - 0.90 / 1.0

Top-tier score. Google classifies the session as "very likely a human". Most anti-detect stacks plateau around 0.3-0.7.

reCAPTCHA score 0.90

Fingerprint Pro - bot: not detected, VPN: false, tampering: false, dev tools: not detected

FingerprintJS Pro's full Smart Signals battery flips every flag to "Not detected". Browser correctly identified as Firefox 150 on Windows 10. Confidence score 0.9.

FingerprintPro not detected

CreepJS - 0 lies, fingerprint is internally coherent

No contradictions between headless hints, spoofed values, and real rendering output. That "0 lies" is what kills most anti-detect browsers: one inconsistency (e.g. Chrome UA + Firefox WebGL) and the trust score collapses.

CreepJS 0 lies

BrowserLeaks WebRTC - no public IP leak

WebRTC srflx address is the proxy egress IP; host candidates are private LAN. The real public IP never leaks via STUN, even on pages that configure their own ICE servers. Stock Firefox exposes an mDNS hostname (e.g. abc-1234.local) as a host ICE candidate, which is itself a stable per-session signal detectors fingerprint. invisible_playwright replaces host candidates with synthetic private-LAN IPs that match the spoofed network, removing the mDNS tell.

WebRTC no leaks

bot.sannysoft.com - all checks pass

Every row green: WebDriver not present, Chrome-only properties absent, plugin/mime/languages arrays coherent, permissions API correct, iframe/source window checks pass.

Sannysoft all green


Why it's powerful

Most anti-detect browsers patch Chromium at the JavaScript level - they override navigator, WebGLRenderingContext.getParameter, canvas APIs, and so on via injected scripts. This has two fatal problems:

  1. JS patches are detectable. Anti-bots enumerate native function .toString(), check descriptor configurability, compare property enumeration order, watch for prototype mutations. Every patch leaves a fingerprint of its own. CreepJS has an entire battery of "lies detectors" built around this.
  2. Chromium itself is now suspect. Residential-proxy bot traffic is overwhelmingly Chromium-based, so detectors weight anything Chromium-shaped as risky by default. Chromium-based forks inherit Chrome's open-source layers (BoringSSL, Blink, V8, ANGLE) cleanly, but they still cannot fully match Chrome in practice: Chrome ships closed-source components on top (Widevine, proprietary codecs, Google Update / Safe Browsing endpoints) that flip detectable JS feature flags and network signals, and forks lag Chrome's release cadence by days to weeks, leaving telltale version-specific behaviours that detectors lock onto.

invisible_playwright patches Firefox at the C++ level. The spoofed values come back out through the normal Gecko paths - there is no JS shim, no override, no Object.defineProperty. From the page's point of view, the browser is just telling the truth. Anti-bot lie-detectors have nothing to latch onto.

invisible_playwright spoofs all the layers that matter, together, coherently:

Layer What we do Why it matters
Navigator / hardware C++ overrides: UA, oscpu, languages, hardwareConcurrency, deviceMemory, storage quota Self-description coherent across every API
Screen / window / pointer C++ patch: screen WxH, outerSize bound, media-query device-size, pointer/hover/touch capabilities screen.*, window.outer*, CSS @media (pointer: fine) all coherent
CSS system colors 40 ui.* Win32 palette overrides getComputedStyle() on system colors matches real Windows
GPU / WebGL C++ patch: vendor, renderer, extensions whitelist, integer/float params, shader precisions, readPixels noise Matches real Windows ANGLE down to enum values
Canvas 2D C++ patch: per-pixel substitution + geometry skip-mask noise + TextMetrics variance Defeats canvas hashing and text-metrics fingerprinting
Fonts / DirectWrite C++ patch: family whitelist + fabricated authoritative list + per-family width scale + DWrite settings Font enumeration matches real Win10; canvas text hash stable
Audio C++ patch: sampleRate + output latency + max channels + AnalyserNode/DynamicsCompressor noise AudioContext fingerprints bucket users very tightly
Speech synthesis C++ patch: fabricated voices list navigator.speechSynthesis.getVoices() matches the spoofed OS
WebRTC C++ patch (nICEr): srflx address swap + synthetic srflx fallback + private-LAN host candidates Real public IP never leaks via STUN
Timezone C++ patch: per-Realm TZ via BrowsingContext (no IPC pref races) Date.getTimezoneOffset(), Intl.DateTimeFormat match the spoofed location
DevTools detection C++ patch: Debugger.stealthMode + Juggler Runtime.js + thread actor FP Pro developer_tools = Not detected even with debugger attached
SOCKS5 auth C++ patch Stock Playwright+Firefox cannot negotiate it at all
DNS Routed through SOCKS proxy by default No DNS leak when using a residential gateway
Mouse motion Bezier curves inside Juggler PageHandler.js, ~10 ms per waypoint Even page.click(selector) moves like a human
GPU on virtual desktop Pref-driven workaround for FF150 alt-desktop sandbox regression WebGL renderer populated even in headless / multi-worker mass tests
Fission navigation C++ patch: nsDocShell + CanonicalBrowsingContext Juggler navigation fix page.goto() reliable on FF150 across proxy edge cases
about:newtab race Async wrapper sleep around new_page() No "Navigation interrupted by about:newtab" on FF150
Proxy reliability Juggler PageHandler.equalsExceptRef split try/catch No spurious "Invalid url" with proxies like Evomi

Everything is driven by preferences - no hardcoded values in the binary. You change one pref, you change the spoofed value.


How it compares

Commercial anti-detect browsers (Multilogin Mimic, GoLogin Orbita, AdsPower, Dolphin Anty) ship patched Chromium and apply most spoofing at the JavaScript layer. A few (Kameleo, Multilogin Stealthfox) also offer Firefox-based profiles, but the spoofing pattern is the same: runtime overrides on top of an unmodified rendering engine. That's the ceiling - and it's a low one.

invisible_playwright Multilogin / GoLogin AdsPower / Dolphin Kameleo
Engine Firefox (open source) Chromium fork Chromium fork Chromium
Patch depth C++ source JS overrides JS overrides JS overrides
.toString() clean Native Gecko path Detectable shims Detectable shims Detectable shims
Canvas / WebGL C++ level ⚠️ JS override ⚠️ JS override ⚠️ JS override
SOCKS5 auth Patched ⚠️ Varies ⚠️ Varies
Self-hosted SaaS SaaS Cloud
reCAPTCHA v3 score 0.90 ~0.3-0.6 ~0.3-0.5 ~0.3-0.5
FP Pro - bot detected Not detected Detected Detected Detected
FP Pro - tampering Not detected Detected Detected Detected
FP Pro - VPN flag false true true true
CreepJS lies 0 multiple multiple multiple

Competitor scores reflect our own testing on Windows 10 against the same five detection suites used above; results may vary with their evolving builds.


Install

pip install invisible-playwright
python -m invisible_playwright fetch      # one-time ~100 MB download, SHA256-verified

Supported platforms: Windows x86_64, Linux x86_64.


Usage

Random fingerprint per session

100% Playwright-compatible - sync and async, all methods, zero API changes. If you already use Playwright, switching is two lines:

- from playwright.sync_api import sync_playwright
- with sync_playwright() as p:
-     browser = p.firefox.launch()
+ from invisible_playwright import InvisiblePlaywright
+ with InvisiblePlaywright() as browser:

Every session gets a unique, coherent fingerprint drawn from real-world Firefox telemetry (GPU / audio / fonts / ~400 other fields) and Bezier-curve mouse motion baked into the browser itself.

Sync

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

Async

from invisible_playwright.async_api import InvisiblePlaywright

async with InvisiblePlaywright(proxy={"server": "socks5://...", "username": "u", "password": "p"}) as browser:
    page = await browser.new_page()
    await page.goto("https://example.com")
    await page.click("#submit")

The browser object is a playwright.sync_api.Browser / playwright.async_api.Browser - every Playwright method works as-is.


Random fingerprint per session

from invisible_playwright import InvisiblePlaywright

with InvisiblePlaywright() as browser:
    page = browser.new_page()
    page.goto("https://creepjs-api.web.app")

Every call samples a new coherent profile. Log the seed to reproduce interesting runs:

sf = InvisiblePlaywright()
with sf as browser:
    print("seed =", sf.seed)
    # ...

Reproducible fingerprint

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

Proxies

proxy = {
    "server": "socks5://gate.example.com:1080",
    "username": "user",
    "password": "pass",
}
with InvisiblePlaywright(proxy=proxy) as browser:
    ...

Schemes supported: socks5, socks4, http, https. Auth works on all of them (SOCKS5 via patched nsProtocolProxyService.cpp, HTTP/HTTPS via Playwright). DNS is routed through the proxy by default, no local leak.

Pinning specific fingerprint fields

By default everything comes from seed. To force specific values while the rest stays seed-derived:

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:
    ...

Full list of pinnable keys, how pinning interacts with the Bayesian sampler, and common patterns are in docs/pinning.md.


CLI

invisible_playwright fetch          # download the binary if missing
invisible_playwright path           # print the absolute path to the cached binary
invisible_playwright version        # wrapper and binary versions
invisible_playwright clear-cache    # remove all cached binaries

License

MIT - see LICENSE. The patched Firefox binary is distributed under the MPL-2.0 (Firefox upstream license). The C++ patches against mozilla-central that produce that binary are at feder-cr/firefox-stealth.