JavaScript and WebAssembly read the same CPU
Check id cpu-arch-nan-wasm-vs-js
What an ordinary browser yields
The JavaScript and WebAssembly readings of the quiet-NaN sign bit agreeing. N/A where WebAssembly is unavailable or refused (severity: warn).
What a detector infers
IEEE-754 leaves the sign bit and payload of a quiet NaN to the implementation, and the two dominant architectures chose differently: x86 sets the sign bit on the default quiet NaN (0xFFF8000000000000 for f64), ARM clears it (0x7FF8000000000000). Dividing a runtime zero by itself and reading that bit therefore names the processor. The row cpu-arch-nan-vs-uach already compares that reading against the architecture Client Hint - but it reads the bit with `new Uint32Array(new Float64Array([z/z]).buffer)[1]`, and every object in that expression belongs to the page world. MEASURED on this machine, on this page: redefining Float64Array so that it clears the sign bit of a NaN made that row read "arm" on genuine x86 hardware. It simply believed the override. That is a hole in a deployed check, demonstrated rather than supposed. WebAssembly closes it. The same division compiled to a machine instruction, with the result reinterpreted as an integer inside the module, never passes the bit through a JavaScript object a page could have replaced - measured, the same override that flipped the JavaScript reading to arm left the WebAssembly reading at x86. A 43-byte module is enough, hand-assembled because browsers ship no assembler: (func (export "s") (param f64) (result i32) local.get 0, local.get 0, f64.div, i64.reinterpret_f64, i64.const 63, i64.shr_u, i32.wrap_i64). The dividend is a PARAMETER, not a literal, for the same reason the older row uses Math.random()*0 - V8 constant-folds a literal 0/0 to the canonical NaN with the sign clear, which would make every x86 machine read as ARM. Nothing here is compared against a table of architectures: both readings are taken live on the machine running the audit, so the row asserts only that one processor gives one answer, and there is no constant in it to go stale. Technique: Scrapfly, which also documents relaxed-SIMD operations (f32x4.relaxed_min, i32x4.relaxed_trunc_f64x2) as a second architecture oracle - not implemented here, because relaxed SIMD is unevenly available and the NaN bit already answers the question.
How to resolve it
Stop rewriting the typed-array path used to read NaN bits. It is a surface a page can reach, so patching it produces exactly this contradiction - a machine whose two accounts of its own floating-point unit disagree, which is rarer and more legible than whichever architecture was being hidden. If a different architecture is genuinely required, run on that architecture.
Read in the wild by
Scrapfly
Documents the quiet-NaN sign bit as a CPU architecture oracle reachable from both JavaScript and a small WebAssembly module, and details what spoofing an ARM persona on x86 would require across V8's compiler tiers.
x86 f64 quiet NaN 0xFFF8000000000000 vs ARM 0x7FF8000000000000; f32 0xFFC00000 vs 0x7FC00000
How WebAssembly leaks your CPU architectureEvery attribution traces to a published artifact. See the sources and their limits.
Other checks in Engine & OS oracles
- The measured JS engine matches the engine the User-Agent claims
js-engine-identifier-vs-uaTwo values are read straight out of the JavaScript engine itself: the character length of the RangeError message thrown by (-1).toFixed(-1)… - Math.tanh rounds the way the claimed OS's libm rounds
math-tanh-os-vs-uaIEEE 754 fixes how a double is stored but does not require transcendental functions to be correctly rounded, so every operating system's C… - which OS hyphenation engine the browser hyphenates through
hyphenation-engine-osWhen CSS asks a browser to hyphenate a long word, the break points come from a dictionary supplied by the operating system — and the… - the CPU architecture matches the architecture the browser claims
cpu-arch-nan-vs-uachIEEE-754 fixes the exponent and the quiet bit of a NaN but leaves the sign bit to the hardware, and the hardware disagrees: an x86 FPU… - the three heap numbers describe a possible heap
jsheap-triplet-orderingThis check replaces a retired one, and the swap is the lesson. - the platform's system font belongs to the operating system claimed
system-font-vs-claimed-osThe CSS2 system-font keywords — caption, icon, menu, message-box, small-caption and status-bar — are resolved by the HOST platform's font… - typefaces the claimed OS ships as separate faces measure as separate faces
claimed-os-typefaces-are-distinctA font request is answered by the host's font stack, not by the browser and not by anything the user agent sets, so changing…
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 Engine & OS oracles 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 Engine & OS oracles — the family cpu-arch-nan-wasm-vs-js belongs to.