A visitor ID is supposed to be stable. That’s the whole value: the same browser gets the same identifier tomorrow, after cookies are cleared and in a private window. Yet anyone who looks at real data finds browsers that seem to become a different device for no obvious reason. That’s drift. Understanding it is the difference between rules that work and rules that pester your best customers.
Where the ID comes from
A browser has no hardware serial number that a web page can read. A visitor ID is inferred from many signals: how the device draws a canvas, WebGL parameters and renderer, audio processing output, installed fonts, screen properties, timezone, languages, platform, CPU core count, memory class, media codecs and more. Browser fingerprinting entropy explains why the combination is distinctive even though each part isn’t.
The catch is that none of those inputs is guaranteed to stay fixed.
What makes the signals move
Browser updates
Major browsers release new versions every few weeks. A new version can change text rendering, canvas anti-aliasing, the list of supported codecs or the values a privacy feature reports. One update can shift several signals at once.
Graphics drivers and OS updates
WebGL output and canvas rendering depend on the GPU driver. A driver update, or an OS update that bundles one, changes those signals even though the hardware is the same.
Hardware and peripherals
An external monitor changes screen size and color depth. A new laptop is a new device, correctly. A docking station can switch which GPU is rendering.
User environment
Installing design software adds fonts. Traveling changes the timezone. Changing the system language changes the reported languages.
Privacy features
Some browsers deliberately add noise to canvas or audio output, or randomize it per session or per site. Strict tracking protection may block an API entirely. A blocked API carries no information. The Prynt agent marks such values as sentinels and leaves them out of matching altogether, so a blocked canvas never counts as a match or a mismatch.
How matching absorbs drift
If an ID were just a hash of all those signals, every change would produce a new ID. Prynt’s matching engine is built around drift instead:
- Candidates come from several indexes. That includes a coarse bucket of stable traits as well as exact hashes, so a device whose canvas hash changed is still considered.
- Signals are scored by similarity, not equality. Canvas output is compared perceptually, font sets by overlap, WebGL parameters field by field, audio by numeric closeness.
- Signals are weighted. High-entropy, stable signals count more than volatile ones like timezone.
- Matches heal. When an identification clears the match threshold, the device record takes on the new values, so the next update is measured from the latest state.
- Signal versions are recorded. The agent reports which version of its collectors produced each identification, and the device record stores it, so a change in how signals are collected is visible rather than silent.
A typical browser update moves a few signals a little, and the match holds. A new machine, or an update that moves almost everything at once, produces a new ID. That’s often the correct answer.
Confidence is the uncertainty, made explicit
Every identification returns a confidence object:
const { visitorId, confidence } = await prynt.identify();
// confidence = { score: 0.0–1.0, level: 'low' | 'medium' | 'high' }
It tells you how sure the system is that this browser is the visitor the ID belongs to. High confidence means a strong match on stable signals. Low confidence means a weak match or a brand-new visitor with little to go on. What is a confidence score covers how it’s computed and calibrated.
Two mistakes are common:
- Ignoring it. Treating every ID the same means a weak match carries as much weight as a strong one.
- Filtering on it alone. Throwing away low-confidence events loses exactly the private-mode and locked-down browsers abusers favor.
The right use is as a weight on decisions that depend on recognition.
Designing rules that tolerate drift
For returning-user recognition
The risk is a false “new device” alert that sends a loyal customer through step-up after a routine update.
- Keep a history of visitor IDs per account, not just one.
- On login, check whether the current ID is in that account’s history. If it isn’t and confidence is low, look at the rest of the context. Same IP range, same city, same browser family and no risk signals point to drift, not takeover.
- Use a soft step-up such as an email code rather than a block. When the user passes, add the new ID to the account’s history.
const event = await prynt.getEvent(requestId);
const known = await db.accountDevices.has(user.id, event.visitorId);
if (!known && event.confidence?.level !== 'high' && !event.smartSignals?.tampering?.result) {
return stepUp(user, { reason: 'unrecognized_device_low_confidence' });
}
For multi-account limits
Drift cuts the other way here. If an abuser’s device drifts to a new ID, the account count resets to zero. Two things help:
- Use the account-side history too.
accountsOnDevicecounts the accounts attached to the current device. The risk engine also tracks how many devices one account uses (deviceSpread, flagged above five distinct devices in a day). Many devices per account and many accounts per device are both worth watching. - Look at clusters, not single IDs. Accounts that share a payment method, a normalized email, or a series of drifted IDs from the same network form a cluster that survives drift. Device fingerprinting in 2026 discusses the identity-graph approach.
For deny lists
A deny-listed visitorId stops matching if the device drifts far enough. Deny the account (linkedId) as well as the device, and report the outcome through POST /v1/outcomes with the requestId (or the linkedId and visitorId). The label is then recorded against the account and its IP as well as the old device ID, so it still applies when the same account shows up from a drifted device.
Measuring drift in your own traffic
You can see how much drift affects you with data you already have:
- For logged-in users, count distinct visitor IDs per account per month.
- Plot the distribution. Most accounts should have one or two IDs, with a tail of multi-device users.
- Check the dates of browser releases against spikes in new IDs among known accounts. Matching should keep those spikes small.
- Compare the confidence distribution of first-seen IDs on known accounts with that of all events. Drifted re-identifications should mostly sit in the lower bands.
If routine browser releases produce sharp spikes, tell the vendor, whoever it is. That’s exactly what drift handling should absorb.
Wrap-up
Visitor IDs drift because the browser underneath them changes. Matching absorbs most of it, confidence tells you about the rest, and account context fills the gaps. Write rules that treat an unrecognized, low-confidence ID as a question rather than a verdict.
Try it free
Prynt is device intelligence with a free tier — visitor IDs, bot & fraud Smart Signals, and behavioral biometrics, powered by a cross-site network. Start free.