Draw a short string onto a hidden canvas, read back the pixels, and the result is stable on one device and different on the next. That readback is the single highest-entropy active probe in a modern browser fingerprint, and the most attacked. What follows covers how the technique works, where the entropy comes from, what stealth browsers do to defeat it, and the cross-checks that keep it useful for fraud detection even when the canvas output is spoofed.
If you are new to fingerprinting in general, the foundations are in what is a browser fingerprint. If you want the broader catalog of techniques alongside canvas, browser fingerprinting techniques is the companion piece.
Where the technique came from
The canvas fingerprinting idea was published in May 2012 by Keaton Mowery and Hovav Shacham at UC San Diego, in a paper titled Pixel Perfect: Fingerprinting Canvas in HTML5 (paper, UCSD). They observed that the HTML5 <canvas> element, introduced for legitimate 2D rendering, also leaked enough device-specific behavior to identify a browser uniquely. Their paper described how to render text and shapes, read back the pixel buffer, and treat the resulting bytes as a fingerprint that was “consistent, high-entropy, orthogonal to other fingerprints, transparent to the user, and readily obtainable.”
That description still holds. The original paper laid out the four properties that have kept canvas fingerprinting central to the field for more than a decade: it does not require user interaction, it produces stable values across sessions, the values carry many bits of information, and the technique runs invisibly in the background of a normal page load.
The technique in 12 lines of code
The shortest version of a canvas fingerprint is short enough to fit on screen:
const canvas = document.createElement('canvas');
canvas.width = 240;
canvas.height = 60;
const ctx = canvas.getContext('2d');
ctx.textBaseline = 'top';
ctx.font = '14px Arial';
ctx.fillStyle = '#f60';
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = '#069';
ctx.fillText('Cwm fjordbank glyphs vext quiz \u{1F600}', 2, 15);
ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
ctx.fillText('Cwm fjordbank glyphs vext quiz \u{1F600}', 4, 17);
const dataUrl = canvas.toDataURL();
const hash = await sha256(dataUrl);
toDataURL() returns a base64-encoded PNG of the rendered output. On a real device, that PNG is byte-stable across page loads (modulo browser updates or driver changes). Across devices it differs.
A more thorough implementation will run a handful of variants: different fonts, different overlap geometries, a WebGL scene as well as a 2D scene, and an emoji-heavy string that forces the platform’s emoji font into the result. Each variant adds entropy. Hashing them separately and storing the individual hashes is more useful than concatenating them, because the per-variant differences reveal which layer of the rendering stack changed when a fingerprint drifts.
Where the entropy actually comes from
The canvas hash is high-entropy because the path from ctx.fillText to a PNG byte buffer goes through more system-specific stages than most engineers realize. Each stage contributes variation.
Font rasterization. The OS-level font rasterizer turns glyph outlines into pixel grids. macOS uses Core Text. Windows uses DirectWrite. Linux usually uses FreeType, with distribution-specific configuration. Each renders subpixel anti-aliasing and hinting differently.
Installed fonts. When you write font: '14px Arial', the browser asks the OS for Arial. If Arial is not installed (less common on macOS, more common on minimal Linux containers), the browser falls back. The fallback path is platform-specific. An emoji codepoint forces a similar fallback into Apple Color Emoji, Segoe UI Emoji, Noto Color Emoji or whatever else is present.
Compositor. The browser’s compositor combines the 2D canvas pixels with any overlap or alpha blending you have set up. Two ASCII strings drawn with slightly offset positions and a translucent overlay test the compositor’s blending math, which depends on the GPU and the driver.
GPU and driver. When 2D canvas operations are accelerated (almost always in 2026), the GPU runs the rasterization. Different GPUs produce visibly different output for the same input. NVIDIA, AMD, Intel integrated, Apple Silicon and ARM Mali each have distinctive behavior. So do driver versions.
PNG encoder. toDataURL() serialises the pixel buffer to PNG. Chrome, Firefox and Safari each use different PNG encoders, with different default compression levels and chunk ordering. The same pixels produce different PNG bytes across browsers.
A canvas fingerprint, in other words, is the joint result of five separate platform-specific subsystems, each contributing its own variation.
Why a single string matters
Production fingerprints use carefully chosen test strings. Three properties make a string useful:
- Mixed scripts. A combination of Latin letters, a non-ASCII codepoint (often a Cyrillic or CJK character), and at least one emoji forces the browser to consult multiple font families, which in turn surfaces locale-specific installations.
- Subpixel-sensitive geometry. Drawing the same text twice with sub-pixel offsets (offsets that are not whole numbers) forces the rasterizer to make alignment decisions that differ across implementations.
- Translucent overlap. A translucent layer drawn on top of an opaque layer tests the alpha compositor. The result varies with GPU and driver.
The original Pixel Perfect test string was the pangram “Cwm fjordbank glyphs vext quiz” plus an emoji. That choice was not aesthetic. A perfect pangram exercises every Latin glyph, the unusual letter sequences in “Cwm” and “fjordbank” hit kerning and rasterization paths ordinary text never touches, and the emoji forces color glyph rendering.
Why this still works in 2026
Three sources of variation have appeared since Pixel Perfect was published, and they have made canvas fingerprinting more useful over time rather than less.
Emoji evolution. Apple, Google, Microsoft and Samsung release new emoji glyphs on roughly annual cycles. The same codepoint renders differently across recent OS versions. Including emoji in the canvas string lets a fingerprint distinguish iOS 17 from iOS 18, and Android 14 from Android 15, with high confidence.
GPU acceleration of 2D canvas. Every major browser now offloads canvas rendering to the GPU by default. This exposes more hardware-specific variation than the CPU-rendered canvas of 2012, not less. Two devices with identical OS, browser version and font set still differ in canvas output if their GPUs differ.
WebGPU. WebGPU has been shipping in Chrome since 2023 and is now broadly available across the major browsers. It exposes GPU adapter information at higher fidelity than WebGL and adds another distinct active probe that fingerprinting systems can use to verify GPU claims.
What browsers do to defend against it
Browsers have settled on three defensive strategies, each with its own trade-offs.
Blocking (Tor Browser). Tor’s approach is to ask the user before returning canvas data, and to return a blank or constant image by default. This eliminates the fingerprint at the cost of breaking legitimate canvas usage. For a privacy-first browser this is the right call.
Uniformity (Tor and partially Safari). Even when canvas is allowed, Tor tries to make every Tor Browser instance produce the same canvas output by standardizing fonts and rendering paths. Independent tests give this approach the strongest resistance score in real-world fingerprint suites (Brightside AI, fingerprint test comparison).
Randomisation (Brave). Brave calls its approach “farbling” and applies small per-session perturbations to canvas output so the resulting hash differs every page load (Brave, Fingerprint randomization). This is convenient and unobtrusive. It also turns out to be brittle. A 2025 paper at The Web Conference, Breaking the Shield, showed that randomisation can be reversed through statistical re-linking: the noise has a detectable structure that allows the underlying canvas to be reconstructed across sessions (Nguyen et al., WWW 2025).
Randomisation-based defenses, in short, are better than nothing but weaker than they look.
How stealth browsers and bots try to defeat it
Three patterns dominate.
Pre-recorded canvas output. The simplest approach is to record a canvas hash from a real device, then patch HTMLCanvasElement.prototype.toDataURL and getImageData in the bot to return the recorded value. This works against naive fingerprinting that hashes one test string. It fails against systems that hash multiple strings or run a probe the bot author did not anticipate.
Per-test perturbation. Anti-detect browsers like Multilogin, Octo and Dolphin generate plausible canvas output procedurally and add per-session noise. This avoids the pre-recorded fingerprint problem. It does not produce output that is internally consistent with the rest of the fingerprint (the GPU string, the WebGL renderer, the font list). That inconsistency is what catches it; anti-detect browser detection covers this class of tooling in depth.
Hardware emulation. A small number of operations spin up cloud GPUs that match a target consumer GPU and run a real browser inside that environment. This produces canvas output that is genuinely consistent with the claimed device. It is expensive enough that it does not scale to typical bot operations, and operationally it tends to reveal itself in datacenter IP signals at the network layer.
What a server should look for
Each of those attack patterns leaves a statistical trace, and a defender who knows the patterns can check for the traces directly. If you are detecting canvas spoofing rather than just using the canvas hash as an identifier, the signals worth checking:
Hash diversity. If you serve a population of real users, you should observe a long-tail distribution of canvas hashes with a small number of common ones (typical Windows + Chrome configurations) and many uncommon ones. If you suddenly start seeing a small number of hashes account for an unusual fraction of traffic from a single source, that source is probably replaying.
Per-test hash consistency. Run two or three independent canvas probes (different strings, different sizes, with and without emoji). If a session’s results are internally inconsistent with the joint distribution observed across real users (the same GPU should produce the same kind of variation across all probes), the canvas is being faked.
Cross-layer consistency. A canvas hash that looks like a Mac result paired with a User-Agent claiming Windows, a WebGL renderer string claiming an NVIDIA card, and a TLS fingerprint typical of headless Chrome on Linux is a stack-up of contradictions. Any one of those could be coincidence. All four together are not.
Stability under reload. A real user produces an identical canvas hash on page reloads within a session. A randomising stealth browser produces different hashes each time. Detecting that pattern requires only that you sample the canvas more than once and compare.
Emoji evolution check. Render a string containing the latest released emoji. If the result is the no-glyph “tofu” rectangle, the device’s emoji font is older than the claimed OS version. If the result is the new glyph and the claimed OS is too old to have it, the device is reporting a fake OS.
These checks do not need to be sophisticated. They just need to be there. Most fingerprinting systems built in-house run a single canvas probe and store the hash. That gets you spoofed in production within a week of a determined operator showing up.
Where canvas fingerprinting fits in a session
Canvas is one signal among many. It is one of the highest-entropy single signals, which makes it valuable as an identifier component when it works, and it is one of the most attacked, which makes it the wrong primary key for any system that has to resist sophisticated operators.
The architecture that works in production is to collect canvas alongside WebGL, AudioContext, font enumeration, TLS, Client Hints, and behavioral data; resolve them server-side into a stable identity that tolerates per-attribute drift; and run the cross-checks above as their own signals so that a successful spoof is detected even when it does not change the canvas hash itself.
Further reading
- Keaton Mowery and Hovav Shacham, Pixel Perfect: Fingerprinting Canvas in HTML5 (W2SP 2012): cseweb.ucsd.edu/~hovav/papers/ms12.html
- Brave, Fingerprint randomization (the farbling approach): brave.com/privacy-updates/3-fingerprint-randomization
- Nguyen et al., Breaking the Shield: Analyzing and Attacking Canvas Fingerprinting Defenses in the Wild (WWW 2025): hoangdainguyen.com PDF
- Google, Picasso: Lightweight Device Class Fingerprinting for Web Clients: research.google paper