Skip to content
Anti-Bot

What Is Math & JS Engine Fingerprinting?

Pim

Pim · Scrappey Research

July 30, 2026 6 min read

What Is Math & JS Engine Fingerprinting? — conceptual illustration
On this page

Math fingerprinting identifies a browser by running math functions (sin, cos, tan, exp, log, pow) on fixed inputs and reading the very last bits of the answers. Those final bits depend on the CPU's floating-point unit (the chip's math hardware), the system math library (libm - the C code that computes these functions), and the JavaScript engine's own shortcuts. The results are the same every time on a given machine, so they make a stable, hard-to-fake signal - and they reveal the real engine even when the User-Agent (the browser's self-reported identity) lies about it.

Quick facts

ReadsLast bits of Math.acos, atanh, expm1, sinh, pow, etc.
Varies byCPU FPU, libm version, V8 vs SpiderMonkey vs JavaScriptCore
CostMicroseconds — runs on every page load
RevealsA "Safari" UA running on V8 (i.e. a faker)
RelatedWASM timing, hardware concurrency, device memory

Why the same formula gives different bits

The IEEE-754 standard (the rules for how computers store decimals) only promises exact results for basic operations: +, −, ×, ÷, and sqrt. Functions like Math.tan or Math.expm1 are left implementation-defined in their last bit or two - meaning each engine is free to round them slightly differently. Chrome's V8, Firefox's SpiderMonkey, and Safari's JavaScriptCore each ship their own math routines, and those routines may hand the work off to the platform's libm. The result: Math.tan(1e300) or Math.sinh(1) ends in hex digits that effectively name the engine + OS combination.

Because the answer is identical on every run of a given machine, it drops cleanly into a composite browser fingerprint with no random noise to filter out.

How it exposes spoofed browsers

This is the signal that catches lazy User-Agent spoofing. If a scraper claims a Safari UA but is actually running headless Chrome (Chrome with no visible window, usually on a server), the math probes return V8's values, not JavaScriptCore's. A lie detector compares the math signature against the engine you claim to be and flags the mismatch. The same trick exposes Chrome-on-Linux pretending to be Chrome-on-Windows when combined with other OS signals.

There is no JavaScript-level fix: you cannot reimplement libm to match a different platform without reimplementing the whole engine. Real consistency only comes from running the actual browser + OS you are claiming — which is why anti-detect stacks lock down the entire environment, not just the UA string.

Why you cannot spoof your way out

The differences exploited here come from the JavaScript engine and the CPU's floating-point unit — the last bits of Math.tan(-1e300) or Math.sinh() differ between V8 (Chrome), SpiderMonkey (Firefox) and JavaScriptCore (Safari), and again across hardware. You cannot fake these results convincingly from inside a content script (the JavaScript a site can run in the page) without re-implementing the math, and any wrapper you add is itself detectable. So the engine signature has to genuinely match the browser you claim to be.

That makes engine fingerprinting a coherence test more than a value test: a tool running the SpiderMonkey engine must present a Firefox identity, not a Chrome one, or the math bits and the User-Agent contradict each other. This is why Camoufox is built on Firefox and reports as Firefox — and why bolting a Chrome User-Agent onto a non-V8 runtime is caught instantly by a lie detector.

Why the last bit is not specified

The ECMAScript specification is precise about arithmetic on doubles, but deliberately loose about transcendental functions. Math.tanh, Math.sinh, Math.expm1, Math.acosh, Math.pow and their relatives are described as "implementation-approximated": engines must be close to the correctly rounded result but are not required to be exactly it. That single sentence in the standard is what makes math a fingerprinting surface.

Engines satisfy the requirement by delegating to whatever high-performance math routines are available. In practice that means the platform math library - glibc on most Linux distributions, Apple's libm on macOS and iOS, the Microsoft C runtime on Windows - or a bundled implementation such as the fdlibm port that some engines ship for a subset of functions. Each rounds the final unit in the last place differently for some inputs. The differences are invisible in ordinary use and perfectly stable for a given platform, which is exactly the combination that makes a good identifier.

