# 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.

Source: https://whiskerjs.com/compare/botd/ (last updated 2026-10-26)

> **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 9, 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 | 47 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 9) | BotD 2.0.0 |
| --- | --- | --- |
| Canvas 2D image | Y (double render, known-answer probe) | - |
| WebGL vendor and renderer (unmasked) | Y | b |
| WebGL parameters and extensions | Y | - |
| WebGL rendered image | Y (double render, known-answer probe) | - |
| 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, Playwright binding, headless; no CDP) | b (18 kinds) |
| Lie or tamper detection | b (native source, descriptors, clean iframe, Proxy, cross-API consistency) | 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 (previous ids after a bump) | 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 |

WhiskerJS signalsVersion 8 added five more flags: `media_query_mismatch` (CSS device size against `screen`), `timezone_inconsistent` (Intl against `Date`), `ua_ch_mismatch` (user agent against UA Client Hints), `exposed_binding` (Playwright’s marker on an exposed function) and `vm_renderer` (a virtual machine or emulator graphics adapter). They are not in the table, because BotD 2.0.0 was not read again for them.

The flag names and conditions are in [Integrity checks](https://whiskerjs.com/docs/integrity/#the-flags).

## 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 47 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](https://whiskerjs.com/docs/integration/#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](https://whiskerjs.com/compare/).

## Questions and answers

### Can WhiskerJS replace BotD?

They answer different questions. BotD returns bot true or false and a bot kind. WhiskerJS returns 19 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.

## Related pages

- [Integrity checks](https://whiskerjs.com/docs/integrity/): All 19 WhiskerJS flags and their false positives.
- [Bot and headless detection](https://whiskerjs.com/use-cases/bot-detection/): Using the flags and signals on your server.
- [WhiskerJS and fpscanner](https://whiskerjs.com/compare/fpscanner/): Another library with automation checks.
- [WhiskerJS and FingerprintJS](https://whiskerjs.com/compare/fingerprintjs/): The same vendor's device identifier.
- [Headless browser](https://whiskerjs.com/glossary/#headless-browser): What headless means and how it shows.
