Math.tanh, every CSS trig function, and the Web Audio compressor all route through the host libm, so the rounding of a cosine betrays the OS a browser actually runs on. Where the leak lives across V8, Blink, and Web Audio, and what bit-for-bit reproduction of Apple's math library takes to close it.
Fingerprinting is usually about canvas, WebGL, fonts, audio. There is a quieter signal, and it lives in the last bits of a number.
Run this in any console:
Math . tanh ( 0.8 ) // 0.6640367702678491 genuine Linux Chrome (glibc) // 0.664036770267849 genuine macOS Chrome (libsystem_m) // 0.6640367702678489 genuine Windows Chrome (UCRT)
That output is an approximation, and its exact bits depend on the OS that computed it. A genuine Mac runs Math.tanh through Apple’s math library. Linux runs it through glibc. The two disagree on about a quarter of all inputs, usually by one unit in the last place (1 ULP). Windows, through the Universal C Runtime, disagrees with both on a few percent, and on the input above all three land on a different bit.
The same call, run on genuine Chrome 150 across three real machines:
Call Linux (glibc) macOS ( libsystem_m ) Windows (UCRT) Split Math.tanh(0.5) 0.46211715726000974 0.46211715726000974 0.46211715726000974 all three agree Math.tanh(0.7) 0.6043677771171636 0.6043677771171635 0.6043677771171635 Linux alone, 1 ULP Math.tanh(0.8) 0.6640367702678491 0.664036770267849 0.6640367702678489 all three differ, 2 ULP spread Math.tanh(0.9) 0.7162978701990245 0.7162978701990245 0.7162978701990244 Windows alone, 1 ULP
Measured over the DevTools protocol on Chrome 150: Linux (glibc), macOS 26 on Apple Silicon ( libsystem_m ), Windows 11 ( ucrtbase.dll ). tanh(0.5) is one of the roughly three-in-four inputs where everyone agrees, which is exactly why it makes a useless probe. tanh(0.8) is one that separates all three at once.
One tanh call on the right input is a per-OS signature. Claim macOS, return Linux math bits, and you have contradicted your own User-Agent.
This tell is recent. Until Chrome 148, V8 computed tanh itself with a bundled fdlibm port, so it returned the same bits on every OS and leaked nothing. V8 commit c1486295ae5 replaced it with std::tanh , which reads the host libm . It first shipped in V8 14.8.57, which is Chrome 148. Chrome 147 and earlier do not leak here. Chrome 148, 149, and 150 do.
... continue reading