# WhiskerJS facts

> Precise statements you can quote, each with the page that explains it. They describe WhiskerJS 0.1.0 at signalsVersion 9 and idVersion 2.

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

> **In plain words:**
>
> WhiskerJS is a free, open-source script for web pages that reads technical characteristics of a browser and device and hands them, as one JSON object, to the website’s own server. It does not send anything anywhere itself, does not store anything, and does not decide whether a visitor is a person or a bot. It is still in development and not yet on npm.

## Identity

| Fact | Value |
| --- | --- |
| Name | WhiskerJS |
| What it is | A browser collector: it reads device and browser signals in the page and returns one plain JSON result |
| What it is not | A scoring engine, a bot verdict or a tracking service |
| Licence | Apache License 2.0 |
| Library version | 0.1.0 |
| Signal set | signalsVersion 9 |
| Identifier derivation | idVersion 2 |
| Language and format | TypeScript, compiled to ES2020 modules with `.d.ts` types |
| Runtime dependencies | None |
| Release status | In development. Not yet published to npm or any other registry: build it from source; the npm package is coming |
| Website | [whiskerjs.com](https://whiskerjs.com/) |

## What one call returns

- **47 components** (signals). Every component is always present with one of six statuses: `ok`, `unavailable`, `blocked`, `noisy`, `excluded`, `timed_out`; a non-ok component carries a short `ruleId` saying why. See [Signals](https://whiskerjs.com/docs/signals/) and [How it works](https://whiskerjs.com/docs/how-it-works/#statuses-and-reason-codes).
- **37 stable components** make up the `visitorId`, a 128-bit hash written as 32 lowercase hex characters. The other **10** are reported but kept out of both identifiers because they change too easily or describe browser settings (zoom, taskbar, permissions, keyboard layout, voices, versions, audio latency, canvas readback, noise profile, content blockers). See [Identifiers](https://whiskerjs.com/docs/identifiers/).
- **7 groups** (`gpu`, `audio`, `fonts`, `device`, `browser`, `locale`, `prefs`) form the `structuredId`: the idVersion followed by one 48-bit part (12 hex characters) per group, so two results can be compared group by group. See [Identifiers](https://whiskerjs.com/docs/identifiers/#structuredid).
- **19 integrity flags**: signs of automation or tampering, such as `automation_webdriver` or `prototype_lie`. They are hints for the server, never a verdict, and each has documented false positives. See [Integrity checks](https://whiskerjs.com/docs/integrity/).
- An optional **anti-replay binding**: the server’s nonce and timestamp, echoed with a digest over the whole result. It stops replay of an old result; it is not a signature. See [Anti-replay binding](https://whiskerjs.com/docs/integration/#anti-replay-binding).
- For one release after a version bump, **previous ids** (`previousVisitorId`, `previousStructuredId`, with `previousSignalsVersion` and `previousIdVersion`): the ids the version before would have given the same browser, so a server can link old and new ids. See [Identifiers](https://whiskerjs.com/docs/identifiers/#previous-ids-linking-across-a-version-bump).
- `durationMs`, the time the collection took, and the three version strings `version`, `signalsVersion` and `idVersion`.

## Limits and timing

| Limit | Value |
| --- | --- |
| Inner timeout per component | 1000 ms |
| Overall budget (`timeoutMs`) | default 5000 ms, clamped to 0 to 60000 ms |
| Value length | at most 128 printable ASCII characters; longer or non-ASCII values are hashed |
| Digest | first 128 bits of SHA-256, as 32 lowercase hex characters |
| Errors | `collect()` never throws and never rejects |
| Secure context | needed for Web Crypto; without it both ids are `null` |

## Privacy stance

What the library itself never does, as enforced by its test suite (a recording fake browser and a scan of the compiled source):

- **No network**: no fetch, XHR, `sendBeacon`, WebSocket or EventSource, no element with a network source, no telemetry.
- **No storage**: no cookies, localStorage, sessionStorage or IndexedDB; each `collect()` starts from nothing.
- **No permission prompts**: permission states are read only with `navigator.permissions.query`, which never prompts. No WebRTC addresses, battery, geolocation or media device labels.
- **No console output.**
- **No raw large values**: images, audio samples, font lists, voices, codecs, plugins, WebGL parameters, WebGPU details and permission states leave the page only as a digest.
- **Privacy browsers are not worked around**: under Brave, Firefox Fingerprinting Protection and resistFingerprinting, Tor and Mullvad Browser, and Safari Advanced Fingerprinting Protection the randomised or normalised components are not collected and report `excluded`. Noise a browser adds is reported as `noisy`, never as an integrity flag.

Device signals can still be personal data: the site that uses WhiskerJS decides the purpose, the legal basis, the retention and the notice. See [Privacy](https://whiskerjs.com/docs/privacy/) and [Privacy and compliance](https://whiskerjs.com/privacy/) (informational, not legal advice).

## Measurement status

- **No accuracy, stability or uniqueness figures are published.** The head-to-head measurement against other libraries has not run yet, so WhiskerJS makes no claim to be more accurate, more stable or more unique than any other library.
- **Early lab observation**, an early small sample, not a benchmark: at signalsVersion 1, four device and browser pairs (Mac Chrome, 4 runs; Mac Safari, 2; iPhone Chrome, 3; iPhone Safari, 3) each kept one identifier across their runs, and the four identifiers differed. `device_memory` was `unavailable` on Safari and iOS, by design.

## Credits

Two small parts are adapted from MIT-licensed projects with their notices kept: the Math operations from FingerprintJS 5.3.0 and the media MIME lists from fpscanner 1.0.8. Other techniques are credited as ideas, not code. See the [comparison](https://whiskerjs.com/compare/).

## How to cite

“WhiskerJS 0.1.0 (signalsVersion 9, idVersion 2), an open-source (Apache-2.0) browser collector of 47 device signals, [https://whiskerjs.com/](https://whiskerjs.com/)”. When a fact changes, this page and the [changelog](https://whiskerjs.com/whats-new/) change with it; compare ids only within the same signalsVersion and idVersion.
