WhiskerJS is a small script you run in your web page. You call collect(), wait a moment, and get back one JSON object describing the browser and device: 46 measurements, two identifiers and a list of warning flags. You then send that object to your own server with a request you already make. The library is not on npm yet, so you build it from source first.
This page takes you from the source code to a first result in a page. It assumes you know how to run a JavaScript build and serve a web page.
1. Build the library
WhiskerJS is not on npm yet, and its source repository opens with the first
public release. With the source in a folder named whiskerjs:
cd whiskerjs
pnpm install # dev dependencies only: TypeScript and Node types
pnpm build # writes dist/: ES2020 modules plus .d.ts type files
pnpm test # optional: runs the test suite with node:test
Requirements: Node 18 or newer (tested on Node 22) and pnpm 10
(corepack enable gives you the pinned version).
2. Add it to your project
Pick one of these:
- As a package. In the WhiskerJS folder run
pnpm pack. It writeswhiskerjs-0.1.0.tgzwithdist/,LICENSE,NOTICEandTHIRD_PARTY_NOTICES.md. In your project:npm install ../whiskerjs/whiskerjs-0.1.0.tgz(or the pnpm or yarn equivalent). Your bundler then resolvesimport { load } from 'whiskerjs'. - As plain files. Copy
dist/*.jsanddist/collectors/*.js(keep the folder layout) to your static files and loadindex.jsas a module:<script type="module">import { load } from '/vendor/whiskerjs/index.js';</script>. This is how the live demo on whiskerjs.com runs it.
There are no runtime dependencies and no CDN.
3. Collect once
import { load } from 'whiskerjs';
const agent = await load(); // cheap: no work happens yet
const result = await agent.collect(); // at most 5 s by default (1 s per component)
console.log(result.visitorId); // '3feb3ead31e6b520459ad33ebc58f2d7'
console.log(result.integrityFlags); // [] or e.g. ['automation_webdriver']
collect() never throws and never rejects, whatever the browser does.
Collect in the browser, after the page has a <body>: several probes add
hidden elements to the body for a moment, and without one they report
unavailable.
The page must be a secure context (https, or http://localhost while
developing). Hashing uses crypto.subtle, which browsers only offer there.
Without it visitorId and structuredId are null and every large
component reports unavailable with ruleId no_digest.
4. Read the result
for (const [name, c] of Object.entries(result.components)) {
if (c.status === 'ok') console.log(name, c.value, c.hashed ? '(digest)' : '');
else console.log(name, c.status, c.ruleId);
}
visitorId: one hash over the 37 stable components. Equal ids mean the same browser setup, as far as these signals can tell.structuredId: seven short hashes, one per signal group, so you can see which part of a device changed. See Identifiers.components: all 46, always present, each with a status. See Signals and How it works.integrityFlags: names of checks that saw signs of automation or tampering. A hint for your server, not a verdict. See Integrity checks.signalsVersionandidVersion: store them with every id; ids are only comparable within the same versions.
5. Send it to your own server
WhiskerJS has no network code. Add the result to a request you already make, for example the login form post:
const result = await agent.collect({
timeoutMs: 3000,
binding: await getNonceFromMyServer(), // { nonce, timestamp }, optional
});
await fetch('/api/login', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ email, password, device: result }),
});
The result is plain JSON, about 4 KB. Read Integration for what to check on the server, and Anti-replay binding for the nonce.
6. Tell your users
Device signals can be personal data. Before you deploy, read Privacy and add a notice to your privacy policy.
Try it without building
The live demo at whiskerjs.com/demo runs the real library in your browser and shows every component, both ids and the flags.