Research

The role of WebGL renderer in browser fingerprinting

Browser fingerprinting leverages different JavaScript attributes related to the user's device, OS, and browser. When it comes to bot detection, fingerprints can be used as a signature to block attackers, even if they delete their session cookies. Bot detection engines also verify the values of different attributes to verify their consistency and detect potential lies.

This article focuses on the WebGL renderer attribute, a key attribute present in most bot fingerprinting systems since it provides valuable insights into a user’s hardware and software configuration. Thus, it can help to distinguish between legitimate users and potential bots or fraudsters.

What is the WebGL Renderer?

WebGL (Web Graphics Library) is a JavaScript API that enables the rendering of 3D and 2D graphics within a web browser without requiring external plugins. The WebGL renderer refers to the specific graphics rendering engine utilized by a browser to process these graphics. It depends on multiple factors such as the user’s GPU (Graphics Processing Unit), graphics drivers, browser version, and operating system.

Each combination of these components results in a unique WebGL renderer string, which websites can capture using JavaScript. Some common WebGL renderer values include:

  • Google SwiftShader (software-based rendering used when a dedicated GPU is unavailable)
  • ANGLE (Almost Native Graphics Layer Engine) (used in Chrome and Edge on Windows)
  • Mesa/X11 (common on Linux systems)
  • Apple GPU Variants (seen on macOS and iOS devices)

For example, depending on the user’s device, it can be:

  • "ANGLE (Intel, Intel(R) UHD Graphics (0x00008A56) Direct3D11 vs_5_0 ps_5_0, D3D11)”
  • "ANGLE (NVIDIA, NVIDIA GeForce RTX 2060 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)"
  • "ANGLE (Apple, ANGLE Metal Renderer: Apple M1 Pro, Unspecified Version)"
  • "PowerVR Rogue GE8320"

How WebGL Renderer is Used in Fingerprinting

Websites and security systems utilize WebGL renderer data as part of a broader browser fingerprinting technique to:

  1. Identify Unique Devices: Since the WebGL renderer string varies based on hardware and software configurations, it can be used to differentiate users even if they have similar browser and OS versions. It adds uniqueness and entropy to the browser fingerprint.
  2. Detect Bots and Emulators: Automated bots often run in virtualized environments or headless browsers, which might return generic or unexpected WebGL renderer values. Fraud detection systems can flag these anomalies as suspicious behavior.
  3. Prevent Fraud and Account Takeovers: Fraudsters attempting to impersonate users might try to spoof browser fingerprints. However, inconsistencies in WebGL rendering data compared to other collected fingerprinting signals, such as the web GPU or the claimed user OS, can help expose fraudulent activity. A simple example is a user who claims to be on an Android device in its user agent but who has a WebGL renderer equal to Apple GPU, which indicates the presence of MacOS.

How to read a WebGL renderer string?

A typical WebGL renderer string contains:

  1. Rendering Engine – This is usually ANGLE (Almost Native Graphics Layer Engine) for WebGL implementations that translate OpenGL ES calls into Direct3D calls on Windows. Other systems may use native OpenGL or Vulkan instead.
  2. GPU Vendor & Model – This identifies the manufacturer (e.g., NVIDIA or Qualcomm) and the specific GPU model.
  3. Graphics API – This specifies which API is being used to interface with the GPU. For example:
    • Direct3D11 (D3D11) – Direct3D 11 is common on Windows devices.
    • OpenGL ES – More common on mobile devices.
  4. Shader Model Version – The vs_5_0 ps_5_0 refers to vertex shader and pixel shader models, indicating the shader capabilities supported by Direct3D.

How is the WebGL renderer signal collected?

The following code snippet shows the WebGL renderer can be collected using JavaScript and the WebGL API.

const canvas = document.createElement('canvas');
const ctx = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
if (ctx.getSupportedExtensions().indexOf("WEBGL_debug_renderer_info") >= 0) {
    const webGLRenderer = ctx.getParameter(ctx.getExtension('WEBGL_debug_renderer_info').UNMASKED_RENDERER_WEBGL);
}

