| Component name | math |
|---|---|
| In the visitorId | yes |
| structuredId group | browser (engine and build) |
| Hashing | always a digest (first 128 bits of SHA-256) |
| Excluded under | no browser class |
| Inner timeout | 1000 ms |
| Added in | signalsVersion 1 |
Computers approximate mathematical functions such as sine or the exponential, and different browsers’ JavaScript engines do it in slightly different ways. For a few carefully chosen inputs, the very last digits of the answer differ. WhiskerJS computes 24 such results and keeps only a short hash of them. It needs no permission and draws nothing; it is pure arithmetic.
What it reads
The window’s Math object, called with fixed inputs:
acos,asinof 0.123124234234234242;acoshof 1e308;asinhof 1;atanhandatanof 0.5;sinandtanof -1e300;cosof 10.000000000123;sinh,cosh,tanh,exp,expm1of 1;log1pof 10;- “polyfill” forms of some of them written with
log,sqrtandexp(for examplelog(1e154 + sqrt(1e154 * 1e154 - 1))for acosh); pow(PI, -100).
That is 24 named results in a fixed order.
How the technique works
- Each function is bound to the window’s
Math. A function that is missing is replaced by one that returns 0, so a missing function changes the value instead of failing. - Each result is written as
name=<number>with JavaScript’s default number printing, and the 24 pairs are joined with;. - The string is always hashed: SHA-256, first 128 bits, 32 hex characters.
The operations, constants and polyfill expressions are adapted from FingerprintJS 5.3.0 (MIT); the notice is kept beside the code and in THIRD_PARTY_NOTICES.md.
Why it helps
The inputs sit where approximation errors show: huge arguments to sin and tan, values near the edge of the floating-point range, and expressions that lose precision. Different engines (V8, SpiderMonkey, JavaScriptCore), and sometimes different versions of one engine, round the last bits differently. The value says little about the device and a lot about the engine build, which is why it is in the browser group.
Stability
- Stable within one browser version on one device: the same inputs always give the same outputs.
- It can change with a browser update, when an engine changes its maths library. That moves the visitorId and the
browserpart of the structuredId. - It does not follow user settings.
Browser support and what blocks it
Every browser has Math, so it is ok everywhere: Chrome, Firefox, Safari, iOS browsers, Brave, Edge. No browser class excludes it. Because all iOS browsers share WebKit’s engine, they are expected to share the same math results with Safari on the same iOS version.
Privacy
The value is a property of the browser’s engine build, shared by everyone on that build. It contains nothing about the user and leaves the library only as a digest.
How WhiskerJS handles noise and failures
| Status | ruleId | When |
|---|---|---|
ok |
the digest of the 24 results | |
unavailable |
no_api |
there is no Math object or Math.exp is not a function |
unavailable |
no_digest |
crypto.subtle is missing, so the value cannot be hashed (never sent raw) |
blocked |
threw, security_error |
a patched Math function throws |
excluded |
caller_excluded |
you excluded it |
timed_out |
timeout, aborted |
budget or abort |
There is no noise to detect: the results are deterministic.
Example value
In headless test browsers in a Linux container on an ARM host, Chromium and Firefox both gave 93c6779db9af8debbf63b6d97bbcbd97 and WebKit gave c5a6fcbb6b7bfc2c6aca4d4345d810bb. A small example, not a measurement of how often engines agree.
Questions and answers
Why would Math.sin give different answers in different browsers?
The standard allows small differences in how functions such as sin, tan or exp are approximated. Engines use different algorithms or libraries, so the last digits can differ for some inputs.
Is the math code taken from another library?
The list of operations, their input constants and the polyfill expressions are adapted from FingerprintJS 5.3.0 under the MIT licence, with the notice kept in THIRD_PARTY_NOTICES.md. The collector code around them is WhiskerJS's own.
Does math tell the CPU apart?
Mostly it tells engines and their versions apart. Two engines can also agree on the same machine: in a container run, headless Chromium and Firefox produced the same math digest while WebKit differed.