Canvas Fingerprinting, Explained
Most people picture browser tracking as cookies: a small file a site drops on your machine and reads back later. Cookies are easy to clear, block, or sandbox per-site, so trackers went looking for something sturdier — a way to derive an identifier from properties of the device itself, without writing anything to disk. Canvas fingerprinting, first described publicly around 2012, is one of the oldest and still most effective techniques in that family.
The basic idea
The HTML5 <canvas> element lets a page draw arbitrary 2D graphics and then
read the result back as raw pixel data. A fingerprinting script draws a
short, specific scene — usually text with an unusual font, a background
color gradient, and a shape or emoji — into an off-screen canvas the user
never sees, then calls toDataURL() or getImageData() to pull out the
rendered pixels.
function getCanvasFingerprint() {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
ctx.textBaseline = "top";
ctx.font = "14px 'Arial'";
ctx.fillStyle = "#f60";
ctx.fillRect(0, 0, 220, 30);
ctx.fillStyle = "#069";
ctx.fillText("fsh.ink fingerprint test 😀", 2, 15);
return canvas.toDataURL();
}A minimal fingerprint draw call — the specific font, fill color, and emoji glyph are chosen because they render differently across GPUs and OSes.
That data URL is then hashed down to a short string. The trick is that the same code, run on two different machines, rarely produces identical pixels — and that inconsistency is the signal.
Why the same drawing instructions produce different pixels
Rendering text and shapes to a canvas isn't a pure, spec-defined operation. The final pixels depend on a stack of things that vary by machine:
- The GPU and graphics driver. Anti-aliasing, sub-pixel rendering, and color management are implemented in hardware and driver code, and different vendors round and blend slightly differently.
- The operating system's font rasterizer. Even with the same nominal font name, glyph hinting differs between Windows (DirectWrite), macOS (Core Text), and Linux (FreeType) — and even between OS versions.
- Installed fonts. If the requested font isn't present, the browser silently falls back to a different one, changing glyph shapes and metrics.
- Browser-level rendering settings. Some browsers apply their own anti-aliasing or color-space handling on top of the OS, and this changes across versions.
None of these differences are visible to the human eye — two canvases can look pixel-identical on screen while differing by a handful of bits in the underlying RGBA buffer. But a hash function doesn't care about visual similarity; a single bit of difference produces a completely different hash. That's what makes the technique so effective: it isn't measuring anything a user would think to hide, and it's stable for a given device configuration across browsing sessions.
How much entropy does it actually add?
On its own, a canvas hash is not usually unique — plenty of people run the same OS, same GPU vendor, and same font set. Panopticlick-style studies generally found canvas fingerprints distinguishing devices into a few hundred to a few thousand buckets, not millions. The reason canvas fingerprinting is still dangerous is that it's almost never used alone. Combined with:
| Signal | Rough entropy contribution |
|---|---|
| Canvas hash | Moderate — clusters by GPU/OS/font stack |
navigator.userAgent / client hints | Low–moderate, and shrinking as browsers freeze UA strings |
| WebGL renderer string | Moderate — GPU model, driver version |
| AudioContext fingerprint | Moderate — depends on audio stack rounding, similar principle to canvas |
| Installed fonts (measured indirectly) | Moderate–high |
| Screen resolution, timezone, language | Low individually, useful combined |
...the combined fingerprint becomes highly specific to one physical device, often unique among all visitors to a site. This is the core move of most fingerprinting libraries: no single signal needs to be unique, because the intersection of several moderately-distinguishing signals usually is.
What actually resists it
A few mitigations matter more than others:
- Fingerprint-resistant rendering. Firefox's
privacy.resistFingerprintingand Brave's fingerprint protections either return a fixed, spoofed image or inject deterministic per-origin noise into canvas reads, so the same page gets a different (but stable-per-session) hash on every device — destroying the signal's usefulness for cross-site tracking without breaking legitimate canvas use. - Read-permission prompts. Some browsers gate
toDataURL()/getImageData()behind a user-visible permission the first time a script tries to read canvas content, on the theory that legitimate uses (image editors, games) are rare enough to prompt for. - Blocking the API entirely via extensions, which is effective but occasionally breaks sites that use canvas for real rendering, not tracking.
Clearing cookies, using private browsing, or rotating IP addresses does nothing here — the whole point of the technique is that it doesn't depend on anything stored client-side. That's worth sitting with: an identifier that survives every action a privacy-conscious user is taught to take is a fundamentally different threat model than a cookie, and it's why fingerprinting keeps showing up in later posts on this site.