Skip to content
All fingerprint checksRender & GPU

WebGL exposes a full parameter table + UNMASKED vendor/renderer

Check id webgl-param-table

What an ordinary browser yields

Non-empty unmasked vendor and renderer, plus 12 or more parameters returning values.

What a detector infers

Beyond the two headline strings, a WebGL context answers getParameter for dozens of driver-specific limits. The audit queries 21 of them — MAX_TEXTURE_SIZE, MAX_VIEWPORT_DIMS, MAX_VERTEX_UNIFORM_VECTORS, ALIASED_LINE_WIDTH_RANGE, the RED/GREEN/BLUE/ALPHA/DEPTH/STENCIL bit depths, MAX_TEXTURE_MAX_ANISOTROPY_EXT and others — hashes the collected values, and passes when at least 12 of them returned a value and the context is not masked: WEBGL_debug_renderer_info must be present, UNMASKED_VENDOR and UNMASKED_RENDERER must both be non-empty, and the renderer must not read "Masked" or "Not supported". Only a missing WebGL context reports N/A — a stripped debug extension is a fail, not an abstention. Note the scope: it verifies the table is present and unmasked, not that the limits match the named GPU. That second step is the detector's: they hash the whole table, so a rewritten renderer string can be checked against limits that belong to a different GPU — which is why exposing an honest table only helps if the strings describe it.

How to resolve it

Expose a real GPU's parameter table by giving the browser a genuine GPU-backed context (canvas bridge). Don't strip WEBGL_debug_renderer_info or blank out vendor/renderer — the absence is itself distinguishing. If you do set the strings, set them to a GPU whose limits actually match the table you are exposing.

What is WebGL fingerprinting?

Read in the wild by

PerimeterX / HUMAN

Collects the full WebGL driver-limit table — the aliased line-width and point-size ranges, the alpha, blue and green bit depths, depth and stencil bits, the MAX_* limits and MAX_TEXTURE_MAX_ANISOTROPY_EXT — and appends a getShaderPrecisionFormat reading (precision, rangeMin, rangeMax) for each shader and precision pair, sending the table both as a raw array and as a hash. getSupportedExtensions() is collected and hashed as its own separate field.

stample/ifood/source/main.min.js (identifiers hQ/Ak; the same logic appears in stample/walmart and stample/totalwine under different minified names): `n.webglParameters=[];var h=n.webglParameters;if(h[hQ(234)](c(Ak(t,t.ALIASED_LINE_WIDTH_RANGE))),h.push(c(Ak(t,t.ALIASED_POINT_SIZE_RANGE))),h[hQ(234)](Ak(t,t.ALPHA_BITS)),h.push(t.getContextAttributes()[hQ(493)]?"yes":"no"),h.push(Ak(t,t.BLUE_BITS)),h.push(Ak(t,t.DEPTH_BITS)),h.push(Ak(t,t.GREEN_BITS)),h.push(function(t){…e=t.getExtension("EXT_texture_filter_anisotropic")…t.getParameter(e.MAX_TEXTURE_MAX_ANISOTROPY_EXT)…}(t)),…h.push(Ak(t,t.MAX_CUBE_MAP_TEXTURE_SIZE)),h.push(Ak(t,t.MAX_FRAGMENT_UNIFORM_VECTORS)),…h.push(c(Ak(t,t.MAX_VIEWPORT_DIMS))),h.push(Ak(t,t.STENCIL_BITS))` — note RED_BITS is absent (grep -c RED_BITS = 0). Precision: `var l=t[hQ(497)](t[u],t[s]);h.push(l.precision,l[hQ(500)],l[hQ(501)])`. Extensions: `n.extensions=t[hQ(490)]()||[pd]`. Dispatch pairs each raw value with a hash: `e["PAhJQnpoSHY="]=n.extensions,e["GCQtLl1ELx8="]=iR(n.extensions),e["LVkYU2g9Hmg="]=n.webglParameters,e["dWFAKzABQhs="]=iR(n.webglParameters)`. Decoded payload stample/ifood/sample/1/decoded_payload_2.json confirms: LVkYU2g9Hmg= = ["[1, 1]","[1, 1024]",8,"yes",8,24,8,16,32,16384,1024,16384,16,16384,30,16,16,4096,"[32767, 32767]","no_fp",23,127,127,…] (20 params + 12 precision triples), dWFAKzABQhs= = "dab81cb438e9b1ecd9151a3ba33a82b8", GCQtLl1ELx8= = "96ff435b6ebac2817a4d5bfc475aa8e4", PAhJQnpoSHY= = ["ANGLE_instanced_arrays","EXT_blend_minmax",…]. hQ(490)/hQ(497) semantics confirmed empirically from these payload values. Walmart build independently shows the same raw+hash pairing: `n["bRJWEyh9Xig="]=t.webglParameters,n["GwBgAV5rbDE="]=fJ(t.webglParameters)`.

Kasada

Hashes a full WebGL getParameter table rather than trusting the vendor string alone.

gl_unmasked_vendor, gl_unmasked_renderer, gl_version, gl_max_uniform_buffer_bindings, gl_max_fragment_uniform_vectors, gl_aliased_line_width_range …

ips.js 427-probe teardown (emro.cat, Apr 2026)

Akamai

Lists the GPU renderer and vendor via WEBGL_debug_renderer_info as hardware signals.

signal_categories.md - 2. Hardware - WebGL renderer / WebGL vendor

Edioff/akamai-analysis — signal_categories.md

Every attribution traces to a published artifact. See the sources and their limits.

Nearby checks in Render & GPU

See all 32 checks in Render & GPU
Who builds this test

Clearcote is a browser built for fingerprint coherence

It is a Chromium fork, maintained by the same people who wrote this reference. It ships as a compiled browser rather than as a stealth script injected into someone else's — which is a description of how it is built, and is not an argument about how it behaves on this check.

This audit takes no position on how Clearcote scores on Render & GPU checks, on this one, or anywhere else. It has no baseline corpus of other people's fingerprints to rank you against and no vendor scoreboard — nearly every check is self-referential, asking one browser the same question through two independent APIs and reporting whether both answers can be true at once. It runs identically on any browser, including ours. Run it on yours and read the result yourself.

See the other checks in Render & GPU — the family webgl-param-table belongs to.