# Privacy

> What is and is not collected, and what the site using it still has to do.

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

> **In plain words:**
>
> WhiskerJS reads device characteristics, turns the large ones into hashes in the browser, and gives the result to your page. It makes no network requests, stores nothing, shows no permission prompts and writes nothing to the console. It does not decide anything about the visitor. Because a device fingerprint can still be personal data, the site that uses WhiskerJS must explain it in its privacy notice; a template is below.

WhiskerJS is built to collect as little as it can while still telling devices apart, and to keep every decision about the data with the site that uses it. This page says exactly what it does and does not do, and what that site still has to do.

## What it collects

The 46 components in [Signals](https://whiskerjs.com/docs/signals/): screen and display settings, languages and time zone, installed fonts (as a digest), how graphics and audio render (as digests), CPU, memory and browser build details, which permissions are already granted or refused, and whether a content blocker hides test elements. Large values (images, audio samples, font lists, voices, codecs, plugins, WebGL parameters, WebGPU details, permission states) leave only as a digest: the first 128 bits of SHA-256.

## What it never does

| Never | How this is enforced |
| --- | --- |
| Make a network request (fetch, XHR, `sendBeacon`, WebSocket, EventSource, an element with a network source) | no network code exists; the iframe has no `src`; tests scan the compiled source and record every call on a fake browser |
| Send telemetry or usage data | there is no sender of any kind |
| Store anything (localStorage, sessionStorage, IndexedDB, cookies) | `storage` only checks that the APIs exist; tests record every storage and cookie access |
| Show a permission prompt | permission states are read only with `navigator.permissions.query`; no requesting API is called |
| Read WebRTC addresses, the battery, geolocation or media device labels | not collected; see the roadmap in the library source (`docs/ROADMAP.md`) |
| Write to the console | no console calls; `audio_latency` and WebGL skip APIs that would make the browser warn |
| Play sound or open an audio device | audio is rendered offline; `AudioContext` is created only in Safari and on Android, and closed at once |
| Send a raw large value | it is hashed, or reported `unavailable` (`no_digest`) without Web Crypto |
| Score, profile or decide | it is a collector; there is no scoring code |

For a moment during `collect()` the page holds some hidden elements: an off-screen container of measuring spans (fonts, font preferences), eight 1x1 bait elements (content blockers) and one hidden `about:blank` iframe (integrity checks). All are removed before `collect()` resolves.

## Data minimisation

- Signals that change too easily (zoom, taskbar, permissions, keyboard layout, voices, versions) are kept out of the ids, so ids do not follow users’ everyday choices.
- Signals a privacy browser randomises on purpose (Brave farbling, Firefox resistFingerprinting) are not collected at all instead of being worked around.
- `collect({ exclude: [...] })` lets you drop any component you do not need. Dropping fewer components makes ids more distinguishing; dropping more makes them less so. Decide for your purpose.
- Nothing persists between calls. If you want to remember a device, that is your decision and your storage.

## What the site still has to do

A fingerprint can identify a device, and in many places that makes it personal data, whether or not the site also knows the user’s name. WhiskerJS does not decide the lawful basis, the retention or the notice. The site that uses it does, and should take legal advice for its own case. In particular, rules on accessing information on a user’s device (for example the ePrivacy rules in the EU and UK) can require consent unless an exemption applies, and data protection law (for example the GDPR or POPIA) requires transparency, a purpose and limited retention.

For a longer, informational look at the consent and notice questions (not legal advice), see [Privacy and compliance](https://whiskerjs.com/privacy/): [GDPR and ePrivacy](https://whiskerjs.com/privacy/gdpr-eprivacy/), [POPIA](https://whiskerjs.com/privacy/popia/), [CCPA and CPRA](https://whiskerjs.com/privacy/ccpa-cpra/), [fingerprinting without cookies](https://whiskerjs.com/privacy/fingerprinting-without-cookies/) and [what a privacy notice should say](https://whiskerjs.com/privacy/privacy-notice/).

### Disclosure text you can adapt

> **Device information for security.** When you sign in [or: register, pay], our site runs a script in your browser that reads technical characteristics of your device and browser, such as screen size, language and time zone settings, installed fonts, and how your device draws a test image and processes a test sound. Most of these are turned into short codes (hashes) in your browser. We use them to recognise devices you have used before and to detect automated or fraudulent access. The script does not ask for any permission, does not read your location, contacts, files, camera or microphone, and does not store anything on your device. The results are sent to us only with the form you submit, are kept for [period], and are not sold or used for advertising. [Legal basis.] [How to object or contact us.]

Adjust the moments, purpose, retention and legal basis to what you really do.

## The live demo

The demo at [whiskerjs.com/demo](https://whiskerjs.com/demo/) runs the same library. The demo page itself (not the library) keeps your run history in your browser’s local storage so it can show whether your id holds. For visitors it sends nothing; its Content Security Policy allows connections only to OpenStake’s own lab server, which only lab mode with a lab key (OpenStake’s own test devices) uses. “Clear my history” removes everything it stored.
