Why a FingerprintJS visitor ID changes
People run FingerprintJS, get a visitorId, run it again a week later and get a different one. Or they run it in two tabs and get the same one and assume it is tracking something durable. Both reactions come from not knowing how the value is made.
It is a hash. Everything else follows from that one fact.
Read from source on 2026-07-27 (fingerprintjs/fingerprintjs, src/sources/ and src/agent.ts).
How the ID is produced
export function hashComponents(components: UnknownComponents): string {
return x64hash128(componentsToCanonicalString(components))
}
Roughly forty-one component sources are collected, serialised in a canonical order, and hashed. The identifier is that hash.
So there is no notion of a partial match. Change one component and the entire ID changes, exactly as if everything had changed. There is a separate confidence score, but the ID itself is all or nothing.
The components, and which of them move
The full set, from the source directory:
canvas webgl audio audio_base_latency
fonts font_preferences screen_resolution screen_frame
color_depth color_gamut hdr contrast
inverted_colors forced_colors reduced_motion reduced_transparency
monochrome timezone date_time_locale languages
platform os_cpu architecture cpu_class
hardware_concurrency device_memory touch_support vendor
vendor_flavors user_agent_data apple_pay pdf_viewer_enabled
plugins dom_blockers indexed_db local_storage
session_storage open_database cookies_enabled math
private_click_measurement
Group them by how likely they are to move under a real person, and the instability explains itself:
Moves whenever the hardware setup changes. screen_resolution and screen_frame change when a laptop is docked, an external monitor is plugged in, or the taskbar moves. device_memory, hardware_concurrency and architecture change with the machine.
Moves with the operating system’s own settings. forced_colors, inverted_colors, reduced_motion, reduced_transparency, contrast, monochrome, color_gamut and hdr all read accessibility and display preferences. Someone turning on dark mode or reducing motion changes their ID.
Moves with ordinary software changes. fonts and font_preferences change when any application installs a font. plugins, vendor_flavors and user_agent_data change when the browser updates. dom_blockers changes when someone installs or configures an ad blocker.
Moves with travel. timezone, date_time_locale, languages.
That is why a real user’s ID is not stable across weeks. It is not a flaw in the library, it is what hashing forty-one volatile things produces.
What this means if you are automating
Two things, and they point in opposite directions from what people assume.
An ID that changes between runs of your own tool is a signal, not safety. If your setup produces a different visitor ID on every launch of the same configuration, some component is being randomised per call, not per identity. Real machines do not do that. A tool adding canvas noise on every read, instead of deriving stable noise from a fixed seed, produces exactly this and is detectable by the instability itself.
An ID that is stable per identity and different per identity is the target. Not a stable ID across all your sessions, which is one machine doing everything, and not a new ID every request, which is a machine that transforms between page loads. One consistent machine per identity, for as long as that identity lives.
Deriving the fingerprint from a seed is how you get that. The same seed gives the same forty-one components, so the same hash, run after run; a different seed gives a different hash that is equally ordinary. You can verify it in a minute: run FingerprintJS twice with one seed and confirm the ID matches, then change the seed and confirm it does not.
The Pro version is a different thing
The open-source library computes the hash in the browser. Fingerprint Pro adds server-side signals and identification that this article does not cover, and it deliberately survives some of the changes listed above instead of breaking on them.
So “I pass the open-source one” and “I pass the commercial one” are separate claims, and the second does not follow from the first. Anyone telling you a tool defeats browser fingerprinting, without saying which of the two they measured, has not told you anything.
Checking your own
Run it twice in the same configuration and compare. Then run it in a fresh profile with the same configuration and compare again. Then change one thing you would expect to matter, the seed if you have one, and confirm the ID moves.
If it moves when it should not, read the components, not the ID: the library exposes them, and the one that differs between two runs is your problem.
Short answers to the questions that lead here
Why does my FingerprintJS visitor ID keep changing? Because it is a hash of many components, so any one of them moving changes the whole value. It is not tracking you less, it is being fed different input.
Is a changing ID good for privacy? Less than it sounds. An ID that changes every single request describes a machine that transforms between page loads, which no real machine does, and that pattern is itself detectable.
What makes the ID stable? The same components answering the same way every time. Deriving them from a fixed seed does that by construction.
Does the open-source version equal the commercial one? No. The commercial one adds signals the library does not have and deliberately survives some of the changes that break the open-source ID.
Does canvas noise help? Only if it is stable per identity. Noise added per call makes two reads in one session disagree, which is the cheapest tampering check there is.
See also: which fingerprint fields can be pinned, if you want specific components held still while the rest stays seed-derived, what BotD detects, from the same team but aimed at a different question, and why headless renders different fonts, since fonts and font_preferences are two of the forty-one.
From the notes of invisible_playwright. Both the open-source library and the commercial one are release gates for this project, and the difference between them cost us enough time to be worth spelling out.