All posts

Diagnostics

Why your Canvas fingerprint changes (and when that’s a problem)

Canvas hashes shift after driver updates, browser upgrades and privacy add-ons. Which changes are normal, which ones make you look like a modified browser, and what to do about each.

By Mango TeamPublished 5 min read

Yesterday a fingerprint test showed one Canvas hash. Today it shows another, and you changed nothing you can think of. Or the opposite: you installed something to "protect" the canvas, and now the hash is different on every reload, which sounds like success. One of these is normal and the other is the most visible flag a browser can raise. This guide explains how the hash is made, which changes are expected, which are not, and what each means for one browser or for many.

How a Canvas fingerprint is made

A script creates an invisible <canvas>, draws a line of text in a couple of fonts, an emoji, some overlapping shapes with transparency, and maybe a gradient. Then it reads the pixels back with toDataURL() or getImageData() and hashes them.

The hash is interesting because the same drawing instructions do not produce the same pixels everywhere. Text is rasterised by the operating system, with different anti-aliasing and hinting on Windows, macOS and Linux. Shapes and blending go through the browser's graphics library and, when hardware acceleration is on, through the GPU and its driver, each with its own rounding. The emoji comes from the OS emoji font. Mowery and Shacham described the technique in 2012, and it has worked ever since because rendering differences are a property of the machine, not something a site has to ask for.

Changes that are normal

What changedWhy the hash movedWhat a site sees
Browser updateThe graphics library changed how it rounds or blendsA new stable value, from the update onwards
GPU driver updateDifferent shader output for the same drawingA new stable value
OS updateText rasterisation or the emoji font changedA new stable value
Laptop switched GPUsIntegrated and discrete GPUs render differentlyTwo stable values, depending on which GPU is active
Hardware acceleration turned offSoftware rendering replaces the GPUA new stable value
A font used by the test was installed or removedDifferent glyphs in the drawingA new stable value

Every line ends the same way: a new stable value. Your machine changed, so its fingerprint changed, and it stays changed until the next update. Sites expect this. A canvas that moves a few times a year, in step with updates to the browser and OS visible in the User-Agent, is what real browsers do.

Changes that are not normal

There is one kind of change that no real machine produces: a canvas that differs between two draws of the same picture in the same session. That happens when something adds noise to the pixels before the script reads them, which is what most canvas "protection" extensions and some privacy browsers do. Sites test for it directly by drawing twice and comparing, or by drawing on-screen and off-screen and comparing, and a mismatch is a stronger signal than any particular hash.

The second tell is the function itself. toDataURL and getImageData are native browser code; a script can check that they still are. An extension that patches them to add noise leaves a function that is no longer native, and a page can see that in one line.

A moving canvas is a modified browser.

A hash that changes because your driver updated is a real machine changing. A hash that changes on every reload is a browser announcing that its output has been tampered with, and that is what gets acted on.

How sites actually use the value

Canvas is rarely used alone. It is one input to a fingerprint identifier and, more importantly, one side of several cross-checks. The canvas output, the WebGL renderer string and the font list all depend on the operating system and GPU, so they have to agree with each other and with the User-Agent. A canvas rendered with macOS text smoothing under a browser that claims Windows, or a GPU string that could not exist on the claimed machine, is a contradiction, and the User-Agent guide shows how many of those cross-checks there are.

For one account, the practical effect is usually nothing until the value becomes inconsistent. For several accounts on one machine, a stable canvas is the problem in the other direction: every profile on the same hardware draws the same picture, so the canvas links them regardless of cookies or IP.

How to check

  1. Open the Lab and read the Canvas row. It draws the test picture twice and checks that the two results match, and it checks that the drawing functions are native.
  2. Reload and compare. The hash should be identical. If it is not, something between the page and the pixels is adding noise.
  3. Compare with the WebGL row and the fonts row. All three should describe the same operating system and GPU.
  4. After a browser or driver update, expect a new hash and check again that it is stable.

Check your own Canvas result

The Lab draws the test twice, checks the drawing functions are native and shows the hash next to your WebGL renderer and font list.

Open the Lab

What to do

If you want privacy for one browser: leave the canvas real. A stable, real canvas is one of millions of similar values and only becomes identifying in combination with everything else. Adding per-draw noise makes you rarer, not more private, because it marks the browser as modified.

If you need several identities on one machine: each profile needs a canvas that is different from the others and identical to itself on every draw. The noise has to be seeded per profile, applied consistently on-screen and off-screen, and delivered by native-looking functions, or the cross-checks catch it. Random noise fails the first test; no noise fails the second.

How Mango Browser handles Canvas

Mango applies seeded noise per profile: each profile has its own stable seed, so its canvas is unique across profiles and identical on every draw, on-screen or off-screen. The two-draw check passes because the two draws match; the cross-profile check passes because the profiles differ. The mode is Noise by default, with a Real option for profiles that should show the machine's own canvas.

The same idea runs through the rest of the rendering layer: WebGL image noise is seeded per profile with a renderer string drawn from a pool of real GPUs for the profile's operating system, audio samples get deterministic noise while silence stays silent, and the font list is drawn from that OS's own pool. Each profile draws one consistent picture of one consistent machine.

Sources

  1. MDN: HTMLCanvasElement.toDataURL()
  2. Mowery & Shacham: Pixel Perfect: Fingerprinting Canvas in HTML5
  3. MDN: Canvas API

See what your browser reveals in the Fingerprint Lab

Keep reading