Canvas, WebGL and audio fingerprints: how your hardware gives you away
Tiếng Anh
Where the three most common hardware-related fingerprints get their variation, why the same machine looks different in another browser, and why noise-adding extensions usually make you more visible, not less. With a note on WebGPU.
Đọc 6 phút · Đăng 10 tháng 9, 2026
Ask the browser to draw a line of text, render a triangle, and process a snippet of audio nobody will hear. Hash the results. None of it needs a permission prompt, the user notices nothing, and together they sort your machine into a very small bucket among millions of devices. The mechanics aren't mysterious.
Canvas: differences you can draw
Canvas fingerprinting scripts all look alike. Create an off-screen <canvas>, write a sentence in a couple of fonts (usually something with varied glyphs like "Cwm fjordbank glyphs vext quiz", plus an emoji for good measure), layer a few semi-transparent coloured rectangles and arcs on top, then call toDataURL() to get the PNG as base64 or getImageData() to read raw pixels, and hash the result.
Identical drawing commands ought to produce identical pixels. They don't, because the path from command to pixel is long:
- Font rasterisation. The same font file goes through DirectWrite on Windows, Core Text on macOS and FreeType on Linux, and each turns curves into pixels its own way.
- Anti-aliasing and subpixel rendering. The translucent edge pixels depend on system settings, screen orientation and whether ClearType-style rendering is on.
- GPU and driver. Browsers hand canvas drawing to the GPU, and arc interpolation and blend-mode rounding differ subtly across cards and driver versions.
- Colour management. The display's colour profile can leak into canvas output on some setups.
Any single pixel difference is trivial, but a hash amplifies all of them into an entirely different string. The good news, for the tracker, is stability: the same machine and browser draws exactly the same image today and next month. That stability is what makes it usable as an identifier.
WebGL: pixels plus a pile of parameters
WebGL fingerprinting has two layers. The first is canvas-like: render a scene with gradients and a vertex shader, read back the pixels, hash them. The shader compiler and the GPU's floating-point precision add yet another source of variation.
The second layer is blunter. WebGL just reports a lot of static facts. The WEBGL_debug_renderer_info extension exposes the unmasked vendor and renderer strings, something like ANGLE (Apple, ANGLE Metal Renderer: Apple M2, Unspecified Version) or ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 Direct3D11 vs_5_0 ps_5_0, D3D11). Then there's the list of supported extensions, assorted limits (maximum texture size, vertex attributes, anti-aliasing samples) and shader precision formats. The combined entropy is very high; the renderer string alone names the chip, the graphics backend and roughly the driver generation.
Browser vendors know this. Safari and Firefox, in some configurations, coarsen the renderer to something like "Apple GPU".
Audio: the fingerprint you can't hear
AudioContext fingerprinting is the one most often misunderstood as "it needs the microphone". It doesn't. The script builds a signal in memory using OfflineAudioContext: an oscillator generates a triangle or sine wave, it passes through a DynamicsCompressorNode, a few thousand samples are rendered offline, and the output is summed or hashed. Silent, done in tens of milliseconds.
The variation comes from floating-point implementation details, the DSP library the browser uses, the exact compressor algorithm, and rounding in the OS audio stack. Run the same code and Chrome and Firefox disagree a few decimal places in. That's enough.
Why the same machine changes with the browser
Because every "last mile" above is implemented by the browser itself. Chrome draws 2D with Skia and routes WebGL through ANGLE to Direct3D 11 on Windows or Metal on macOS. Firefox has its own stack; Safari another. Text shaping engines and audio backends differ too. So canvas, WebGL and audio fingerprints really identify the combination "this hardware + this OS + this browser build", not the hardware alone.
It's also why fingerprints frequently change after a major browser update, and why trackers lean on other stable signals (screen, time zone, fonts) to link the old fingerprint to the new one.
Why noise-adding extensions often backfire
There are plenty of "Canvas Defender"-style extensions that inject small random perturbations into pixels before toDataURL returns, so each read is different. Sounds reasonable. Two problems.
First, real browsers are stable. If a site draws the canvas twice and gets two hashes, it knows immediately you're running a perturbation tool. Our authenticity score has exactly this check: two draws disagree, points off. Few people run such tools, so the signal by itself places you in a small group.
Second, if the extension applies a fixed offset instead (same every time), your hash becomes something nobody else in the world has. That's more unique than the un-noised value, which at least matched a few thousand identical MacBooks. Precisely the opposite of the goal.
The more mature approaches are built into browsers. Firefox's privacy.resistFingerprinting prompts before canvas reads and normalises WebGL parameters to common values, aiming to make everyone with the setting look the same. Brave's "farbling" is cleverer: it perturbs using a seed derived from site plus session, so your fingerprint is stable within one site (no telltale flicker) but different across sites (no linking).
WebGPU is next
WebGL's successor, WebGPU, ships in Chrome, and as of this writing Safari and Firefox are catching up. Its adapter info exposes vendor, architecture, device and description, a long list of limits (maximum buffer size, workgroup dimensions and so on) and optional features. These sit closer to the hardware than WebGL's equivalents, and compute shaders make timing-based measurements easier. Fingerprinting research has already adopted it as a fresh source. Our WebGL report lists WebGPU adapter info whenever the browser supports it, so you can see how much it reveals.
FAQ
Can I just turn canvas fingerprinting off? You can, but many canvas-dependent things break (charts, image editors, some CAPTCHAs), and "off" is itself detectable. The practical route is Firefox's resistFingerprinting or Brave: accept "looks like a crowd" instead of chasing "invisible".
My WebGL renderer says SwiftShader or llvmpipe. What does that mean? The browser is rendering in software on the CPU with no GPU acceleration. Real desktop browsers rarely do that; virtual machines and headless browsers commonly do, which is why it counts as a bot-detection signal.
On this site my canvas hash "matches M of the last N visits". Is a big M good or bad? Big is good: you look like a lot of other people and are hard to single out. M equal to 1 means nobody else has produced that value.