# Behavioral bot detection test: what it scores

> A behavioral bot detection test scores mouse paths and dwell next to browser attributes. Load one and touch nothing, and the score describes your stillness.

Source: https://mosaicbrowser.com/blog/behavioral-bot-detection-test

## Behaviour tests score your mouse, not just your browser

A behavioral bot detection test scores mouse paths and dwell next to browser attributes. Load one and touch nothing, and the score describes your stillness.

![15s: Fifteen seconds of input, then the score, beside a screenshot: the Mosaic warm-up robot about to give a new profile a browsing history](https://mosaicbrowser.com/covers/behavioral-bot-detection-test.webp)

A behavioral bot detection test scores what you do on the page as well as what your browser is. Incolumitas BotOrNot, the one in our catalogue that does this, watches mouse movement, scrolling and dwell, and prints a score from 0 (bot) to 1 (human) beside thirty-odd checks on the browser's attributes. Load it and touch nothing, and the score is a measurement of your stillness.

We learned that by reporting the wrong number for as long as the row existed.

## The row that said "behaviour 0" every time

Our detection suite opens each public detector in a real profile and reads its verdict by machine. For Incolumitas it reported `behaviour 0` on every run. That looks like a finding about the profile. It had two causes, and neither was the fingerprint.

The first was that our harness never moved anything. It opened the page, waited, and read. A classifier that rates mouse paths had no mouse path to rate, so zero was the correct answer about the visitor we sent.

The second was worse. The extractor matched `/behavioral[^\d]*([\d.]+)/` against the page's visible text, and the first thing that pattern meets is the page's own help sentence, the one explaining that the score "gives a rating between 0 (Bot) and 1 (Human)". It captured that 0, every run. The real score lives in an element of its own, `#behavioralScore`, and the browser checks render as JSON in `#new-tests` and `#detection-tests`. Three rendered values were being ignored in favour of a digit in a sentence. Read the element, never the prose.

## How to run a behavioural test so it measures something

The fix for the first cause was to behave, using the same code the profile uses everywhere else. Mosaic's warm-up robot and its page-driving tools send input through one module, `src/main/engine/human-input.ts`, and the detection suite imports it rather than keeping a second mouse that would drift from the first. For Incolumitas it now does this, in order:

1. Move the cursor to a random spot in the middle 60% of the viewport, then rest 0.6 seconds.
2. Scroll down 420 pixels, move again, rest 0.9 seconds.
3. Scroll back up 260, move, rest 1.2 seconds.
4. Scroll down 700, move, rest 2.5 seconds.

That is about fifteen seconds. Then it polls `#behavioralScore` every three seconds for up to twenty-four, because the page shows `...` until its classifier has something to say, and a fixed six-second wait had reported "no number" about a page that was still thinking.

If you are testing by hand, the same applies: use the mouse the way you would on any page, scroll, wait for the score to change from its placeholder, and read the number the page puts in its result box.

Mosaic is a desktop app

Email yourself the link and open it on your computer.

[How it works: REST API](https://mosaicbrowser.com/features/rest-api)

## What "trusted" input is, and why it is not enough

A page can tell a scripted click from a hand. `element.click()` dispatches one `click` event with `isTrusted` set to false and nothing around it: no `pointerover`, no `mousemove`, no `pointerdown` or `mouseup`, no focus change. `window.scrollBy` fires `scroll` with no `wheel` in front of it, which a wheel cannot produce. Any script that counts mousemoves before a click separates that from a person on the first sample.

So Mosaic sends input at the browser level, through the DevTools `Input.dispatchMouseEvent` and `Input.dispatchKeyEvent` commands, and the page receives the whole sequence with `isTrusted` true. We checked that neither command flips the CDP-automation check on deviceandbrowserinfo, which `Runtime.enable` does flip ([why we never call it](https://mosaicbrowser.com/blog/runtime-enable-detection)).

Trusted is still not human. A trusted cursor travelling in a straight line at constant speed is a robot with good paperwork. The motion follows the models that describe real pointing:

- Fitts' law sets how long a move takes, so far or small targets take longer, on a logarithmic curve.
- The minimum-jerk profile, `10τ³ − 15τ⁴ + 6τ⁵`, sets its shape: slow at both ends, quick in the middle.
- Moves longer than 220 pixels overshoot about 28% of the time and land with a second, small correction.
- Every sample carries sub-pixel tremor.

Each profile also gets a pointing device and a tempo from its seed: a trackpad for 64% of profiles and a notched wheel for the rest, at a speed between 0.82 and 1.24 times the mean. A session that scrolls like a trackpad on one page and a wheel on the next is describing two computers.

## The score needs a window someone could be looking at

The suite runs its browsers off-screen, at `--window-position=-32000,-32000`, so a long run does not cover the desk. Headed, not headless, because a headless Chrome names itself in its User-Agent and loses the real GPU, and we will not grade a browser we do not ship.

Off-screen, Incolumitas's score never left `...`. We measured fifteen seconds of real minimum-jerk movement, scrolling and dwell, and then forty-five seconds of polling, and got no number. Nothing ever focuses an off-screen window, and their classifier appears to want focus. The attribute checks rendered every time.

So the row is decided by the attribute checks, and the score is reported when it exists and named as unavailable when it does not. Run the suite with `--watch` and the windows are visible, and the score arrives. The longer list of what unattended runs need is in [running 49 fingerprint tests unattended](https://mosaicbrowser.com/blog/automating-fingerprint-tests).

## Read the two halves separately

On our last full run, all of Incolumitas's attribute tests but one returned OK. The one that did not, `WEBDRIVER`, expects `navigator.webdriver` to be absent, which stopped being true when Chrome made it a standard property that is `false` in an ordinary browser. Our engine and the machine's own Google Chrome failed it byte for byte the same, so it says nothing about the profile, and the row names it as stale instead of dropping it.

That is the general lesson for any behavioural test. The attribute half tells you whether the browser is consistent. The behaviour half tells you whether what drove it looked like a person during those seconds, and it is only meaningful if something drove it. A low behaviour score over a page you never touched is not a finding. Pixelscan has a similar card, and how far we could reproduce its verdict is in [Pixelscan's automated behavior card](https://mosaicbrowser.com/blog/pixelscan-automated-behavior-card).

## Questions

### What is a behavioral bot detection test?

A page that watches how you use it, such as mouse paths, scrolling, dwell time and key timing, and scores how human that looks. Most also run attribute checks on the browser itself, and the two results mean different things.

### Why does a behaviour test give me a score of 0?

Usually because nothing moved. A classifier that rates mouse movement has nothing to rate on a page nobody touched. Check too that you are reading the score element and not a number in the page's own explanation; ours did exactly that.

### Does a behaviour score work in a background or off-screen window?

On Incolumitas BotOrNot it did not for us. Off-screen, the score stayed at an ellipsis after fifteen seconds of input and forty-five more of waiting, while the attribute checks rendered normally. Run it in a visible, focused window.

### Is element.click() enough to look human?

No. It fires one click event with isTrusted false and no pointer or mouse events around it, which a page that counts mousemoves before a click separates from a person on the first sample.

## Related

[Running 49 fingerprint tests unattended, and what that takes](https://mosaicbrowser.com/blog/automating-fingerprint-tests): To automate browser fingerprint testing, tell a failed profile from a dead site, an unreadable page and your own bug. Our runner names each across 49 rows.

[Runtime.enable flips a detector, so we don't call it](https://mosaicbrowser.com/blog/runtime-enable-detection): Runtime.enable detection, measured: one DevTools command turned a clean Mosaic profile into isBot=true on deviceandbrowserinfo. We removed all five calls.

[Pixelscan's automated behavior card, reproduced and not](https://mosaicbrowser.com/blog/pixelscan-automated-behavior-card): Pixelscan's automated behavior detected card never came back on our own platform beside stock Chrome. The foreign platform claim failed four times in four.

[49 browser fingerprint detectors, and how we read them](https://mosaicbrowser.com/blog/forty-nine-detectors): Our browser fingerprint detectors list: 49 public test pages in five groups, 40 read by a script and 9 kept as reference because they print no verdict.

[What is an anti-detect browser?](https://mosaicbrowser.com/guides/what-is-an-anti-detect-browser): An anti-detect browser gives each profile its own fingerprint, proxy and cookies, so sites see separate visitors. What it changes, how, and its limits.



[A retired bot test can still answer 200 and look like a pass](https://mosaicbrowser.com/blog/dead-detector-returns-200): Antoine Vastel's fp-scanner demo now serves his homepage with HTTP 200, which a challenge reader scores as a pass. His tests moved; here is where.

[setUserAgentOverride without metadata wipes your client hints](https://mosaicbrowser.com/blog/setuseragentoverride-clears-hints): Without userAgentMetadata, setUserAgentOverride left our page with empty brands while a worker reported both. We set the User-Agent at launch instead.

## Mosaic, on your own computer

The browser this blog is about: its own Chromium, fingerprints from real machines and a proxy exit per profile. Free holds 3 profiles. Pro is $49 a month for any number.

Mosaic is a desktop app

Email yourself the link and open it on your computer.
