What Is WebGL Pixel Hash Fingerprinting? How Shaders Identify Your GPU
Browser & Device

What Is WebGL Pixel Hash Fingerprinting? How Shaders Identify Your GPU

WebGL pixel hash fingerprinting renders a hidden GLSL shader and hashes the output pixels — exploiting GPU floating-point micro-differences that persist across browsers, incognito mode, and VPNs.

8 min read·

WebGL pixel hash fingerprinting renders an invisible test scene using a GLSL shader and reads back the resulting pixels with gl.readPixels() — exploiting the fact that every GPU computes floating-point arithmetic slightly differently, producing a stable hash that identifies your hardware across all browsers, survives clearing cookies, and cannot be hidden by a VPN. You can see your own WebGL pixel hash right now on whatsmy.fyi.

TL;DR

A GLSL shader program is compiled and executed on your GPU inside a hidden WebGL canvas. The rendered pixel values are read back and hashed into a compact identifier. Because floating-point rounding in GPU shader units varies between manufacturers, architectures, and driver versions, the same shader produces subtly different pixel outputs on different hardware — even on two machines with the same GPU model but different driver installations. This makes the pixel hash a more fine-grained identifier than simply reading the GPU renderer string, which only reveals hardware model, not the exact driver-level behaviour.

What Is WebGL Pixel Hash Fingerprinting?

WebGL pixel hash fingerprinting is a specific technique within the broader family of WebGL fingerprinting that targets the computational output of your GPU rather than its metadata. While reading the GPU renderer string via WEBGL_debug_renderer_info tells a tracker your GPU model, it cannot distinguish two identical GPUs running different driver versions. The pixel hash closes that gap.

The technique works because the WebGL API gives JavaScript direct access to GPU shader execution. A fingerprinting script can write a GLSL fragment shader that performs mathematical operations — trigonometric functions, fractional parts, pseudo-random noise — and have the GPU execute it. The resulting colour values written to each pixel depend on how the GPU's floating-point units handle those operations internally. Different GPU architectures accumulate rounding errors differently, and different driver versions on the same GPU can produce measurably different outputs.

This is closely related to canvas fingerprinting, which also reads pixel output — but canvas fingerprinting targets 2D font rendering and anti-aliasing differences. WebGL pixel hashing targets the GPU's 3D shader arithmetic directly, making it both higher-entropy and harder to normalise.

How Does WebGL Pixel Hash Fingerprinting Work?

The entire process runs in JavaScript, takes under 50 milliseconds, and is completely invisible to the user. It unfolds in four stages.

Stage 1 — Creating a Hidden WebGL Canvas

The script creates an off-screen canvas element — never attached to the document — and obtains a WebGL rendering context. Canvas size is typically small (256×128 or 512×256 pixels) to keep execution fast. The canvas is never displayed.

Stage 2 — Compiling a Shader Designed to Maximise Variation

A GLSL fragment shader is written to perform operations that are sensitive to floating-point precision differences. The most effective shaders combine sin(), cos(), fract(), and large coefficient multiplications — because these operations accumulate rounding errors across GPU architectures in different ways. The shader maps each pixel's screen coordinates through these mathematical functions to produce a colour value.

A well-known example uses a formula like:

// GLSL fragment shader used for pixel hash fingerprinting
precision highp float;

void main() {
  float x = gl_FragCoord.x / 256.0;
  float y = gl_FragCoord.y / 128.0;

  // These sin/fract combinations amplify GPU floating-point differences
  float r = fract(sin(x * 12.9898 + y * 78.233) * 43758.5453);
  float g = fract(sin(x * 93.989 + y * 17.211) * 43421.631);
  float b = fract(cos(x * 51.234 + y * 31.456) * 12345.678);

  gl_FragColor = vec4(r, g, b, 1.0);
}

The large constant multipliers (43758.5453, 43421.631, 12345.678) mean that tiny floating-point differences in intermediate results are amplified to visible colour differences when fract() wraps them. Two GPUs that agree to 14 decimal places in their intermediate arithmetic will still produce different final pixel colours.

