Skip to content
All fingerprint checksClocks & ledgers

the monotonic clock and the wall clock name the same instant

Check id epoch-vs-monotonic-clock

What an ordinary browser yields

performance.timeOrigin + performance.now() reconstructs Date.now() to within a few hundred milliseconds — they are two readings of one instant.

What a detector infers

A browser carries two physically different clocks: a monotonic counter anchored at performance.timeOrigin, and the wall clock behind Date.now(). The specification welds them — performance.now() is DEFINED as the distance from timeOrigin — so timeOrigin + now() reconstructs Date.now() by construction. Anyone who mocks time mocks one of them. Puppeteer's clock.install(), CDP's virtual time policy, and anti-detect browsers that shift the epoch to match a proxy's locale all hook Date and leave timeOrigin reading the real navigation instant. This is the purest one-fact-two-sources case on the page: you never need to know what a normal browser looks like, only that a browser must agree with itself. The engineering around it is what makes it fair. It samples fifteen times and keeps the tightest Date/perf/Date sandwich, so a preempted read on a loaded machine does not become evidence. Its tolerance is sized for Firefox's resistFingerprinting and Tor, which clamp timeOrigin, now() and Date.now() to a 100ms quantum INDEPENDENTLY — three clamped terms stack into a few hundred milliseconds of entirely legitimate error, so the margin is a measurement, not padding. And when the two do disagree it samples again 700ms later: a gap that MOVED is the system clock being stepped underneath us by an NTP correction, and is reported rather than scored; only a gap that holds steady is a contradiction. The document-age gate matters most of all. On macOS and Linux the monotonic base does not advance across system suspend while the wall clock tracks real time, so a laptop that slept eight hours with this tab open wakes with an eight-hour delta. A check that cannot tell a sleeping laptop from a liar has no business scoring anyone.

How to resolve it

performance.now() is defined as the distance from performance.timeOrigin, so the pair cannot drift apart on its own. Hooking Date.now, or installing a fake clock, moves one and leaves the other anchored to the real navigation. If time must be controlled, control it at the layer both clocks read from rather than patching one API and hoping nobody solves for the other.

How automation gets caught, layer by layer

Other checks in Clocks & ledgers

Who builds this test

Clearcote is a browser built for fingerprint coherence

It is a Chromium fork, maintained by the same people who wrote this reference. It ships as a compiled browser rather than as a stealth script injected into someone else's — which is a description of how it is built, and is not an argument about how it behaves on this check.

This audit takes no position on how Clearcote scores on Clocks & ledgers checks, on this one, or anywhere else. It has no baseline corpus of other people's fingerprints to rank you against and no vendor scoreboard — nearly every check is self-referential, asking one browser the same question through two independent APIs and reporting whether both answers can be true at once. It runs identically on any browser, including ours. Run it on yours and read the result yourself.

See the other checks in Clocks & ledgers — the family epoch-vs-monotonic-clock belongs to.