A probe picks a set of inputs where the implementations are known to diverge, evaluates them, and serialises the results at full precision. The resulting string is effectively an operating system signature, obtained without any permission prompt and without touching a single identity API. It is a member of the broader engine and OS oracle family: a value nobody set on purpose, derived from how the binary was built.

What makes it awkward to keep consistent

Math rounding has an unusual profile compared with other fingerprinting surfaces. It is low entropy on its own - it distinguishes platform families, not individual machines, so it will never identify a specific user. But it is high confidence, because within a platform the values are exact and deterministic, with no noise to average away and no legitimate variation between users on the same OS.

That makes it a coherence anchor rather than an identifier. Its job is not to say who you are but to contradict a claim about what you are running. A session presenting a Windows User-Agent whose math signature matches glibc has produced a contradiction between a declared value and a derived one, and unlike a rare canvas hash there is no innocent explanation for it.

Consistency is awkward precisely because the value is derived. Overriding Math.tanh in JavaScript replaces a native function with a JavaScript one, which is immediately visible to native function integrity checks - and the override has to reproduce another platform's rounding across every input a probe might choose, not just the handful in a public test. Nor does it reach Workers, where the original implementation is still present. Producing a coherent math signature means running on the operating system being claimed, which is a provisioning decision rather than a scripting one.

The straightforward answer is to let the claim follow the machine: run real browsers on real operating systems and describe them accurately. Managed web data infrastructure does this by default, so derived values like math rounding line up with the declared identity without anyone having to think about it.

Code example

javascript
// Math fingerprinting: a platform signature from implementation-approximated
// functions, plus the coherence check that gives it its real value.

const PROBES = [
  () => Math.tanh(1),          () => Math.sinh(1),
  () => Math.expm1(1),         () => Math.acosh(1e300),
  () => Math.atanh(0.5),       () => Math.cbrt(100),
  () => Math.log1p(10),        () => Math.pow(Math.PI, -100)
];

// Full-precision serialisation: base-36 preserves the differing final bits
const mathSignature = PROBES.map(f => f().toString(36)).join('|');

// Low entropy on its own - it identifies a platform family, not a machine.
// Its value is as a coherence anchor against the declared platform:
const claimedWindows = /Windows NT/.test(navigator.userAgent);
const claimedMac     = /Mac OS X/.test(navigator.userAgent);

// A reference table is built by sampling real devices per OS, then compared:
// if the signature matches a known glibc profile while the UA claims Windows,
// a derived value has contradicted a declared one.
console.log({ mathSignature, claimedWindows, claimedMac });

// Note: overriding Math.tanh replaces a native with a JavaScript function,
// which native-function integrity checks read directly - and Workers keep
// the original implementation regardless.

Related terms

Concept map

Concept map

How Math & JS Engine Fingerprinting connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Anti-Bot
Building map…

Frequently asked questions

Is math fingerprinting high-entropy on its own?

No — on its own it mostly identifies the engine + OS family, not a specific device (low entropy: it narrows down what kind of machine you are, not which one). Its real value is consistency and serving as a lie-detector cross-check, not standalone uniqueness.

Can I randomise the math results?

Patching the Math functions in JavaScript is detectable - the patched function fails the native-code toString check that reveals it is no longer the browser's built-in version - and randomising breaks the stable-per-machine behavior detectors expect. It does more harm than good.

Does WebAssembly have the same issue?

WebAssembly (WASM - a low-level binary format browsers run for speed) specifies math more strictly, but its SIMD timing and rounding still leak engine details, which is why anti-bots increasingly pair Math probes with WASM ones.

How much entropy does a math signature actually carry?

Very little on its own - it typically separates operating system families and sometimes major libm versions, so it might distinguish a handful of groups rather than individual users. Its value is not identification but corroboration: the values are exact and deterministic within a platform, so a mismatch against a declared platform is a hard contradiction rather than a probabilistic hint.

Last updated: 2026-07-30 · Facts last verified: 2026-07-30