Canvas and WebGL Fingerprinting
How Canvas Fingerprinting Works
Canvas fingerprinting uses the HTML5 Canvas API, which was designed for drawing 2D graphics in the browser. The technique works by creating a hidden <canvas> element, executing a series of drawing commands, and then reading back the rendered pixel data. The key insight is that identical drawing instructions produce slightly different pixel outputs on different hardware and software configurations.
A typical canvas fingerprinting script draws a carefully designed test image that exercises multiple rendering pathways. The image usually includes text rendered in several fonts (because font rasterization varies across operating systems and font rendering engines), geometric shapes with gradients and transparency (because blending algorithms differ), curved lines with anti-aliasing (because anti-aliasing implementations vary by GPU), and sometimes emoji characters (because emoji rendering differs dramatically between operating systems).
After drawing the image, the script calls canvas.toDataURL() to extract the rendered image as a base64-encoded PNG string, or getImageData() to read the raw RGBA pixel values. The output is then hashed (typically using MurmurHash, CRC32, or MD5) to produce a compact fingerprint identifier. Two browsers that produce identical pixel output will generate the same hash, while browsers on different hardware will produce different hashes.
The rendering differences that drive canvas fingerprinting come from multiple layers of the graphics stack:
GPU hardware is the most fundamental source of variation. Different GPU architectures (NVIDIA, AMD, Intel, Apple Silicon) use different floating-point precision, different rasterization algorithms, and different shader implementations. Even GPUs from the same manufacturer but different generations produce subtly different output.
Graphics drivers add another layer. The same GPU with a different driver version may produce different rendering because driver updates change optimization strategies, fix rendering bugs, or alter anti-aliasing behavior. On Windows, NVIDIA's proprietary drivers produce different output than the generic Microsoft drivers for the same GPU.
Font rendering is a major differentiator because each operating system uses a different text rasterization engine. Windows uses DirectWrite (or the older GDI) with ClearType sub-pixel rendering. macOS uses Core Text with its own sub-pixel anti-aliasing. Linux distributions use FreeType with configurable hinting and anti-aliasing settings. These engines produce visibly different text at the sub-pixel level, and the differences are captured by the canvas fingerprint.
Color management affects how colors are converted between color spaces. Browsers with color management enabled (like Safari, which has had it for years, and Chrome, which enables it on some configurations) apply ICC profile transforms that alter pixel values compared to browsers with color management disabled.
Research has shown that canvas fingerprinting alone can distinguish between 80-90% of desktop browsers. When combined with other fingerprinting signals, it becomes extremely powerful. The technique is deployed on a significant percentage of the top websites, with studies finding canvas fingerprinting scripts on over 5% of the Alexa top 100,000 sites.
How WebGL Fingerprinting Works
WebGL fingerprinting extends the rendering-based approach into 3D graphics, using the WebGL API (and increasingly WebGL 2 and the emerging WebGPU standard). WebGL fingerprinting operates at two distinct levels, each providing different types of identifying information.
Parameter-Based WebGL Fingerprinting
The first level queries the WebGL context for configuration parameters and capabilities without rendering anything. The most valuable query is the WEBGL_debug_renderer_info extension, which exposes two strings: the unmasked vendor (typically the GPU manufacturer or "Google Inc." when running through ANGLE) and the unmasked renderer (the specific GPU model and driver details). A typical renderer string looks like ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 Direct3D11 vs_5_0 ps_5_0, D3D11), which directly reveals the GPU model, the graphics API in use, and the shader model version.
Beyond the renderer info, WebGL exposes dozens of capability parameters: maximum texture size, maximum viewport dimensions, maximum vertex attributes, supported extensions, shader precision format ranges, and aliased line width ranges. Each parameter varies across GPU and driver combinations. A script that queries all of these parameters and concatenates the results produces a fingerprint that is highly distinctive, even without rendering a single pixel.
On some browsers and configurations, the WEBGL_debug_renderer_info extension is restricted or unavailable. Firefox hides it behind a preference flag. Some privacy extensions block it. When the extension is unavailable, the parameter-based approach still works using the other WebGL parameters, but with reduced entropy.
Rendering-Based WebGL Fingerprinting
The second level renders a 3D scene and reads back the pixel data, similar to canvas fingerprinting but operating through the 3D graphics pipeline. The script creates a WebGL context, sets up a scene with specific geometry, lighting, textures, and shading, renders it to a framebuffer, and reads back the pixel data using readPixels(). The rendering output varies across GPU and driver combinations because of differences in shader compilation, floating-point precision, texture filtering, and rasterization algorithms.
Rendering-based WebGL fingerprinting is more distinctive than parameter-based fingerprinting because it captures the actual behavior of the GPU rather than just its reported capabilities. Two GPUs that report identical parameters may still render a complex 3D scene with measurably different pixel outputs due to differences in their shader implementations or floating-point units.
The combination of parameter-based and rendering-based WebGL fingerprinting produces a highly unique identifier. When stacked on top of canvas fingerprinting, the two rendering techniques together make up the strongest component of most modern fingerprinting systems.
Canvas and WebGL in Headless and Automated Browsers
Headless browsers present a particularly problematic canvas and WebGL fingerprint for automation. When Chrome runs in headless mode, it uses SwiftShader, a software-based GPU emulator, instead of the actual hardware GPU. This means the WebGL renderer string reports "Google SwiftShader" rather than a real GPU model, and the canvas and WebGL rendering output matches SwiftShader's software rendering rather than any real hardware. Since no real user runs SwiftShader as their primary GPU, this fingerprint immediately identifies the browser as headless and automated.
Chrome's newer "headless=new" mode (available since Chrome 112) improved this situation by using the same rendering pipeline as headed Chrome. In this mode, the browser uses the actual GPU (or the system's default rendering backend), which produces a realistic canvas and WebGL fingerprint. However, the system still needs a GPU available, and on Linux servers without a display, this may still fall back to software rendering.
Playwright and Puppeteer scripts running on cloud servers face the same challenge. Unless the server has a GPU or a properly configured virtual display (using Xvfb with GPU acceleration), the rendering fingerprint will not match a real desktop browser. Some automation practitioners solve this by running browsers on GPU-equipped cloud instances (like AWS g4dn or g5 instances with NVIDIA GPUs), which provide genuine hardware rendering at a higher infrastructure cost.
Antidetect browsers approach this differently by intercepting the rendering output and replacing it with pre-captured or synthesized output that matches the claimed GPU. When a fingerprinting script calls toDataURL() or readPixels(), the antidetect browser returns pixel data consistent with the GPU specified in the profile, regardless of what GPU is actually installed in the machine. The best implementations do this at the engine level, making the substitution invisible to detection scripts that check for JavaScript-level interception.
Defending Against Canvas and WebGL Fingerprinting
Several approaches exist for mitigating rendering-based fingerprinting, each with different tradeoffs.
Noise injection adds random, imperceptible perturbations to the canvas output. Brave browser uses this approach, adding a small amount of random noise to the pixel data returned by toDataURL() and getImageData(). The noise is too small to affect the visual appearance of the canvas, but it changes the fingerprint hash on every page load, preventing cross-session tracking. The limitation is that sophisticated fingerprinting scripts can detect noise injection by rendering the same image multiple times and comparing the results. If the outputs differ, noise is being applied.
API blocking prevents fingerprinting scripts from accessing the canvas or WebGL APIs entirely. Browser extensions like CanvasBlocker can intercept calls to toDataURL(), getImageData(), and readPixels(), either blocking them or returning blank data. This effectively prevents canvas and WebGL fingerprinting, but it also breaks legitimate websites that use these APIs for their intended purpose, such as image editors, games, data visualizations, and maps. Additionally, blocking the APIs creates a distinctive fingerprint of its own, because a browser that refuses to render canvas content is unusual.
Uniform rendering is the approach taken by the Tor Browser. By running on a standardized software rendering backend and using a fixed set of fonts and rendering parameters, every Tor Browser produces identical canvas and WebGL output. This makes all Tor users look the same, eliminating the fingerprint as an identifier. The tradeoff is reduced rendering performance and the inability to use hardware GPU acceleration.
Profile-based spoofing is the approach used by antidetect browsers for automation. Rather than blocking or randomizing the rendering, the browser returns output consistent with a specific, claimed GPU and driver combination. This is the most effective approach for automation because the fingerprint looks like a real device rather than a blocked or randomized one, and it passes consistency checks that compare the rendering output to the claimed WebGL renderer string.
Testing Your Canvas and WebGL Fingerprint
BrowserLeaks.com provides dedicated tests for both Canvas and WebGL fingerprinting. The Canvas test shows you the exact image rendered by the fingerprinting script, the resulting hash, and how unique that hash is in BrowserLeaks' database. The WebGL test shows your renderer and vendor strings, all supported extensions, parameter values, and the rendering-based fingerprint.
For automation testing, compare the Canvas and WebGL outputs from your automated browser against a real browser on the same machine. If they differ (which they will if you are using headless mode or a different rendering backend), you know the fingerprint is detectable. Then apply your chosen mitigation (antidetect browser, stealth plugin, or GPU-equipped server) and test again. The goal is not to produce a unique fingerprint, but to produce one that is consistent with the claimed browser and device configuration.
CreepJS adds another layer of testing by checking whether the canvas and WebGL outputs are consistent with each other and with other browser attributes. A canvas fingerprint that matches an NVIDIA GPU but a WebGL renderer string reporting Intel integrated graphics is an inconsistency that CreepJS will flag.
Canvas and WebGL fingerprinting are the most powerful identification techniques because they capture hardware-level rendering differences that are extremely difficult to spoof. For automation, the key is ensuring your rendering output is consistent with your claimed device, whether through antidetect browser profile management, GPU-equipped servers, or careful stealth configuration.