What it measures
Four pixel offsets, each rounded to a small fixed bucket: top = screen.availTop (OS menu bar or top-taskbar), left = screen.availLeft (sidebar dock or left-taskbar), right = screen.width − screen.availWidth − screen.availLeft (sidebar dock on the opposite edge), bottom = screen.height − screen.availHeight − screen.availTop (bottom dock or bottom-taskbar). The exact rounding granularity is part of the internal recipe and not part of the public contract.
Distinct from the screen signal. screen captures width / height / devicePixelRatio; screen_frame captures the chrome around them. Two devices with identical resolutions still produce different screen_frame hashes if one has the Dock on the bottom and the other on the left.
Why round
OS-chrome auto-hide behaviours move the underlying offsets across a small range as the user hovers or animates the dock or taskbar. The rounding step is sized so that the hash stays stable across hover state and minor browser-chrome variance between fullscreen and split-view modes, capturing 'dock is on this edge' rather than the millisecond-by-millisecond geometry.
Negative offsets (which can occur when a browser window straddles monitor boundaries) are clamped to zero before bucketing. This protects the hash from garbage in multi-monitor edge cases.
Confidence rules
| Confidence | Trigger |
|---|---|
| normal | All six screen fields readable as numbers; the Firefox resistFingerprinting check is false |
| degraded | All fields readable but the Firefox resistFingerprinting check is true, so every offset collapses to 0 by Firefox's design |
| absent | globalThis.screen is undefined or required fields are non-numeric |
Things worth knowing
- On mobile (iOS Safari, Android Chrome), all four offsets are typically 0 because the web view fills the screen. Hash is the all-zero bucket; near-zero entropy on phones but a useful tablet-vs-phone discriminator (iPad in split-view mode reports non-zero offsets).
- OS-level 'Always show menu bar' toggles change the top offset, so the hash changes when the user toggles the setting. This is real user-preference entropy.
- DPI scaling does not affect the values, because offsets are reported in CSS pixels independent of devicePixelRatio. A 2× retina display reports the same offsets as a non-retina display at the same logical size.
- Dev-tools docked to the page does NOT affect screen.*; it modifies window.innerWidth only. Hash stays stable across opening / closing dev tools.
- FingerprintJS collects screen_frame (their sources/screen_frame.ts uses replaceNaN rounding); Thumbmark does not. Adopting it brings us to FingerprintJS parity and net-positive vs Thumbmark.
Last reviewed 2026-06-04

