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.
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.
Is it on npm?
Not yet. Build it from source; see 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.
Is the id the same in a private window?
Often, but not always: some browsers add noise to canvas or WebGL in private
windows, which turns those components noisy and changes the id. Brave and
Firefox resistFingerprinting are handled by the exclusion table instead.
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.
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.
Can I compare ids from different versions?
No. Store idVersion and signalsVersion with every id and compare only
within the same pair. See What’s 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 in the library source (docs/ROADMAP.md).