visualViewport agrees with innerWidth/innerHeight
Check id visual-viewport-vs-window
What an ordinary browser yields
At scale 1 the difference between innerWidth and visualViewport.width is between 0 and about 20 pixels — a scrollbar gutter and nothing else.
What a detector infers
window.innerWidth and visualViewport.width are two independent readings of one viewport, and they are related by exactly one thing: the classic scrollbar gutter, which innerWidth includes and the visual viewport excludes. That makes the GAP between them the signal, not either value. At scale 1 the difference can only be a scrollbar, because both numbers describe the same surface — one measured by the layout engine, the other reported by the compositor. The gutter is not hard-coded but MEASURED, with a private probe element, and that detail is the whole check. A scrollbar is a fixed physical widget sized from the device scale factor, which does not include page zoom, while innerWidth and visualViewport.width are both divided by the zoom factor — so the gutter's width in CSS pixels GROWS as you zoom out, roughly fifteen pixels at 100% but thirty at 50% and sixty at 25%. A fixed CSS-pixel bound therefore fails an honest Windows or Linux Chrome that is merely zoomed out, and the pinch-zoom guard never catches it because desktop page zoom leaves visualViewport.scale at exactly 1. The probe is also deliberately independent of innerWidth rather than derived from it: a rewritten innerWidth would shift both sides of a derived comparison by the same amount and cancel itself out. A viewport size rewritten in JavaScript moves one of the two and not the other, and the gap immediately stops being scrollbar-shaped. On a stock Chrome this was measured at innerWidth 1624 against visualViewport.width 1614: a ten-pixel overlay scrollbar, exactly as it should be. The check declines when the page is pinch-zoomed, because the visual viewport is the zoomed viewport by design and the two readings are then legitimately scaled apart rather than contradictory — that is the difference between a signal and a false positive. Kasada reads seven visual_viewport_* probes; this is the coherence that makes them worth reading.
How to resolve it
Do not rewrite innerWidth/innerHeight. The visual viewport is reported by the compositor and keeps describing the real surface, so patching the window properties only separates two numbers that must stay a scrollbar apart. Size the actual window instead — at launch, so both readings derive from the same surface.
Read in the wild by
Kasada
Reads the visual viewport across seven probes, alongside window.inner*, giving two views of one viewport.
visual_viewport_width / _height / _scale / _offset_top / _offset_left / _page_top / _page_left
Kasada ips.js teardown — the full 427-probe listEvery attribution traces to a published artifact. See the sources and their limits.
Nearby checks in Screen & display
- the outer window fits within the screen
window-boundsThe check asserts only the zoom-invariant bound: window.outerWidth/outerHeight must be non-zero and must fit within the screen. - a non-touch desktop has a fine pointer + hover
pointer-hover-desktopThis check applies only when the UA identifies a desktop platform (Windows, Macintosh, Mac OS X, X11, Linux, CrOS, and not Android/Mobile)… - maxTouchPoints coheres with pointer media
touch-pointer-coherenceA one-directional coherence test: if navigator.maxTouchPoints is greater than zero, matchMedia("(any-pointer: coarse)") is expected to… - screen dimensions resolve via native prototype getters
screen-descriptorsFor width, height, availWidth, availHeight, colorDepth and pixelDepth this asks two questions: does the screen instance carry an own… - screen.orientation agrees with the screen's own dimensions
orientation-vs-screen-dimsscreen.orientation.type and screen.width/height are both device-space values sourced from the same display metrics, so they must agree: a… - a mobile UA has a real touch stack
touch-createevent-coherenceThis check asserts in one direction only: if navigator.userAgent matches iPhone, iPad, iPod or Android, then the environment must also have… - the CSS layout viewport width equals window.innerWidth
css-viewport-vs-innerwidth-coherencewindow.innerWidth and the width CSS media queries evaluate against are the same quantity read two ways. matchMedia("(min-width: Npx)") is… - navigation timing milestones run in their spec-defined causal order
nav-timing-orderAkamai Bot Manager v2 devotes an entire signal category to timing — navigation timing, resource timing, first paint, DOM-ready — and this…
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 Screen & display 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 Screen & display — the family visual-viewport-vs-window belongs to.