Stage 3 — Reading Pixels with gl.readPixels()

After the shader executes, the script calls gl.readPixels() to transfer the rendered pixel data from GPU memory back to a JavaScript Uint8Array. This gives the script direct access to the RGBA values of every pixel in the canvas — the raw numerical result of the GPU's shader computation.

// JavaScript: compile shader, render, and extract pixels
const canvas = document.createElement('canvas');
canvas.width = 256;
canvas.height = 128;
const gl = canvas.getContext('webgl');

// Compile and link vertex + fragment shaders (abbreviated)
const program = compileAndLinkShaders(gl, vertexSrc, fragmentSrc);
gl.useProgram(program);

// Draw a full-screen triangle strip
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(
  gl.ARRAY_BUFFER,
  new Float32Array([-1, -1,  1, -1,  -1, 1,  1, 1]),
  gl.STATIC_DRAW
);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

// Read pixel data from GPU memory
const pixels = new Uint8Array(256 * 128 * 4); // width × height × RGBA
gl.readPixels(0, 0, 256, 128, gl.RGBA, gl.UNSIGNED_BYTE, pixels);

// Hash the pixel buffer → stable GPU fingerprint
const hash = fnv1a(pixels); // or MurmurHash, xxHash, etc.

Stage 4 — Hashing the Pixel Buffer

The full pixel array — 131,072 bytes for a 256×128 canvas — is hashed into a compact value. Common choices are FNV-1a (fast, non-cryptographic), MurmurHash3, or a simple numeric sum of sampled pixel bytes. The result is a short hexadecimal string that represents the exact floating-point behaviour of your GPU shader units. This hash is stable across browser restarts, incognito sessions, and reboots. It changes only when the GPU driver is updated or the physical hardware is replaced.

How Is This Different from Reading the GPU Renderer String?

Both are WebGL fingerprinting techniques, but they operate at different levels of the hardware stack and capture different dimensions of uniqueness.

PropertyGPU Renderer StringWebGL Pixel Hash
What it readsGPU model name + vendor stringShader computation output
API usedWEBGL_debug_renderer_infogl.readPixels()
Distinguishes same GPU, different drivers?No — same GPU always gives same stringYes — driver differences change pixel output
Blocked by privacy.resistFingerprinting?Yes — returns generic placeholderPartially — requires additional normalisation
Works on virtual machines?Inconsistently — VMs often return Mesa/SwiftShaderYes — software renderers produce distinct outputs too
Requires a WebGL extension?Yes — WEBGL_debug_renderer_infoNo — uses core WebGL

The pixel hash is considered a more robust signal because it does not rely on a debug extension that browsers can simply block. As long as WebGL rendering is available — which is the case on over 95% of browsers — some form of pixel-level variation can be extracted, even if the renderer string returns a generic value.

How Unique Is the WebGL Pixel Hash?

The pixel hash contributes approximately 5–7 bits of entropy in isolation, according to research from Fingerprint.com and academic studies on GPU-based fingerprinting. That translates to distinguishing your browser from roughly 32–128 similar configurations — a meaningful narrowing even before other signals are combined.

FindingValueSource
Entropy from WebGL rendering output alone~5.7 bitsFingerprint.com research
Pixel hash stability across browser restartsNear 100% (changes only with driver update)Cao et al., NDSS 2017
Cross-browser consistency on same hardwareHigh — hardware arithmetic is browser-independentCao et al., NDSS 2017
Identifiability when combined with GPU renderer + vendor< 0.01% of visitors share the same tripleInria / KU Leuven field research
WebGL browser support (Chrome, Edge, Safari, Firefox)95–99%BrowserLeaks WebGL Test

When combined with the GPU renderer string, vendor string, and other hardware signals, the combined WebGL fingerprint can narrow a visitor to fewer than 0.01% of the typical website audience — a level of precision that exceeds what most third-party cookies achieved for cross-site tracking.

