Compare · WhiskerJS 0.1.0

WhiskerJS and FingerprintJS

Two MIT and Apache-licensed browser collectors with overlapping signal sets and different designs, compared feature by feature from a code reading of FingerprintJS 5.3.0.

In plain words

FingerprintJS is an established open-source browser fingerprint library. Version 5 is MIT-licensed and reads a broad set of signals, then returns one visitor id with a confidence score. WhiskerJS reads an overlapping set, returns every measurement with a status, two identifiers and integrity flags, and makes no network request. This page compares what each one does. It does not say which identifies devices better: that has not been measured.

At a glance

WhiskerJS 0.1.0 FingerprintJS 5.3.0
Licence Apache-2.0 MIT (from 5.0.0; 4.x was BUSL-1.1)
Version read main at signalsVersion 5, idVersion 2 tag v5.3.0, commit dac5ae5
Last release not yet published 2026-10-07
Status in development maintained
Kind collector: signals, ids and flags, no verdict device identifier with a confidence score
Identifier 32-hex visitorId plus a 7-part structuredId 32-hex visitor id
Bot and tamper checks 14 integrity flags none (bot detection is the separate BotD)
Default network behaviour none sampled monitoring GET, about 1 in 1000 loads
Off switch not needed load({ monitoring: false })
Install build from source; npm package coming npm package @fingerprintjs/fingerprintjs

How this was made: FingerprintJS’s public repository was read at v5.3.0 on 2026-10-24 (code read, never installed or run). Its own documentation is the reference if anything here differs.

Signals

Y collected for the identifier · b used only as a bot, lie or consistency check · o opt-in, or collected but outside the hash · - not collected.

Signal WhiskerJS 0.1.0 (signalsVersion 5) FingerprintJS 5.3.0
Canvas 2D image Y (double render, tamper probe) Y (noise skip, double render)
WebGL vendor and renderer (unmasked) Y Y
WebGL parameters and extensions Y Y
WebGL rendered image Y (double render) -
WebGPU adapter info Y -
Audio (OfflineAudioContext) Y Y
Audio base latency o (Safari and Android only) Y
Fonts (how probed) Y (54, span width and canvas measureText) Y (52, span width, iframe)
Font preferences (default font sizes) Y Y
Screen size and colour depth Y Y
Screen frame or available area o Y
Time zone Y Y
Intl locale and calendars Y Y
Languages Y Y
User-Agent and platform Y (platform; the UA string only for the class and checks) Y
UA Client Hints (high entropy) Y (versions outside the id) Y
Hardware concurrency Y Y
Device memory Y Y
JS heap limit Y -
CPU architecture (NaN trick or CH) Y (NaN) Y
Touch support Y Y
Plugins and mimeTypes Y (+ consistency check) Y
Math precision Y Y
CSS media features Y (5) Y (8)
Storage availability Y Y
Cookies enabled Y Y
Media codecs Y -
Speech voices o -
Permissions states o (24) -
Media device counts - -
WebRTC - -
Network information - -
Battery - -
Keyboard layout o -
Content blockers or extensions o (8 bait elements) Y (filter lists)
Vendor-specific globals Y Y
Apple Pay, Private Click Measurement Y Y
PDF viewer Y Y
Automation markers (webdriver, Selenium, Playwright, CDP, PhantomJS) b (webdriver, 32 globals, headless; no CDP) -
Lie or tamper detection b (native source, descriptors, clean iframe, Proxy) b (canvas noise)
iframe and Worker cross-check b (iframe; no Worker) Y (iframe for fonts only)
Anti-replay (nonce, time) Y -
Identifier format 32-hex visitorId plus 7-part structuredId 32-hex hash

In short: the two sets overlap on most classic signals (canvas, WebGL identity and parameters, audio, fonts, font preferences, screen, Intl, Client Hints, Math, media features, Apple Pay and Private Click Measurement, vendor flavours, PDF viewer). WhiskerJS also reads a WebGL rendered image, WebGPU adapter details, media codecs, the JS heap limit, speech voices, permission states and the keyboard layout, the last three outside its ids. FingerprintJS reads a few CSS media features and navigator values that WhiskerJS does not (for example inverted colours, monochrome, reduced transparency, osCpu).

How they differ in approach

Noise. FingerprintJS skips sources where it knows browsers add noise (its code comments name canvas in Safari 17 and later and Firefox 120 and later) and renders canvas twice to drop noisy results. WhiskerJS has no browser-version skip list. It renders canvas and WebGL twice on every browser and reports noisy with ruleId render_mismatch when they differ, and for Brave and Firefox with resistFingerprinting it uses an exclusion table that does not collect the randomised components at all. Its audio probe is rendered once. See the double render.

