Anti-Bot

What Is WebGL Fingerprinting?

What Is WebGL Fingerprinting? — conceptual illustration
On this page

WebGL fingerprinting reads identifying information directly from the GPU. The browser exposes the graphics card vendor and renderer string (via WEBGL_debug_renderer_info), the list of supported WebGL extensions, the maximum texture size, the precision of shader operations, and — most distinctively — the pixel output of a known render operation. The combination is roughly hardware-tied and survives most user-agent spoofing.

Quick facts

ReadsGPU vendor + renderer string, ~70 extension flags, render output hash
Distinct from CanvasWebGL probes the GPU directly; Canvas probes the 2D drawing pipeline
Common bot tellSwiftShader renderer (Google Inc. SwiftShader) = headless Chrome with no GPU
Block-grade signalRenderer mismatched to platform — e.g. NVIDIA on macOS, llvmpipe on Windows
DefeatsVanilla headless Chrome, default Playwright/Puppeteer

What WebGL exposes

The browser API exposes four classes of identifying data:

  1. Renderer string — via gl.getParameter(gl.RENDERER) with the WEBGL_debug_renderer_info extension. Returns strings like "ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 Direct3D11 vs_5_0 ps_5_0, D3D11)" on Windows or "Apple GPU" on macOS.
  2. Extension list — about 70 named extensions (EXT_color_buffer_float, OES_texture_float_linear, etc.). The exact set varies by GPU model and driver version.
  3. Parameter values — max texture size (4K/8K/16K), max viewport dimensions, max vertex attributes, fragment shader precision. Each varies by hardware tier.
  4. Rendered output — draw a known shape with a known shader and hash the pixels read back from the canvas. Different GPUs produce subtly different floating-point rounding, anti-aliasing, and gradient interpolation.

Why headless browsers fail WebGL the hardest

Headless Chrome without a GPU falls back to SwiftShader, Google's software rasterizer. The renderer string is "Google Inc. SwiftShader" or "Google SwiftShader" — anti-bot vendors block this string unconditionally because no real desktop user has SwiftShader as their primary GPU. The fallback chain is even worse on Linux: llvmpipe (Mesa software rasterizer) is an instant tell.

Running headless Chrome with --use-gl=angle --use-angle=swiftshader-webgl still produces a SwiftShader renderer. The mitigations are: (1) run with xvfb + a real GPU passed through, (2) spoof WEBGL_debug_renderer_info at the CDP level with a believable renderer string, or (3) use a tool like Camoufox or CloakBrowser that patches the renderer at the C++ level.

The renderer-platform coherence trap

Spoofing the renderer string alone is insufficient. Anti-bot vendors cross-check the claimed renderer against the platform (navigator.platform, navigator.userAgent) and the GPU's expected extension list. A request claiming Windows + Chrome 131 with renderer string "Apple GPU", or macOS with an NVIDIA RTX renderer, gets blocked. The renderer extension set must also match the claimed hardware — a budget integrated GPU with the extensions only found on enthusiast cards is a clear tell.

Camoufox's approach is to maintain a database of real (platform, GPU, renderer, extensions, parameters) tuples harvested from real users, and serve a coherent one per session. This is more expensive than spoofing individual values, which is why DIY hardening of WebGL almost always trips at least one cross-check.

Code example

javascript
// What an anti-bot script reads to fingerprint WebGL
function webglFingerprint() {
  const canvas = document.createElement('canvas');
  const gl = canvas.getContext('webgl');
  if (!gl) return 'no-webgl';   // already suspicious

  const debug = gl.getExtension('WEBGL_debug_renderer_info');
  const renderer = debug
    ? gl.getParameter(debug.UNMASKED_RENDERER_WEBGL)
    : gl.getParameter(gl.RENDERER);

  return {
    renderer,                                                 // 'ANGLE (NVIDIA RTX 4070 ...)' or 'SwiftShader'
    vendor: gl.getParameter(gl.VENDOR),
    extensions: gl.getSupportedExtensions().sort().join(','), // ~70 entries
    maxTexture: gl.getParameter(gl.MAX_TEXTURE_SIZE),         // 4096 / 8192 / 16384
    maxViewport: gl.getParameter(gl.MAX_VIEWPORT_DIMS).join('x')
  };
}
// SwiftShader anywhere in the renderer string is a hard block at major vendors.

Related terms

Concept map

How WebGL Fingerprinting connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Anti-Bot
Building map…

Frequently asked questions

Is WebGL fingerprinting more or less reliable than Canvas fingerprinting?

More reliable for headless detection — SwiftShader is an instant tell that canvas fingerprinting alone cannot catch. Less reliable for distinguishing between real users because most consumers have one of a small set of common GPUs (integrated Intel, Apple GPU on Mac), so collision rates are high among real traffic.

Can I just disable WebGL to avoid the fingerprint?

No — disabling WebGL is itself a strong signal. The expected return of getContext("webgl") on a modern browser is a working context. Returning null or throwing is what scrapers and Tor Browser do, and anti-bot vendors block it.

What renderer string should a spoofed browser use?

A common real one matching your claimed platform. The most-used renderers in production traffic are "ANGLE (Intel, Intel UHD Graphics ...)" on Windows, "Apple GPU" on macOS/iOS, and "Mali-G78" on mid-range Android. Pick one consistent with the User-Agent and don't change it within a session.

Last updated: 2026-05-27