Orange textured background

Signal

WebGL GPU trace

Fingerprints the GPU's execution-unit timing pattern to derive a hardware-identity signal that survives renderer-string spoofing.

Reviewed

Tier 2 engine

What it measures

Each GPU model has a characteristic pattern of relative timing across its shader execution units. The signal probes that pattern with a vertex-shader workload, normalises the resulting timing matrix so absolute clock speed and CPU scheduling jitter drop out, and hashes the normalised shape. The shape is stable for a given GPU model and survives spoofing of the public renderer string.

Specific point counts, round counts, stall-iteration counts, shader source, and the timing-extension name are deliberately not published.

How it is collected

A WebGL2 context is acquired on an OffscreenCanvas (with a DOM canvas fallback) and a shader program is compiled and linked. If either step fails the signal returns absent. Where the browser exposes a GPU-side timing extension it is used for resolution; where it does not, the collector falls back to a lower-resolution wall-clock fallback. Several rounds of measurement are run, a warmup round is discarded, the remaining rows are row-normalised, and the result is hashed via xxHash64.

Exact round count, point count, the timing extension used, the polling cadence, and the normalisation rounding are part of the internal recipe and not part of the public contract.

Confidence rules

ConfidenceTrigger
normalData collected and the inter-round stability of the timing matrix is within the internal acceptance threshold.
degradedEither the timing fallback resolution was too coarse to distinguish per-EU stalls, or the GPU architecture does not produce inter-round-stable per-EU timings (some unified-architecture GPUs fall in this bucket). In this case the hash is replaced with a sentinel so the composite fingerprint stays stable across collections.
absentNo WebGL2 context, shader compilation failed, or a timeout fired. Paired with the appropriate sentinel ('unsupported', 'threw', or 'timeout').
stabilizedstabilize: ['private'] was set and the active browser matches the private-mode drop rule for this signal.

Why engine-bound

Raw GPU timing values are influenced by browser throttling policies, background-tab scheduling, and the availability of a GPU-side timing extension which varies by browser. Normalisation removes absolute timing differences but does not fully remove the engine-dependent shape variation, so the signal is classified engine-bound rather than hardware-bound.

Things worth knowing

  • The first measurement round is always discarded as a warmup to avoid cold-start GPU pipeline overhead.
  • When all values in a normalised row are identical, the standard deviation is forced to 1 to avoid a division-by-zero in the normalisation step.
  • The collector yields cooperatively during and between rounds so it does not block the main thread.
  • Unified-architecture GPUs that do not produce inter-round-stable per-execution-unit timings degrade cleanly via an internal stability check rather than destabilising the composite fingerprint. The exact stability metric and threshold are part of the internal recipe and not part of the public contract.
  • Raw timing arrays are returned in debug mode for diagnostics even on the degraded path; they do not contribute to the fingerprint.

Last reviewed 2026-06-04