// On macOS + Chrome webGLRenderer = "ANGLE (Apple, ANGLE Metal Renderer: Apple M4 Pro, Unspecified Version)"

How do browsers generate the WebGL renderer string?

For Chromium-based browsers, the location of the code responsible for retrieving the WebGL renderer string depends on the operating system and GPU type because WebGL is built on top of different graphics APIs that vary across platforms.

On Windows, WebGL often runs through ANGLE (Almost Native Graphics Layer Engine), which translates OpenGL ES calls into Direct3D (D3D11 or D3D12) for better compatibility with Windows graphics drivers. This means that the WebGL renderer string is fetched from ANGLE’s Direct3D backend. On macOS and Linux, where OpenGL or Vulkan is more common, Chromium directly queries the OpenGL/Vulkan driver for the renderer details. Similarly, on mobile devices, WebGL runs using OpenGL ES, and the renderer string is obtained from the GPU driver provided by Qualcomm (Adreno), ARM (Mali), or Apple (Metal-based GPUs).

On Linux and Mac with the OpenGL driver, the information is retrieved: DisplayGL.cpp

std::string DisplayGL::getRendererDescription()
{
    std::string rendererString        = GetRendererString(getRenderer()->getFunctions());
    const angle::FeaturesGL &features = getRenderer()->getFeatures();

    if (features.sanitizeAMDGPURendererString.enabled)
    {
        return SanitizeRendererString(rendererString);
    }
    return rendererString;
}

Under the hood, GetRendererString calls different functions depending on the OS. For example, on Android:

uint32_t GetDeviceID(const FunctionsGL *functions)
{
    std::string nativeRendererString(GetString(functions, GL_RENDERER));
    constexpr std::pair<const char *, uint32_t> kKnownDeviceIDs[] = {
        {"Adreno (TM) 418", ANDROID_DEVICE_ID_NEXUS5X},
        {"Adreno (TM) 530", ANDROID_DEVICE_ID_PIXEL1XL},
        {"Adreno (TM) 540", ANDROID_DEVICE_ID_PIXEL2},
    };

    for (const auto &knownDeviceID : kKnownDeviceIDs)
    {
        if (nativeRendererString.find(knownDeviceID.first) != std::string::npos)
        {
            return knownDeviceID.second;
        }
    }

    return 0;
}

For Windows devices that use ANGLE, the renderer string is also generated at different places depending on the Direct3D version. For example. for Direct3D V9, the code is in Renderer9.cpp:

std::string Renderer9::getRendererDescription() const
{
    std::ostringstream rendererString;

    rendererString << mAdapterIdentifier.Description;
    if (getShareHandleSupport())
    {
        rendererString << " Direct3D9Ex";
    }
    else
    {
        rendererString << " Direct3D9";
    }

    rendererString << " vs_" << D3DSHADER_VERSION_MAJOR(mDeviceCaps.VertexShaderVersion) << "_"
                   << D3DSHADER_VERSION_MINOR(mDeviceCaps.VertexShaderVersion);
    rendererString << " ps_" << D3DSHADER_VERSION_MAJOR(mDeviceCaps.PixelShaderVersion) << "_"
                   << D3DSHADER_VERSION_MINOR(mDeviceCaps.PixelShaderVersion);

    return rendererString.str();
}

The code for Direct3D11 is similar:

std::string Renderer11::getRendererDescription() const
{
    std::ostringstream rendererString;

    rendererString << mDescription;
    rendererString << " (" << gl::FmtHex(mAdapterDescription.DeviceId) << ")";
    rendererString << " Direct3D11";
    if (mD3d12Module)
        rendererString << "on12";

    rendererString << " vs_" << getMajorShaderModel() << "_" << getMinorShaderModel()
                   << getShaderModelSuffix();
    rendererString << " ps_" << getMajorShaderModel() << "_" << getMinorShaderModel()
                   << getShaderModelSuffix();

    return rendererString.str();
}

Frequent WebGL renderer values

The table below shows a list of the most common WebGL renderer values observed in the wild:

