Compare · WhiskerJS 0.1.0

WhiskerJS and ThumbmarkJS

Two open-source collectors with a similar signal surface and very different defaults on telemetry and noise, compared from a code reading of ThumbmarkJS 1.12.0.

In plain words

ThumbmarkJS is an MIT-licensed browser fingerprint library with a client part and an optional paid API. Its client reads a wide set of signals, including permissions, speech voices and WebRTC session details, and returns a hash. WhiskerJS reads a similar surface, keeps the easily changing parts out of its ids, and has no network code at all. The biggest practical difference is the defaults: ThumbmarkJS samples telemetry by default, WhiskerJS never sends anything.

At a glance

WhiskerJS 0.1.0 ThumbmarkJS 1.12.0
Licence Apache-2.0 MIT
Version read main at signalsVersion 5, idVersion 2 tag v1.12.0, commit 7eeda65
Last release not yet published 2026-09-25
Status in development maintained
Kind collector: signals, ids and flags, no verdict device identifier (client library plus optional paid API)
Identifier 32-hex visitorId plus a 7-part structuredId hash of the components
Bot and tamper checks 14 integrity flags none client-side
Default network behaviour none sampled logging on about 1 in 10000 runs, with a remote script fetched and evaluated on that run
Off switch not needed logging: false, no api_key, collect_beacon: false
Install build from source; npm package coming npm package @thumbmarkjs/thumbmarkjs

How this was made: ThumbmarkJS’s public repository was read at v1.12.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) ThumbmarkJS 1.12.0
Canvas 2D image Y (double render, tamper probe) Y (3 renders, common pixel)
WebGL vendor and renderer (unmasked) Y Y
WebGL parameters and extensions Y -
WebGL rendered image Y (double render) Y
WebGPU adapter info Y -
Audio (OfflineAudioContext) Y Y
Audio base latency o (Safari and Android only) -
Fonts (how probed) Y (54, span width and canvas measureText) Y (89, canvas measureText, iframe)
Font preferences (default font sizes) Y -
Screen size and colour depth Y Y
Screen frame or available area o -
Time zone Y Y
Intl locale and calendars 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) -
Hardware concurrency Y Y
Device memory Y Y
JS heap limit Y Y
CPU architecture (NaN trick or CH) Y (NaN) -
Touch support Y Y
Plugins and mimeTypes Y (+ consistency check) Y
Math precision Y Y
CSS media features Y (5) Y (11)
Storage availability Y -
Cookies enabled Y Y
Media codecs Y -
Speech voices o Y
Permissions states o (24) Y (28)
Media device counts - -
WebRTC - Y (SDP, no IP)
Network information - -
Battery - -
Keyboard layout o -
Content blockers or extensions o (8 bait elements) -
Vendor-specific globals Y Y (Apple Pay)
Apple Pay, Private Click Measurement Y Y (Apple Pay)
PDF viewer 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) -
iframe and Worker cross-check b (iframe; no Worker) Y (iframe for fonts)
Anti-replay (nonce, time) Y -
Identifier format 32-hex visitorId plus 7-part structuredId hash

In short: both read canvas, a WebGL image, WebGL identity, audio, fonts by canvas measureText, Math, the JS heap limit, screen, languages, time zone, permissions and speech voices. ThumbmarkJS also reads WebRTC session details (with no ICE servers, so no address) and more media queries; WhiskerJS does not touch WebRTC at all. WhiskerJS also reads WebGL parameters, WebGPU, Client Hints, Intl locale details, media codecs, storage availability and font preferences, and runs integrity checks.

How they differ in approach

Canvas and WebGL noise. ThumbmarkJS renders its canvas three times (its WebGL image once, or three times in Samsung Browser) and keeps the most frequent byte per pixel, which reconstructs a usable image even when a browser adds per-read noise. WhiskerJS takes the opposite position: it renders twice, and if the two differ the component is noisy and carries no value; for Brave and Firefox with resistFingerprinting it does not collect the randomised components at all. That keeps a noised value out of the id instead of working around a privacy feature. See Noise handling.

Stability. ThumbmarkJS has an option stabilize (default ['private', 'iframe']) that drops components known to change in private windows and iframes. WhiskerJS has no such presets; instead nine components that change easily (pixel ratio, screen frame, versions, permissions, keyboard layout, voices, audio latency, canvas readback, content blockers) are always reported but never part of the ids, and you can drop any component with collect({ exclude }).

Permissions. Both read permission states only through navigator.permissions.query, which never prompts: ThumbmarkJS over 28 names inside its hash, WhiskerJS over 24 names outside its ids (states change when users grant or revoke).

Network. ThumbmarkJS’s sampled logging posts all components and the page path to its logging API, and on that run fetches and evaluates experimental.min.js from its own host; with an API key it also calls its API and may send a beacon. WhiskerJS has no network code, no telemetry and an optional onResult hook that you wire to your own endpoint.

Identifier and storage. ThumbmarkJS keeps the API’s visitor id in localStorage when the paid API is used. WhiskerJS stores nothing and returns a versioned visitorId and a part-by-part structuredId.

Integrity and anti-replay. WhiskerJS adds 14 integrity flags and an optional nonce binding. ThumbmarkJS’s client does not include automation or lie detection in the version read.

Their claims, in their words

  • README.md (1.12.0): “The client library achieves ~80% uniqueness across general browser populations. Mac/Safari users can show higher collision rates or higher noise depending on the audience.”
  • README.md (1.12.0): the paid API claims “over 99% uniqueness” with server-side signals.
  • Option stabilize: “Preset exclusion list for stability across private browsing and iframes”.
  • README.md, sampled logging: the samples show “which components turn out to be unstable across real browsers.”

WhiskerJS publishes no accuracy, stability or uniqueness figure yet.

Code credits

WhiskerJS contains no ThumbmarkJS code. Its THIRD_PARTY_NOTICES.md credits ThumbmarkJS 1.12.0 for ideas only: the component layout, the offline audio graph, speech voices as a signal, permission states, the JS heap limit and font detection by canvas measureText. Its API and telemetry calls were not used.

When you might pick either

  • You want a registry package today and may later add a paid server-side API: ThumbmarkJS offers that path.
  • You want a value even in browsers that add per-read canvas noise: ThumbmarkJS’s common-pixel reduction is designed for that.
  • You must guarantee that no request and no remote code leaves or enters the page through the library: WhiskerJS has none; with ThumbmarkJS set logging: false and leave out the API options.
  • You need statuses per component, a part-by-part id, integrity flags or a nonce binding: those are WhiskerJS features.
  • Either way, measure on your own traffic.

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 ThumbmarkJS send data by default?

In 1.12.0, sampled logging is on by default: on about 1 in 10000 runs it posts the components and the page path to its logging API and, on the same run, fetches and evaluates a remote script. logging: false turns it off. API and beacon calls happen only with an API key or after an API response.

ThumbmarkJS says about 80 percent uniqueness. What does WhiskerJS say?

Nothing yet. The 80 percent figure is ThumbmarkJS's own claim for its client library across general browser populations. WhiskerJS publishes no uniqueness figure until its measurement has run.

How do the two handle canvas noise?

ThumbmarkJS renders the canvas several times and keeps the most common byte per pixel. WhiskerJS renders twice and, if the two differ, reports the component as noisy and leaves it out instead of reconstructing a value.