Canvas Fingerprinting: How It Works and What Helps
How Canvas rendering differences become a stable device identifier, why VPNs and private browsing leave it unchanged, and how profile-driven deterministic output works.
BotBrowser Team
Prefer the maintained product doc?
This article has a matching page in the docs center. Use the docs for the canonical setup flow, current flags, and long-term reference.
BotBrowser can make Canvas readback reproducible for an authorized, declared profile-and-seed test. It cannot guarantee anonymity, site acceptance, or identical rendering across hosts and graphics stacks, so this article treats Canvas as one bounded privacy and compatibility signal.
The HTML5 Canvas element exists for drawing. Charts, maps, image editors, signature pads, games, and dashboards all use it, which makes it one of the most widely used web APIs and one that ordinary sites rely on every day.
Canvas also has a side effect. When a browser draws text or shapes onto a canvas, the exact pixels depend on the device's GPU, graphics driver, operating system text rendering, color management, and anti-aliasing. Two computers that run the same drawing instructions can return slightly different pixel data, while one computer returns the same data every time. That repeatability is what makes the output usable as a signal for recognizing a browser.
Tracking research has documented this in the wild. The Princeton Web Census, a large measurement of the top one million websites, found Canvas fingerprinting scripts on more than 14,000 of them. Those figures belong to that study and are not BotBrowser measurements.
Canvas fingerprinting needs no permission prompt, stores nothing on the device, and shows nothing to the visitor. Browsers offer no built-in setting that turns the signal off, and the pixels are produced whether or not a visitor ever sees a page element, so the signal persists unless something changes how Canvas output is produced. The sections below cover how the signal is produced, why common privacy tools leave it unchanged, the different ways Canvas output can be handled, and how to check that the output is stable during an authorized test.
What Canvas Fingerprinting Measures
A fingerprinting script does not care about the picture itself. A typical collection flow has five short steps:
- A script runs on the page or inside an embedded frame, often bundled with an advertising or analytics component.
- It creates a canvas and draws a fixed test pattern: text in several fonts, gradients, overlapping shapes, and semi-transparent colors.
- It reads the pixels back with
toDataURLorgetImageData. - It condenses the pixel data into a short digest.
- It sends the digest to a server together with other browser signals, where it is matched against earlier visits.
The digest is useful to a tracker for three reasons. It stays the same when cookies are cleared, it stays the same behind a different IP address, and it is cheap to collect on any page. A site that sees the same digest again can link the visits, and a tracker that is present on many sites can link visits across unrelated domains.
Canvas is rarely used alone. Scripts combine it with WebGL renderer details, installed fonts, screen properties, timezone, and language. Taken together, signals that are each shared by many browsers can still single out one device. Canvas contributes the part that comes from the graphics and text rendering path, and that part is hard to change by editing other settings.
Reading pixels back is also a normal operation. Charts, photo editors, signature capture, and some CAPTCHAs read pixels for ordinary reasons, so the call alone is not suspicious. The concern is a hidden test pattern whose only purpose is to produce an identifier. The Canvas API documentation on MDN and the canvas section of the HTML Standard describe the interface itself, and both are listed in the public sources below.
Reviewers who audit a site for fingerprinting usually look for a recognizable shape rather than a single API call. A canvas that is created but never attached to the page, a small fixed size, a sentence that uses many different letters, overlapping colored shapes, and an immediate readback followed by a network send are typical signs. None of these proves intent by itself, and a legitimate chart can share some of them, so a reviewer weighs them together with what the script does with the result.
Why the Same Drawing Differs Between Devices
The specifications describe what a drawing call means, not the exact value of every output pixel, so each platform has room to differ. In practice, several layers of the rendering path contribute:
- Font rasterization. Windows, macOS, and Linux each use their own text rendering stack, with different glyph shapes, hinting, and edge smoothing.
- GPU and driver. Gradients, blending, and compositing run through graphics hardware and drivers that can differ in the last bits of each color channel.
- Color management. Operating systems apply different color profiles and gamma handling, which shifts values inside gradients and translucent shapes.
- Anti-aliasing. Edge smoothing varies by platform, driver version, and system settings. The differences are invisible to the eye but present in the pixel data.
- Floating-point math. Curves and geometric operations can round differently across hardware, which changes a few pixels along the edges.
Fonts add one more layer. When a script asks for a font that is not installed, the platform falls back to a default face, and the choice of fallback depends on the operating system and its installed fonts. The same text drawn with the same font name can therefore produce different glyphs on two systems, which is why fonts are treated as a fingerprint surface of their own.
Each difference is tiny, but a fingerprint only needs one pixel to differ. The combination is stable on one machine and varies across machines. It also survives restarting the browser, clearing the cache, or opening a private window, because none of those actions change the graphics stack underneath.
The same logic works in reverse. Two machines with the same operating system version, GPU, driver, and fonts may return identical output, so Canvas on its own narrows the field rather than naming one device. That is the reason trackers pair it with other signals, and the reason a defense that looks only at Canvas leaves the rest of the combination untouched.
This is why Canvas is closely tied to the rest of a device profile. A browser that reports an Apple GPU and a Mac user agent while producing Canvas output typical of Linux text rendering is internally inconsistent, and consistency across signals is exactly what anyone who analyzes fingerprints looks at.
Why VPNs, Private Browsing, and Extensions Fall Short
Each common privacy tool works on a different layer than the one Canvas reads from.
VPNs and proxies. A VPN or proxy changes the network address a site sees. Canvas output is produced inside the browser from local rendering and never travels through the network path, so two devices behind the same VPN still return their own Canvas output.
Private browsing. Private windows discard cookies, history, and site storage when the session ends. They do not change the rendering path, so Canvas output in a private window matches a normal window. The private browsing guide covers what these modes do and do not change.
Extensions that block Canvas. Blocking readback makes calls fail or return empty data. Very few real browsers behave that way, so the block itself can stand out, and pages that use Canvas for legitimate purposes may stop working.
Extensions that add random noise. Random noise produces a new value on every load. That prevents one stable identifier, but a value that changes on every visit is also unusual, and noise added at the scripting layer may not resemble the natural variation between real devices.
Extensions that return fake data. Substituted pixels can break charts, maps, and CAPTCHAs, and they often disagree with the GPU, fonts, and operating system that the same browser reports elsewhere.
Built-in randomization. Some browsers, Brave among them, add small random changes to Canvas output per site. That is better than blocking, but the result may still not line up with the GPU, font, and operating system signals reported by the same browser, and each implementation decides which Canvas operations it covers.
Unequal results across tools. No single tool changes every layer. A VPN changes the network address but not the pixels, private browsing changes storage but not the pixels, and an extension changes what a script sees without changing how the page is rendered. Each helps with one concern and leaves the Canvas signal as it was, which is why a lasting answer has to address the pixels themselves.
Permission prompts. Tor Browser asks before a site can read Canvas image data. That makes the readback visible, but it leaves the decision to the visitor, and a refused request can break the page that asked.
Four Ways to Handle Canvas Output
The approaches differ in what they give up. Two questions make the comparison easy: does the output stay stable for the same setup, and does it stay consistent with everything else the browser reports?
- Blocking. The output is stable because it is always empty, but it is inconsistent with a normal browser and likely to break pages that need Canvas.
- Random noise. The output differs on every load or every origin. That limits linking within the noise scope, but it gives no repeatable result for testing and may not match other signals.
- Fake data. A fixed substitute value is repeatable, but it is unrelated to what the page actually drew, so drawing-dependent features and other signals can disagree with it.
- Profile-driven deterministic output. Canvas output follows the loaded profile and a fixed noise seed, so the same profile and seed give the same result every time, while a different profile or seed gives a different one.
The last option matters for authorized testing and privacy research because it makes Canvas behavior reproducible. If a test shows a change in output, the cause is the profile or seed that was changed, not leftover noise from an earlier run.
Which option fits depends on the goal. Blocking suits a locked-down reading setup where broken pages are acceptable. Random noise suits everyday browsing where the aim is only to avoid being linked across sites. Deterministic output suits work where a result has to be repeated and explained, such as regression checks on a site you operate, or research that compares one configuration with another.
Determinism does not mean the signal disappears. Canvas is one signal among many, and a stable value that disagrees with WebGL, fonts, timezone, locale, or network location can still look inconsistent. That is why the checks in the last section extend past Canvas alone.
How BotBrowser Handles Canvas
The BotBrowser Canvas documentation describes profile-driven protection for Canvas readback. When a profile is loaded, BotBrowser applies deterministic noise to Canvas 2D output at the rendering level rather than through JavaScript injection, and the documentation states that this covers pixel readback and text measurement. Four documented behaviors matter for the scenario above:
- Canvas noise is enabled by default and can be set explicitly with
--bot-noise-canvas=trueor--bot-noise-canvas=false. --bot-noise-seedselects a specific, reproducible noise pattern. Each seed produces a unique but stable Canvas output, and the same seed also applies to other surfaces such as WebGL, WebGPU, and text metrics.- With a profile loaded, BotBrowser uses its built-in font libraries and rendering so teams can compare Canvas output across hosts under the documented profile, seed, display, and graphics conditions; identical rendering across every host or graphics stack is not guaranteed.
- BotBrowser 150 keeps Canvas 2D protection active for standard and wide-gamut color spaces, including
display-p3.
A minimal launch that fixes both the profile and the seed looks like this:
chromium-browser --bot-profile="path/to/profile.enc" --bot-noise-seed=42
Because each seed gives a stable result of its own, a team that runs several sessions can give each session its own seed. Sessions then stay distinct from one another yet repeat exactly when a seed is reused, which is the property a regression test needs.
Keep the setup simple. A second tool that also modifies Canvas output, such as an extension, makes results harder to interpret because two layers then shape the same pixels.
For review, the CanvasLab documentation describes recording the Canvas 2D, WebGL, WebGL2, and WebGPU calls a page makes with --bot-canvas-record-file, so a team can see what a page actually draws and reads before deciding what to test.
Checking Stability and Troubleshooting Differences
A useful check has three steps and needs no custom collection code. Run it only on pages and systems you are authorized to test.
- Launch with a profile and a fixed
--bot-noise-seed, open a public fingerprint testing page, and note the reported Canvas output. - Reload, then close and restart the session with the same profile and seed. The output should match the first run.
- Change the seed or the profile. The output should change.
Keep the scope of a test narrow and documented. Use pages you own or have written permission to test, and treat the outcome as evidence about Canvas output only, not as a verdict on any other fingerprint surface or on how a particular site will react.
Write down the setup next to every result. The BotBrowser version, the profile file, the seed value, the host operating system, and whether the run was headless or headful are enough for a later run to be compared fairly. Without that record, a difference between two runs cannot be traced to a cause.
When the result is not what you expect, the BotBrowser documentation points to a short list of causes:
- The output changes between sessions with the same profile. Without a fixed seed, noise varies per session by design, so confirm the same
--bot-noise-seedvalue. - The output differs between headless and headful runs. Confirm
--bot-noise-canvas=trueand the same profile in both. - The output looks like the raw system output. Confirm the profile loaded correctly and read the startup messages.
- The output differs on another host operating system. Check the profile, seed, display, graphics backend, and font conditions before interpreting the difference; a profile makes the comparison reproducible but does not guarantee identical output across every host.
- Wide-gamut Canvas behaves differently from standard Canvas. Use BotBrowser 150.0.7871.46 or newer with a matching profile package.
Read the first result with care. A single run shows only that the output exists. The comparison across reloads, restarts, seeds, and profiles is what shows whether the output behaves as intended, and a missing change in step three is as informative as a mismatch in step two.
Then check what Canvas does not cover. Compare WebGL renderer values, installed fonts, timezone, locale, and network location against the same profile, because a stable Canvas result next to a timezone or language that disagrees with the profile still gives an inconsistent picture. The articles on WebGL fingerprinting, font fingerprinting, and noise seed reproducibility cover those neighboring signals.
BotBrowser applies profile-driven deterministic Canvas 2D noise at the rendering level, reproducible per --bot-noise-seed under declared conditions, so a team can compare Canvas readback in the authorized testing and privacy-research scenario described here. BotBrowser cannot guarantee that a site will not identify, track, or block the visitor, cannot control a site's Canvas collection scripts or server-side device clustering, and does not replace keeping WebGL, fonts, timezone, locale, display, and proxy assumptions consistent with the same profile.
Public Sources
Related Articles
Take BotBrowser from research to production
The guides cover the model first, then move into cross-platform validation, isolated contexts, and scale-ready browser deployment.