Any page can call navigator.gpu.requestAdapter() and read the GPU's vendor and architecture from adapter.info, with no permission prompt. The WebGPU specification chose to expose that much on purpose: enough for a developer to work around a driver bug, coarse enough that it does not name the exact card. The side effect is a second, independent answer to "which GPU is this", beside the WebGL renderer string a page has been reading for years. When the two disagree, nothing is hidden. There are just two GPUs where there should be one.

What WebGPU adapter info contains: vendor and architecture

The adapter info object has four strings. In Chrome on a normal machine, two of them carry something and two are empty:

FieldExample on an Apple Silicon MacExample on a Windows PC
vendorapplenvidia
architecturemetal-3ada-lovelace
deviceemptyempty
descriptionemptyempty

The vendor is a short, lowercase, normalised name rather than the driver's own string. The architecture is a family: metal-3 is shared by every Apple Silicon chip, and ada-lovelace by a whole generation of NVIDIA cards. The specification lets a browser leave any of these empty, or round them down further, and expects code to cope with an empty string. Chrome's default is the middle setting, vendor and family but no model.

So, on its own, WebGPU's architecture field is a weak fingerprint. A few dozen values cover most of the machines on the web. What makes it worth a page's time is what it can be compared against.

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 requestAdapter needs no permission

There is no prompt because the information was judged coarse enough not to need one, and because a prompt before every game or 3D viewer would be absurd. The call needs a secure context and nothing else. It also works in a dedicated worker, which has its own navigator.gpu. That matters for anything that tries to change the answer from page JavaScript: a worker asks the browser directly, and an override installed in the page is not there.

The WebGL renderer and WebGPU, side by side

WebGL has its own GPU answer, and it is far more specific. The unmasked renderer from WEBGL_debug_renderer_info is the string ANGLE builds from the driver:

WebGL    ANGLE (Apple, ANGLE Metal Renderer: Apple M2, Unspecified Version)
WebGPU   vendor: apple   architecture: metal-3

Those agree. The vendor inside the WebGL string is Apple, the WebGPU vendor is apple, and metal-3 is what any Apple Silicon chip reports. Now take a browser that rewrites the WebGL string to claim an NVIDIA card and leaves WebGPU alone:

WebGL    ANGLE (NVIDIA, NVIDIA GeForce RTX 4060 Laptop GPU (0x000028E0) Direct3D11 vs_5_0 ps_5_0, D3D11)
WebGPU   vendor: apple   architecture: metal-3

No real computer produces that pair. The page did not need to know which answer was true; it only had to read both. This is the general rule behind a lot of what consistency scanners do: a changed value is not a problem by itself, but a value that disagrees with another route to the same fact is.

We measured exactly this on one of our own profiles, before the engine moved WebGPU. The WebGL renderer answered the chip the profile claimed, and requestAdapter() answered the host's real one, in the same document. The renderer strings themselves have to be right too, and we once had 41 of 45 Windows rows wrong, in the renderer strings we wrote by hand. How ANGLE assembles that string is in what ANGLE means in a WebGL renderer.

How a detector checks it

BrowserLeaks has a WebGPU page that prints the adapter's info, its limits and its feature list. Our detection runner reads it and passes the row only when the WebGPU vendor matches the vendor named inside the WebGL renderer, or when WebGPU is absent on a device where that is plausible. On a Mac profile it reads apple beside an Apple renderer, and passes.

The limits and features on that page are worth a sentence too. They come from the real adapter, and they differ between GPU vendors and generations. That is one reason a sensible profile does not claim a vendor the machine does not have.

What Mosaic does with the adapter

Mosaic sets the WebGPU answer in the engine, not in the page. One patch in Tesseract Chrome, Mosaic's own Chromium, changes Chromium's WebGPU adapter code so that the vendor, architecture, device and description come from launch switches, and the switches come from the same hardware record as the WebGL renderer. Every record in our device catalogue carries both, and the Android records derive their WebGPU values from the phone's renderer through Dawn's own GPU table (qualcomm and adreno-7xx for an Adreno 740). Because the change is in C++, a worker reads the same answer as the page. The reasons we do this below JavaScript rather than in it are in C++, not JavaScript.

Two details in that patch follow from the contradiction above. If no switch is given, the real adapter is left alone, because a half-claimed adapter (a changed vendor beside a true architecture) is the very mismatch the patch exists to remove. And the GPU vendor a profile claims stays the computer's own, so the limits and features under it are a real GPU's of the same kind. A Mac profile is an Apple GPU in both places. More on which values a page reads and how they combine is in the browser fingerprint glossary entry.