WebGL is how a web page draws 3D on your graphics card, and before it draws anything it gets told what the card is. The chip's name, the largest texture it takes, which driver features exist. A fingerprinting script asks for all of it and never draws a pixel you would see. No other browser API says as much about the hardware.

What WebGL fingerprinting reads

The first call most scripts make is to an extension Chrome exposes as WEBGL_debug_renderer_info. It has two constants, UNMASKED_VENDOR_WEBGL and UNMASKED_RENDERER_WEBGL, and getParameter() turns them into the driver's vendor and renderer strings (MDN, read 2 November 2026). In Chrome the renderer names the actual chip. An M2 MacBook says:

ANGLE (Apple, ANGLE Metal Renderer: Apple M2, Unspecified Version)

and on Windows, through Direct3D:

ANGLE (NVIDIA, NVIDIA GeForce RTX 4060 (0x00002882) Direct3D11 vs_5_0 ps_5_0, D3D11)

Behind the strings sits what we call the capability record: around sixty more values from getParameter(), such as maximum texture size, maximum viewport dimensions and vertex attribute counts, plus the shader precision formats and the list of supported extensions. And then the script draws. It renders a small scene, pulls the pixels back with readPixels() and hashes them. Two GPUs, or one GPU on two drivers, round a few of those pixels differently. It is the trick behind canvas fingerprinting, moved onto the graphics card.

Every profile is drawn from one real computer, not from separate listsIn Mosaic: Machines that exist. GPU, screen, CPU and fonts from one real device. Free holds 3 profiles. Pro is $49 a month for any number.

How it works: Machines that exist

Why a spoofed renderer string fails

Renaming the GPU is one line of code. Done alone, it backfires: the string now says one chip and the sixty limits under it still describe the other. You don't need a database of devices to see it, only to know that an M2 never came with that extension list or that texture limit.

The string itself has to be exact, too, and we learned that the hard way. Every GPU string in our device catalogue was once written by hand, and 41 of the 45 Windows strings were wrong in the same way: they left out the (0x00002882) part, the PCI device id ANGLE takes from the driver. A GPU name with no device id is not a rare configuration; no real Chrome on Windows reports one. That fault sat under all the careful work on canvas and audio until we compared the strings with collected data.

There is also the question of where the value is read. A page can create a WebGL context inside a worker with OffscreenCanvas. If a spoof only covers the page, the page reads one GPU and its worker reads another, and that disagreement is worth more to a detector than either value.

A profile fingerprint in Mosaic: GPU, screen, fonts, timezone and language matched to the exit
The GPU is drawn with the rest of one real machine

How to check yours

Open a WebGL report page, such as BrowserLeaks' WebGL report, and read the unmasked renderer, the extension count and the hash of the rendered image. Now load that page in a second browser on the same computer. Same GPU, so the renderer ought to read the same. In a modified browser, compare the renderer with the limits beneath it. Our guide to checking your browser fingerprint lists other public pages, including ones that read the same values from inside workers.

How Mosaic handles it

Each profile's hardware is one real device from Mosaic's catalogue, so the GPU comes with the screen, CPU and memory that machine actually ships with (machines that exist explains the catalogue). We stopped writing renderer strings by hand after the 41-of-45 episode. They now come from collected fingerprints, and the build refuses any catalogue row whose renderer nobody has seen a real browser report. The vendor and renderer strings are set inside Chromium, in C++. A page, an iframe and a worker all read the same GPU.

On an Apple Silicon Mac, the chip a profile reports is the computer's own. That rule came from Pixelscan. We changed one thing at a time on one profile: a different thread count passed, a different chip failed. The extension list, the precision formats and the pixels all come from the real driver, and none of them move when the name does. The engine does carry a patch that could claim all sixty capability values. It stays switched off until we have those values collected from real machines. A limit no real driver reports is a louder tell than the honest number. The noise on read-back pixels is seeded per profile: open the profile tomorrow and the image hash is the one it had today.

Sources

  1. MDN: WEBGL_debug_renderer_info, read November 2, 2026
  2. BrowserLeaks: WebGL Report, read November 2, 2026