ANGLE (AMD, AMD Radeon (TM) Graphics (0x000015E7) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (AMD, AMD Radeon RX 580 2048SP Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (AMD, AMD Radeon RX 6600 Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (AMD, AMD Radeon(TM) Graphics (0x000015D8) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (AMD, AMD Radeon(TM) Graphics (0x00001636) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (AMD, AMD Radeon(TM) Graphics (0x00001638) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (AMD, AMD Radeon(TM) Graphics (0x0000164C) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (AMD, AMD Radeon(TM) Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (AMD, AMD Radeon(TM) Vega 8 Graphics (0x000015D8) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (AMD, Radeon RX 570 Series Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (AMD, Radeon RX 580 Series Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Apple, ANGLE Metal Renderer: Apple M1 Pro, Unspecified Version)
ANGLE (Apple, ANGLE Metal Renderer: Apple M1, Unspecified Version)
ANGLE (Apple, ANGLE Metal Renderer: Apple M2, Unspecified Version)
ANGLE (Apple, ANGLE Metal Renderer: Apple M3, Unspecified Version)
ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero) (0x0000C0DE)), SwiftShader driver)
ANGLE (Intel, Intel(R) HD Graphics (0x00000152) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) HD Graphics 400 Direct3D11 vs_5_0 ps_5_0), or similar
ANGLE (Intel, Intel(R) HD Graphics 4000 (0x00000166) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) HD Graphics 4600 (0x00000412) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) HD Graphics 520 (0x00001916) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) HD Graphics 530 (0x00001912) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) HD Graphics 5500 (0x00001616) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) HD Graphics 620 (0x00005916) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) HD Graphics 630 (0x00005912) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_5_0 ps_5_0), or similar
ANGLE (Intel, Intel(R) HD Graphics Direct3D9Ex vs_3_0 ps_3_0, igdumd64.dll)
ANGLE (Intel, Intel(R) HD Graphics Family (0x00000A16) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) Iris(R) Xe Graphics (0x000046A6) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) Iris(R) Xe Graphics (0x000046A8) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) Iris(R) Xe Graphics (0x00009A49) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) Iris(R) Xe Graphics (0x0000A7A0) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) Iris(R) Xe Graphics (0x0000A7A1) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) Iris(R) Xe Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics (0x00004628) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics (0x000046A3) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics (0x000046B3) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics (0x00008A56) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics (0x00009A78) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics (0x00009B41) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics (0x00009BC4) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics (0x0000A721) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics 600 (0x00003185) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics 620 (0x00003EA0) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics 620 (0x00005917) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics 630 (0x00003E92) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics 630 (0x00003E9B) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics 630 (0x00009BC8) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics 630 Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics 730 (0x00004692) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics 770 (0x00004680) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Intel(R) UHD Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (Intel, Mesa Intel(R) UHD Graphics 600 (GLK 2), OpenGL ES 3.2)
ANGLE (NVIDIA, NVIDIA GeForce GTX 1050 Ti Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce GTX 1060 3GB Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce GTX 1060 6GB Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce GTX 1070 Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce GTX 1650 Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 Ti Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0), or similar
ANGLE (NVIDIA, NVIDIA GeForce RTX 2060 Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 2060 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 2070 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 3050 Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 3050 Laptop GPU Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Laptop GPU Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Ti Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 3070 Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 3070 Ti Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 3080 Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 4050 Laptop GPU Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 4060 Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 4060 Laptop GPU Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 4060 Ti Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 Ti Direct3D11 vs_5_0 ps_5_0, D3D11)
Adreno (TM) 610
Adreno (TM) 618
Adreno (TM) 619
Adreno (TM) 642L
Adreno (TM) 650
Adreno (TM) 710
Adreno (TM) 730
Adreno (TM) 740
Adreno (TM) 750
Apple GPU
Mali-G52 MC2
Mali-G52
Mali-G57 MC2
Mali-G57
Mali-G68 MC4
Mali-G68
Mali-G76
PowerVR Rogue GE8320
PowerVR SGX Doma