# FAQ

> Short answers to the questions people ask first.

Source: https://whiskerjs.com/docs/faq/ (last updated 2026-10-26)

## Does WhiskerJS tell me whether a visitor is a bot?

No. It reports signals and integrity flags. Deciding what they mean for your site is your server’s job. See [Integrity checks](https://whiskerjs.com/docs/integrity/).

## Does it identify people?

It describes a browser setup. Two identical devices can share a visitorId; one person with two browsers has two. Still treat ids as personal data: see [Privacy](https://whiskerjs.com/docs/privacy/).

## Is it on npm?

Not yet. Build it from source; see [Getting started](https://whiskerjs.com/docs/getting-started/).

## Does it send anything anywhere?

No. It has no network code and no telemetry. You send the result yourself, with your own request.

## Why is `visitorId` null?

`crypto.subtle` is not available, usually because the page is not a secure context. Serve it over https (or `http://localhost` in development).

## Why did my visitorId change?

A stable component changed value or went from `ok` to not `ok` (or back). Compare the two structuredIds to see which group moved, then the components in that group. Common causes: a browser or driver update, fonts, language or time zone, a private window that adds canvas noise. A new `signalsVersion` or `idVersion` also gives new ids. See [Identifiers](https://whiskerjs.com/docs/identifiers/#what-changes-the-id).

## Is the id the same in a private window?

Often, but not always: some browsers add noise to canvas, WebGL or audio in private windows, which turns those components `noisy` and changes the id (once: the noisy state is the same in every session). Brave, Firefox Fingerprinting Protection and resistFingerprinting, Tor and Mullvad Browser and Safari Advanced Fingerprinting Protection are handled by the exclusion table instead. See [Noise handling](https://whiskerjs.com/docs/how-it-works/#noise-handling-the-double-render-and-the-known-answers).

## Why is a component `noisy`?

It was rendered twice and the results differed, so the browser or an extension adds random noise. The value is left out on purpose.

## Why is `device_memory` unavailable in Safari or on my iPhone?

Only Chromium-based browsers offer `navigator.deviceMemory`. Elsewhere it is `unavailable` by design. The same goes for `heap_limit`, `ua_client_hints`, `ua_versions` and `keyboard_layout`.

## Why is `audio_latency` “skipped”?

Outside Safari and Android, creating an `AudioContext` without a user tap makes the browser print a console warning, and WhiskerJS writes nothing to the console. Nothing is wrong with the browser.

## I see `iframe_mismatch` in my automated tests. Is that a bug?

It has been seen under WebKit automation, where the automation layer makes the page and a new frame disagree. Under automation, also expect `automation_webdriver`. See [Integrity checks](https://whiskerjs.com/docs/integrity/#iframe_mismatch).

## How long does `collect()` take?

It is bounded: each component has 1 second and the whole call `timeoutMs` (5 seconds by default). `result.durationMs` tells you the real time on each device; measure it on the devices you care about.

## Does it slow the page down?

It runs on the main thread. Several probes (font measurement, canvas and WebGL drawing) are synchronous for a moment. Collect when you need it, for example on a form submit, rather than on every page load.

## Can I run it on the server or in Node?

It runs without errors (the tests do), but there is no browser to read, so most components are `unavailable`. Collect in the browser; on the server, use `bindingInput` and your own checks.

## Can I leave out a signal?

Yes: `collect({ exclude: ['keyboard_layout', 'permissions'] })`. Excluded stable components count as “missing” in the ids, so a different exclude list gives different ids.

## Can someone fake a result?

Yes. Everything comes from the client. The checks catch common tools and the binding stops replay, but a determined attacker can hand-craft a result. Use it as one piece of evidence. See [Integration](https://whiskerjs.com/docs/integration/#what-never-to-trust-from-the-client).

## Can I compare ids from different versions?

Not directly. Store `idVersion` and `signalsVersion` with every id and compare only within the same pair. For one release after a bump the result also carries `previousVisitorId`, the id the version before would have given, so your server can link old and new ids. See [Identifiers](https://whiskerjs.com/docs/identifiers/#previous-ids-linking-across-a-version-bump) and [What’s new](https://whiskerjs.com/whats-new/).

## Why are some known techniques missing (WebRTC, battery, CDP detection)?

They need a network-capable API, a prompt, or console output, which the library’s rules forbid. See the [roadmap](https://whiskerjs.com/roadmap/#not-doing).