Why the Pixel Hash Persists Across Browsers

This is the feature that makes WebGL pixel hashing especially valuable for cross-browser tracking — a scenario that cookie-based systems fundamentally cannot address. When you use Chrome, Firefox, and Edge on the same machine, each browser maintains completely separate cookie jars and local storage. But all three browsers share the same underlying GPU and driver. The GLSL shader is compiled independently in each browser, but executed on the same hardware — producing pixel output that is identical (or nearly identical) across all of them.

Research published at NDSS 2017 by Cao et al. demonstrated this cross-browser fingerprinting capability using exactly this approach: GPU rendering output was used to link browsing sessions across different browser installations on the same device without any shared state between them.

Who Uses WebGL Pixel Hash Fingerprinting?

Commercial Device Intelligence Platforms

Platforms such as Fingerprint.com, Threatmetrix, and iovation include WebGL rendering hashes in their device identifier computation alongside dozens of other signals. For fraud detection, the pixel hash serves as a stable hardware anchor — even if a fraudster clears cookies, rotates IP addresses, and switches browsers, the GPU rendering output ties their sessions together.

Ad-Tech Tracking

As third-party cookies have been deprecated in Safari and Firefox and phased out in Chrome, advertising networks have increased their reliance on hardware-level fingerprinting for cross-site user identification. The WebGL pixel hash is particularly attractive because it requires no browser storage and no network metadata — it is entirely self-contained within JavaScript.

Headless Browser and Bot Detection

Automated browsers running on server infrastructure typically use software renderers such as SwiftShader (Chrome headless) or Mesa LLVMpipe (Linux). These software renderers produce WebGL pixel hashes that are distinctly different from those produced by real GPU hardware — and they often produce the same hash across all instances running the same software renderer version. Security platforms exploit this consistency to flag likely bot traffic.

The Academic Response: UNIGL

The most significant academic defence against WebGL pixel hash fingerprinting is UNIGL, proposed in the 2019 USENIX Security paper "Rendered Private: Making GLSL Execution Uniform to Prevent WebGL-based Browser Fingerprinting" by Wu, Li, Cao, and Wang. UNIGL works by rewriting GLSL shader programs in the browser before they reach the GPU, transforming floating-point operations into forms that produce uniform output regardless of the underlying hardware.

Unlike approaches that simply block WebGL (Tor Browser) or add noise (Brave), UNIGL aims to make the rendering output genuinely uniform — eliminating the tracking signal without breaking WebGL applications. The researchers demonstrated that UNIGL could make pixel hashes identical across different GPU hardware with less than a 3% performance overhead for typical web applications. However, UNIGL has not been adopted by any major browser as of 2025, and remains a research prototype.

How to Protect Yourself from WebGL Pixel Hash Fingerprinting

Effective protection requires either blocking WebGL rendering access, injecting noise into pixel output, or normalising the computation. Options are ranked from strongest to most practical for daily use.

  • Tor Browser (strongest protection): Tor Browser disables gl.readPixels() entirely in its default configuration, making it impossible for scripts to read the rendered pixel data. It also restricts WebGL to a minimal capability mode. The trade-off is slower browsing via the Tor network and some broken WebGL applications.
  • Brave Browser (recommended for daily use): Brave's Farbling injects a small, randomised, per-session noise value into WebGL pixel output. The noise is consistent within a session (so WebGL applications work correctly) but changes between sessions and between different sites — making the pixel hash an unreliable tracking identifier without breaking most WebGL content.
  • Firefox with privacy.resistFingerprinting: Enabling this flag in about:config normalises WebGL output in Firefox, making the rendering hash less distinguishing. The protection is less aggressive than Brave's Farbling but does not require switching browsers. Some WebGL applications may behave unexpectedly.
  • Safari on Apple Silicon (partial protection): Safari on Apple M-series chips randomises canvas output and restricts some WebGL parameters. However, the underlying Apple GPU architecture is still fingerprint-detectable through rendering output — particularly in comparisons across M1, M2, and M3 generations, which produce measurably different shader results.
  • Disabling WebGL in browser settings: In Firefox, setting webgl.disabled to true in about:config completely prevents WebGL rendering. This eliminates the pixel hash signal entirely but also breaks WebGL-based maps (Google Maps 3D, Mapbox), browser games, and some video conferencing interfaces.
  • What a VPN cannot do: A VPN encrypts your network traffic and changes your visible IP address, but has no effect on GPU shader execution. The pixel hash is computed inside your browser and sent to tracking servers as a JavaScript value — not as network metadata. Verify your VPN's IP-level protection on whatsmy.fyi, but understand it does not address fingerprinting of any kind.

