---
title: "WebGL fingerprint: why SwiftShader is a bot signal"
description: "What a WebGL fingerprint reveals about the graphics card, why SwiftShader rendering is a bot signal, and why a spoofed renderer string contradicts its own pixels."
canonical: "https://scalebrowser.net/blog/webgl-fingerprint"
last_modified: "2026-10-19"
published: "2026-10-19"
author: "davide"
category: "detection"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://scalebrowser.net/llms.txt
> Use this file to discover all available pages before exploring further.

# WebGL fingerprint: why SwiftShader is a bot signal

> What a WebGL fingerprint reveals about the graphics card, why SwiftShader rendering is a bot signal, and why a spoofed renderer string contradicts its own pixels.

Most server-side browsers have no graphics card, and WebGL tells every page so in one line. The usual fix is to overwrite the renderer string with the name of a real card, which turns an honest server into a machine that cannot exist: the string says one card while the pixels come from another path entirely. This guide covers what WebGL reveals, why drawing in software is read as a bot, and why the only renderer string that holds up is the one the machine really has.

## What does a WebGL fingerprint contain?

A WebGL fingerprint contains everything a page can learn about the graphics stack through the WebGL API, from the name of the card to the exact pixels it produces. The Khronos [WEBGL_debug_renderer_info extension](https://registry.khronos.org/webgl/extensions/WEBGL_debug_renderer_info/) exists because implementations "might mask the RENDERER and VENDOR strings of the underlying graphics driver for privacy reasons", and it exposes them anyway "in a guaranteed manner for debugging purposes". A fingerprinting script reads five kinds of value.

- **Vendor**: the unmasked vendor string, constant `0x9245`, which names the maker of the card or the software that stands in for it.
- **Renderer**: the unmasked renderer string, constant `0x9246`, which names the model and the graphics interface the browser drives it through.
- **Parameters**: limits such as the largest texture and viewport the card accepts, and the precision of its shaders.
- **Extensions**: the list of WebGL extensions the driver supports, which differs between vendors and driver versions.
- **Readback pixels**: a scene drawn off screen, read back with `readPixels` and hashed, which reflects how the hardware and driver actually rasterise.

The first four are claims the browser makes. The fifth is evidence the hardware produces, and a checker compares the two.

## Why is software rendering a bot signal?

Software rendering is a bot signal because its pixels match no consumer graphics card, and Chrome is phasing out the fallback that used to put ordinary visitors on it. [SwiftShader](https://github.com/google/swiftshader) is Google's "high-performance CPU-based implementation of the Vulkan 1.3 graphics API", which Chrome used for WebGL on machines without a usable GPU. llvmpipe is the Linux counterpart, Mesa's "software rasterizer that uses LLVM", and it is what a server without a graphics card draws with. Both put their own name into the renderer string, and both produce pixels no consumer card produces.

Chrome is closing the ordinary path to SwiftShader. Its [platform status entry](https://chromestatus.com/feature/5166674414927872) marks the automatic fallback as deprecated, with Chrome 139 on desktop as the milestone, after which "WebGL context creation will fail instead of falling back to SwiftShader"; SwiftShader stays available as an opt-in for testing. A browser that renders WebGL through SwiftShader is therefore a machine without a usable GPU or a browser started with that opt-in, and headless test setups are the usual case of both. Reddit's own detection code, which we ran verbatim in September 2026, sorts the WebGL renderer into exactly three classes: `swiftshader`, `llvmpipe` and `other`.

A cloud browser vendor says the same about its own product. Kernel's [bot detection documentation](https://www.kernel.sh/docs/browsers/bot-detection/overview) states that "Software-rendered browsers produce pixel hashes that don't match any real consumer GPU, which is a strong bot signal on sites with rendering-based fingerprinting", and sells a browser with a real GPU as the answer.

<Evidence source="Kernel pricing and GPU acceleration documentation, per-second rates converted to hours, read 27 September 2026" href="https://www.kernel.sh/docs/info/pricing">

| Kernel browser | Price per second | Price per hour | Against headless | Plans |
| --- | --- | --- | --- | --- |
| Headless | 0.0000166667 USD | 0.06 USD | 1x | all |
| Headful | 0.0001333336 USD | 0.48 USD | 8x | all |
| Headful with GPU acceleration | 0.0008000016 USD | 2.88 USD | 48x | Start-Up (200 USD a month) and Enterprise |

</Evidence>

An agent that runs 8 hours a day on the GPU tier costs 23.04 USD a day at that rate, against 0.48 USD headless. No other line on Kernel's price list multiplies the rate as much as the graphics card does.

## What does a spoofed renderer string contradict?

A spoofed renderer string contradicts the pixels, because the string is text the browser returns while the pixels come from whatever really drew the scene. The Puppeteer stealth plugin shows the pattern in its [webgl.vendor evasion](https://github.com/berstend/puppeteer-extra/blob/master/packages/puppeteer-extra-plugin-stealth/evasions/webgl.vendor/index.js): it answers "Intel Inc." and "Intel Iris OpenGL Engine" by default, and its own comment notes that the headless default it replaces is "Google SwiftShader". Every installation that keeps the default claims the same Intel card over software-rendered pixels.

We measured how much of the drawing path shows up in the pixels. On 3 July 2026 our engine drew the standard fingerprint canvas on one Windows machine in 12 variants and hashed each result.

<Evidence source="Scalebrowser engine, standard fingerprint canvas, one Windows machine with a graphics card, 12 variants, 3 July 2026">

| What changed | Variants | Canvas hash |
| --- | --- | --- |
| font hinting, LCD text, subpixel positioning, colour profile, display scale | 8 | identical to the baseline in 8 of 8 |
| a different font for the same text | 3 | a new hash in 3 of 3 |
| software rendering instead of the graphics card | 1 | a new hash |

</Evidence>

Eight rendering settings left the hash untouched; the drawing hardware changed it. A checker that reads the renderer string and the readback hash therefore holds two independent answers about the same card, and a string that names hardware the pixels did not come from fails that comparison in one step. That is the reasoning behind every surface in [fingerprint surfaces](/blog/browser-fingerprinting): values that describe the machine have to come from the machine.

Scalebrowser follows that rule for the graphics card. It takes the card from the machine when a profile is created and again at every start, refuses to start a profile whose identity names another vendor's card, and refuses to start on a machine that renders without one. Two profiles on the same machine therefore report the same card, which is the honest cost of the rule; the [coherence page](/docs/coherence) explains why that trade is made.

<ProductMention />

## How does this fit into the four layers?

The WebGL fingerprint belongs to the second of the [detection layers of a browser](/blog/bot-detection), the JavaScript fingerprint, and a software renderer found there adds to a score that the connection, the control plane and the input feed as well. How a user agent that names the wrong Chrome version shows is covered in user agent spoofing.
