Canvas pixels from the GPU you claim
A GPU string is easy to change; the pixels that GPU would draw are not. The canvas bridge forwards canvas and WebGL operations to a machine that really has the GPU your persona presents and returns its pixels, so a page that draws and reads back gets the answer that GPU would give.
Experimental and opt-in. Most setups don't need it.
A render server, a tunnel, one option
# on the Windows machine with the real GPU
pip install playwright
python tools/canvas-bridge-server/server.py \
--backend local \
--chrome /path/to/clearcote/chrome.exe \
--port 8443Highlights
Operations, not recordings
It forwards the drawing calls, not a library of pre-recorded images, so arbitrary and generated canvases work, not only known probes.
The readbacks that matter
getImageData, toDataURL, readPixels and measureText return the bridge host's real output. Local noise is skipped on that path; the bridge pixels are the ground truth.
Per-site policy
Bridge every site, or only an allow-list of sites where canvas coherence matters, with a deny-list for the rest.
Wait or keep moving
On a cold cache miss, block waits for the bridge and local serves the fast local render instead of stalling.
Seed-aware
The persona seed is sent to the render server, so it can pick a matching GPU profile.
Off unless you set it
Without a bridge URL, rendering stays fully local. If the bridge is unreachable, Clearcote logs a warning and falls back to local rendering.
Why a bridge, and what it costs
Clearcote renders canvas and WebGL on whatever GPU the host actually has. When a persona claims a different GPU, checks that compare rendered pixels with the claimed hardware can see the difference, and no amount of software makes one GPU emit another's exact pixels. The bridge removes the mismatch instead of hiding it: the drawing happens on the GPU being claimed.
The render server is a small Python coordinator on the GPU machine that launches a headless Clearcote and replays the forwarded operations on its real canvas. The client opens the bridge socket from the renderer process, so it needs --no-sandbox, which the SDK adds when you set a bridge URL.
It has real costs. Every readback is a network round trip (with a five-second timeout before the local fallback), so keep the render host on the same LAN or datacenter. And the canvas identity becomes the render host's, not the seed's: every profile that shares one bridge host shares its canvas hash, so run one GPU host per group of identities you want kept apart.
What people use it for
Personas the host can't render
Present a GPU your automation machine doesn't have, with pixels to match.
GPU-less servers
Headless servers that would otherwise draw through a software rasterizer.
Mobile personas
An Android persona on a desktop engine, with rendering from matching hardware.
Clearcote is built for privacy, testing, research and lawful automation. You're responsible for how you use it and for following the terms of the sites you visit.
Which plan includes it
Experimental, and in every tier. You provide the render host: a Windows machine with the GPU you want to present, reachable over a private network.
Compare plansFAQ
Why can't the GPU just be spoofed?
The GPU string can be, and Clearcote does. But a page can also draw something and read the pixels back, and those pixels come from the GPU that did the drawing. Changing the string without changing the pixels is exactly the kind of contradiction checks look for.
What do I need to run it?
A Windows machine with a real GPU to act as the render host (its GPU becomes the canvas identity your profiles present) and a private network path to it, such as Tailscale, WireGuard or an SSH tunnel.
Is it secure?
The bridge speaks plaintext WebSocket, so run it only over a private network or an encrypted tunnel, and never expose the bridge port on the public internet. Use --canvas-bridge-auth credentials that match the server.
Which canvases are bridged?
Canvas 2D and WebGL, including procedurally generated WebGL textures. Image, 2D-canvas, video, ImageBitmap and 3D texture sources fall back to local rendering for that canvas, so they stay correct but aren't bridged.
Go deeper in the docs
Related features
Canvas, WebGL, WebGPU, audio, fonts, screen, hardware and locale set in the C++ engine from one seed, so every surface agrees with every other.
One seed per account: the same device every session, a different one for the next account, with cookies and storage that persist between runs.
Run Clearcote as a standing CDP endpoint from the official Docker image, serve() or a CLI, and attach any framework with no code change.
The TLS handshake and HTTP/2 frames come from Chromium's own network stack, and the version-dependent fields follow the Chrome version your persona claims.
Related reading
Try Clearcote
Open source, a drop-in for Playwright & Puppeteer, and coherent down to the TLS handshake.
Free for one browser with GitHub. No card.