Frequently Asked Questions

What is the difference between a WebGL pixel hash and a WebGL renderer string?

The renderer string is a text label — for example, "NVIDIA GeForce RTX 4080/PCIe/SSE2" — returned by the WEBGL_debug_renderer_info extension. It identifies your GPU model but is the same for all machines with that exact GPU. The pixel hash is a numerical fingerprint of your GPU's actual computation — produced by running a test shader and reading the output pixels. It captures driver-level and silicon-level differences that make it unique even among machines with identical GPU models.

Does the pixel hash work in incognito mode?

Yes. Incognito mode prevents your browser from saving cookies, history, and downloads to disk — but it does not change your GPU hardware or driver. The pixel hash is generated entirely from GPU computation and is identical in both normal and private windows. Only browsers with active fingerprint protection (Brave, Firefox with privacy.resistFingerprinting, Tor Browser) produce different output.

Can two different GPUs produce the same pixel hash?

Yes, but it is statistically uncommon. GPUs from different manufacturers (NVIDIA vs. AMD vs. Intel vs. Apple) almost never produce the same hash because their shader architectures handle floating-point operations fundamentally differently. GPUs within the same product family — for example, two RTX 3070 cards running identical drivers — will generally produce the same hash, which is why the pixel hash is most powerful when combined with the GPU renderer string and other signals.

Can software renderers be fingerprinted with this technique?

Yes. Software renderers such as SwiftShader (used by Chrome when hardware acceleration is disabled or unavailable) and Mesa LLVMpipe produce their own pixel hashes. Different versions of these software renderers produce different hashes. A machine running Chrome with hardware acceleration disabled produces a SwiftShader hash that is consistent across sessions — and distinctly different from any real GPU hash, which is why security platforms use it to flag headless browser environments.

Does updating my GPU driver change the pixel hash?

It can. Driver updates sometimes change the floating-point behaviour of shader execution — particularly for trigonometric functions on older GPU generations. Whether a specific driver update changes the pixel hash depends on whether the update modifies the shader compiler or the floating-point unit behaviour for the operations used in the fingerprinting shader. In practice, major driver updates occasionally change the hash; minor stability updates typically do not.

Is WebGL pixel hash fingerprinting blocked by ad blockers?

Only if the fingerprinting script is loaded from a third-party domain listed in the ad blocker's filter list. uBlock Origin in medium or hard mode blocks most known fingerprinting scripts from commercial tracking services. However, first-party scripts — loaded from the same domain as the website — are not blocked by network-level ad blockers. The technique itself (reading WebGL pixel output) is a standard browser capability, not an exploit, so it cannot be blocked by network filtering alone.

Is this the same as the technique described in the “Rendered Private” paper?

Yes. The 2019 USENIX Security paper "Rendered Private: Making GLSL Execution Uniform to Prevent WebGL-based Browser Fingerprinting" by Wu, Li, Cao, and Wang directly addresses WebGL pixel hash fingerprinting. Their proposed mitigation, UNIGL, rewrites GLSL shader source code at the browser level to make rendering output uniform across different hardware. The paper remains the most thorough academic treatment of both the attack and potential defences.

Related Articles

Check your IP address, location, and privacy score — instantly.

Zero logs. Zero tracking. Zero external APIs.

Run the check now →

Related articles