What a missing value looks like. FingerprintJS distinguishes per-source error states. WhiskerJS gives every one of its 46 components exactly one of six statuses with a short reason code (no_api, security_error, timeout and so on), so your server can tell “this browser has no such API” from “it was blocked” from “it ran out of time”. See statuses and reason codes.

Identifier. FingerprintJS returns one hash and a confidence score; in the open-source code the score is a fixed value by platform (0.3 to 0.7), and its code comment says the true probability depends on the number of visitors, which the agent cannot know. WhiskerJS returns no score. It returns a visitorId over 37 stable components and a structuredId with one short hash per signal group, so two results can be compared part by part. Both ids carry versions (signalsVersion, idVersion). See Identifiers.

Bot and tamper detection. FingerprintJS 5 leaves automation markers to BotD. WhiskerJS includes 14 read-only integrity checks (automation globals, navigator.webdriver, headless markers, native function tampering, a clean iframe cross-check and others) and reports them as flags, never as a verdict. See Integrity checks.

Network and console. FingerprintJS’s monitoring request is on by default and sampled. WhiskerJS has no network code at all and writes nothing to the console (which is also why it reads audio_latency only in Safari and on Android, the same choice FingerprintJS makes for its audio base latency source).

Anti-replay. WhiskerJS can bind a server nonce and timestamp into the result with a digest (Anti-replay binding). FingerprintJS 5.3.0 has no such option in the code read.

Their claims, in their words

  • README.md (5.3.0): “the accuracy is significantly lower than in the commercial version” (no figure).
  • README.md (5.3.0): “a fingerprint stays the same in incognito/private mode and even when browser data is purged.”
  • README.md (5.3.0), Limitations: “Because fingerprints are generated and processed in the browser, they are vulnerable to spoofing and reverse engineering.”
  • docs/version_policy.md: “The library tries to keep visitor identifiers the same within a minor version … Some visitor identifiers may change within a minor version due to stability fixes.”
  • For the older lines: the 3.4.2 README says “limited accuracy (40% - 60%)” and “Accuracy: up to 60%”; the 4.6.2 README says “the accuracy is limited (40% - 60%)”.

WhiskerJS publishes no accuracy, stability or uniqueness figure yet.

Code credits

WhiskerJS adapts one part from FingerprintJS 5.3.0 under its MIT licence: the list of Math operations, their input constants and the polyfill expressions used by the math component, with the notice kept in the source file, NOTICE and THIRD_PARTY_NOTICES.md. Several techniques were taken as ideas only, with no code copied: the double canvas render, screen orientation normalisation, media feature probes, vendor flavour globals, Client Hints with GREASE filtering and the Permissions Policy marker, the NaN sign bit for CPU architecture, screen frame, Intl locale, Apple Pay and Private Click Measurement, font preferences, audio base latency and content-blocker baits. Nothing from the 4.x (BUSL) line was used.

When you might pick either

  • You want an established package from npm today, with a known upgrade path to a commercial service: FingerprintJS fits that.
  • You need a library you can install from a registry now: WhiskerJS is not published yet.
  • You need per-component statuses, a part-by-part id, integrity flags or a nonce binding in the same result: those are WhiskerJS features.
  • You must guarantee no request leaves the page from the library: WhiskerJS has none; with FingerprintJS turn monitoring off.
  • Either way, measure on your own traffic before relying on the ids.

No head-to-head measurement has run. This page compares features and designs. It does not say that either library is more accurate, more stable or more unique. A side-by-side measurement on the same devices is planned and will be published on the comparison page.

Questions and answers

Does WhiskerJS contain FingerprintJS code?

One small part: the list of Math operations, their input constants and polyfill expressions in the math collector are adapted from FingerprintJS 5.3.0 under its MIT licence, with the notice kept. Everything else is original code; some techniques were taken as ideas only.

Which one is more accurate?

Unknown. No head-to-head measurement has run. FingerprintJS publishes no accuracy figure for 5.x beyond saying it is significantly lower than its commercial version, and WhiskerJS publishes none yet.

Does FingerprintJS send data anywhere?

In 5.3.0, on about 1 in 1000 loads it sends a GET request to the vendor's monitoring host for installation statistics. load({ monitoring: false }) turns it off. WhiskerJS has no network code.

Can I use FingerprintJS 4.x instead?

The 4.x line (4.0.0 to 4.6.2) is under the Business Source License 1.1, which its README describes as for non-production purposes. 5.x returned to MIT.