GPU DETECTION
Detect WebGPU adapter, features, limits
Check if your GPU and WebAssembly runtime expose hardware fingerprints that can track you across sessions
Hardware fingerprint detection process
Detect WebGPU adapter, features, limits
Run GPU compute shaders, measure timing
Execute WASM benchmarks, detect engine
Compare signals with browser claims
User-Agent string lies. Canvas hashes get randomized. WebGL renderer reports get spoofed. Anti-detect browsers built a whole industry on lying convincingly to JavaScript. The strategy worked, mostly, until 2023. In 2023 Chrome shipped WebGPU to default channels. WebGPU exposes the GPU adapter's vendor, architecture, feature set, and resource limits, and lets the page run actual compute shaders against it. Around the same time, browser fingerprinting research moved past "what does the browser claim" toward "what does the math say." A WASM module compiled in V8 takes a different number of milliseconds than one in SpiderMonkey, regardless of what the User-Agent string reports. The JavaScript engine doesn't choose its own performance characteristics; the engine itself is the fingerprint. Sections below: what WebGPU actually exposes, why WASM timing reveals the JS engine across spoofing layers, and how cross-checking these signals catches anti-detect browsers in the act.
For two decades, WebGL gave pages a tightly-controlled hook into the graphics card. Renderer string. Vendor. A handful of extensions. The browser's privacy team got enough years to think about the surface and scrubbed most of it: anti-fingerprint extensions could intercept `WEBGL_debug_renderer_info` cleanly, the spec authors moved sensitive bits behind a worker-only flag, and Tor Browser stripped the lot. WebGPU shipped in 2023 with the opposite design philosophy: maximum honesty. The page calls `navigator.gpu.requestAdapter()`, gets an adapter object, reads: - `vendor`: Intel, NVIDIA, AMD, Apple, Microsoft (for software fallback) - `architecture`: a string like `"ampere"` or `"rdna-3"` or `"apple-m2"` - `device`: the specific chip family - `limits`: roughly 30 numeric values (max texture size, max workgroup count, max bind groups) - `features`: a Set of optional capabilities (timestamp-query, shader-f16, depth-clip-control) That `limits` dict alone is genuinely identifying. An RTX 3070 has different `maxStorageBufferBindingSize` than an RTX 4080 than an Apple M2 GPU. The values aren't user-configurable, aren't intended to vary, and don't change between sessions on the same machine. Combine the limits hash with the features Set and most consumer GPUs land in cohorts of dozens, not millions. Then there's compute. WebGPU lets the page submit a compute shader and time its execution. Run a 1024×1024 matrix multiply. Read back the timing. The distribution is GPU-specific. Memory bandwidth, ALU count, clock speed, thermal throttling state, all of it goes into a single millisecond number that's surprisingly stable per device. Browser privacy teams haven't fully written this story yet. The surface ships, the fingerprinting research lags by a year or two. Same gap as new pharmaceuticals running ahead of long-term studies: the molecule is already in pharmacies, the population data still to come.
JavaScript engines are huge codebases optimized for different things. V8 prioritizes throughput on long-running scripts. SpiderMonkey leans into faster compile-tier-up. JSC (Safari) does heavy upfront type analysis. Three philosophies, three timing fingerprints. Same standard, three different tempos. The cleanest signal: JS-to-WASM call overhead. Calling a WASM function from JavaScript requires a bridge across the engine boundary. V8's bridge is tuned for monomorphic call sites and runs in roughly 50 nanoseconds when warm. SpiderMonkey's bridge is closer to 400 nanoseconds. JSC sits between the two but with higher variance. Run 10,000 calls, measure the total, divide. The number tells you the engine without ever asking the browser. Compile/validate ratio is another tell. Each engine has its own balance between baseline compilation (fast, slow execution) and tier-up to optimized code. V8's Liftoff completes in 10ms for a 1MB module. SpiderMonkey's BaselineCompiler takes around 25ms. JSC's interpreter takes longer but skips the compile step entirely. Memory.grow latency makes a third channel. V8 mmaps a virtual region upfront and grows by remapping. SpiderMonkey allocates a fresh chunk and copies. The latency difference at 4MB to 8MB grow is roughly 12ms versus 200μs. Pick the engine just from that. Why this matters for fingerprinting: every commercial anti-detect browser fakes User-Agent, fakes navigator properties, fakes canvas output. Some fake WebGL renderer. None of them, as of 2026, can fake JS-to-WASM timing. Doing so would require shipping a custom V8 build with intentional slowdowns, and they all use Chromium directly. So if a "Firefox on Mac" identity reports V8-shaped WASM timing, the spoof is visible in 100ms of measurement.
Single-signal fingerprinting is brittle. A noise-injection extension can muddy any one channel. Cross-checking is what makes the test useful: for an honest browser, every signal agrees. For a spoofed one, at least one signal contradicts the others. Concrete checks worth running: - WebGPU vendor versus WebGL vendor. Both APIs query the GPU through different driver paths. They should report the same vendor. When WebGPU says Apple and WebGL says NVIDIA, something between the page and the hardware is rewriting strings. - Engine detected via WASM versus User-Agent claim. UA says Safari, WASM says V8 = Chrome wearing a Safari mask. - Software renderer flag versus reported architecture. Chrome on Linux without a GPU falls back to SwiftShader and reports `vendor: "Microsoft Basic Render Driver"`. If a page sees that vendor combined with a high-end gaming UA, the rig is fictional. - GPU compute speed versus claimed mobile platform. iPhone GPUs do roughly 1.5 TFLOPS. A WASM-claimed iPhone benchmark scoring 4 TFLOPS on FP32 is cosplaying. - WebGPU limits hash matching the architecture string. Each GPU family has fixed limits values; if architecture says `"ampere"` but limits hash maps to RDNA-3, the spoof targeted only the visible string. Same logic as a polygraph: no single channel is reliable, but inconsistencies across channels are diagnostic. The hardware doesn't lie. The driver doesn't lie. The JIT compiler doesn't lie. The browser's self-report is the only piece in this stack with any incentive to bend, so it's the piece worth doubting. The WHOX GPU/WASM leak test runs all five cross-checks, surfaces which one disagrees with which, and gives you a concrete pointer at what spoofing layer you're actually using. Useful for verifying anti-detect setups, useful for proving you're a real device when you actually are one.
GPU fingerprinting identifies your device by measuring unique characteristics of your graphics hardware — compute timing patterns, supported features, shader precision, and memory limits. These signals persist across sessions and are nearly impossible to spoof.
Yes. Different JavaScript engines (V8/Chrome, SpiderMonkey/Firefox, JSC/Safari) execute WASM with measurably different timing patterns. The JS-to-WASM call overhead alone can differ 8x between engines, making engine detection reliable even when User-Agent is spoofed.
Canvas fingerprinting captures rendering output. GPU/WASM fingerprinting measures compute behavior — timing patterns, shader precision, memory access speeds. These are hardware-level signals that anti-fingerprint extensions cannot intercept.
Run the full WHOX toolkit from one place: leak checks, fingerprint probes, network diagnostics, reputation lookups, and privacy failure tests.