Compare · WhiskerJS 0.1.0

WhiskerJS and BotD

BotD is a bot classifier that returns a verdict. WhiskerJS is a collector whose integrity flags report what it saw and leave the verdict to your server. Compared from a code reading of BotD 2.0.0.

In plain words

BotD is a small MIT-licensed library from the makers of FingerprintJS that answers one question in the browser: is this a bot, and if so which tool? WhiskerJS does not answer that question. It runs a set of similar checks, reports each one that fired as a flag next to its device signals, and leaves the decision to your server, where you can weigh the flags with what you already know (account history, rate, network).

At a glance

WhiskerJS 0.1.0 BotD 2.0.0
Licence Apache-2.0 MIT
Version read main at signalsVersion 5, idVersion 2 tag v2.0.0, commit dc6a8f9
Last release not yet published 2025-11-04
Status in development maintained; README: “the near-term roadmap is stability-only”
Kind collector: signals, ids and integrity flags, no verdict bot classifier
Output 46 components, visitorId, structuredId, integrityFlags { bot: boolean, botKind }
Identifier yes none (a verdict)
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/botd

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

BotD’s column is mostly b or - because it collects only what it needs for its checks; it builds no device identifier.

What each one checks

Check WhiskerJS flag BotD
navigator.webdriver true automation_webdriver yes (webDriver source)
Globals and document properties left by automation tools automation_globals (32 names, $cdc_, $wdc_, cdc_ prefixes, webdriver, selenium, driver attributes on <html>) yes (distinctiveProperties, documentElementKeys)
Headless names in the User-Agent headless_user_agent (HeadlessChrome, PhantomJS, SlimerJS) yes (HeadlessChrome, PhantomJS, Electron, SlimerJS and others)
Outer window size 0 headless_markers (outerWidth and outerHeight both 0, or desktop Chrome without window.chrome) yes (window size while the document has focus)
Engine consistency engine_mismatch (eval source length and productSub against the browser class) yes (evalLength, productSub)
Notification permission quirk permissions_inconsistent yes (notificationPermissions)
Plugin and mimeType types plugins_inconsistent yes (pluginsArray, mimeTypesConsistence)
Software renderer software_renderer (SwiftShader, llvmpipe and others) yes (webGL: vendor “Brian Paul” with renderer “Mesa OffScreen”)
Electron, Rhino, Sequentum and similar no yes (process, spawn, windowExternal)
navigator.connection.rtt zero no (network information is not read) yes (rtt)
Native function tampering, navigator own properties, writable webdriver, impossible hardware native_tampering, navigator_own_property, webdriver_writable, impossible_hardware no
Clean iframe cross-checks iframe_mismatch, prototype_lie no

The flag names and conditions are in Integrity checks.

How they differ in approach

Verdict or evidence. BotD combines its detectors into a single answer and a botKind (for example selenium, headless_chrome, phantomjs). WhiskerJS never combines its checks. Its flags are a sorted list of the checks that fired; the docs list the known false positives of each (for example privacy extensions set native_tampering, users without GPU acceleration set software_renderer). Your server decides what a combination means for your site.

Device context. BotD tells you about automation only. WhiskerJS returns its flags in the same result as 46 device signals and two identifiers, so your server can also ask “have I seen this setup before?” and “is the ua_client_hints platform consistent with the request headers?”.

Client trust. Both run in a browser you do not control. WhiskerJS’s docs put it plainly: a flag’s presence is informative, its absence proves nothing. Its optional anti-replay binding stops an old result from being sent again; BotD has no such option in the version read.

Network and console. BotD’s monitoring request is on by default and sampled. WhiskerJS has no network code and writes nothing to the console, which is why it leaves out Chrome DevTools Protocol detection (that technique needs a console call).

Their claims, in their words

  • BotD publishes no accuracy figure in the version read.
  • README.md (2.0.0), Maintenance Status: “the near-term roadmap is stability-only, so new features are unlikely to land soon”; for “more demanding applications” it points to the vendor’s commercial bot detection.

WhiskerJS publishes no detection rate either: its flags are not measured against any bot population yet.

Code credits

WhiskerJS contains no BotD code. Its THIRD_PARTY_NOTICES.md credits BotD 2.0.0 for ideas only: automation globals and headless markers, the eval length and productSub engine checks, the notification permission quirk, and the plugin and mimeType type checks.

When you might pick either

  • You want a ready verdict in the browser with no server logic: BotD gives one.
  • You want the raw checks with their false positives documented, next to device signals, and to make the decision on your server: that is WhiskerJS’s design.
  • Some teams run a classifier and a collector together; the outputs do not conflict.
  • Either way, treat client-side detection as one input among others, and measure on your own traffic.

No head-to-head measurement has run. This page compares checks and designs. It does not say that either library detects more bots or fewer false positives. A side-by-side measurement is planned and will be published on the comparison page.

Questions and answers

Can WhiskerJS replace BotD?

They answer different questions. BotD returns bot true or false and a bot kind. WhiskerJS returns 14 integrity flags with device signals and leaves the decision to your server. Whether the flags cover your needs depends on your traffic; nothing has been measured side by side.

Does WhiskerJS use BotD code?

No. WhiskerJS credits BotD 2.0.0 for ideas only: automation globals and headless markers, the eval length and productSub engine checks, the notification permission quirk and the plugin and mimeType type checks.

Does BotD send data by default?

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