npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

motion-attestation

v1.0.0

Published

Human interaction verification through behavioral biometrics. Analyzes mouse movement, click patterns, keystroke dynamics, scroll behavior, touch pressure, and device sensors to distinguish humans from bots.

Readme

motion-attestation

Human interaction verification through behavioral biometrics. Analyzes mouse movement, click patterns, keystroke dynamics, scroll behavior, touch pressure, and device sensors to distinguish humans from bots. Returns integrity score and anomaly flags. Zero dependencies.

Sophisticated bots with undetected-chromedriver can pass signal checks. They cannot replicate the involuntary biomechanical patterns of a human hand.

Install

npm install motion-attestation

Usage

Collect Signals (Browser)

import { createCollector } from 'motion-attestation';

const collector = createCollector();
collector.attach();

// Track clicks on specific elements
collector.bind(document.getElementById('submit'), 'submit-btn');
collector.bind(document.getElementById('agree'), 'agree-checkbox');

// Wait for enough data (3-15 seconds of interaction)
const interval = setInterval(() => {
    if (collector.isReady()) {
        clearInterval(interval);
        const data = collector.getData();
        collector.detach();
        // Send data to server for analysis
    }
}, 500);

Analyze Interactions (Server)

import { analyze, classifyScore } from 'motion-attestation';

const { score, penalty, reasons, categories } = analyze(data);
// score: 0.0-1.0 (1.0 = human, 0.0 = bot)
// penalty: total deductions
// reasons: ["[mouse] Low curvature entropy: 0.82 (straight-line)"]
// categories: per-category { penalty, maxPenalty, reasons }

const verdict = classifyScore(score);
// "human" | "suspicious" | "bot"

Challenge-Response Protocol

// Server
import { createServer } from 'motion-attestation';

const attestation = createServer({
    secretKey: process.env.MOTION_SECRET, // optional
    scoreThreshold: 0.5,
});

// Mount on existing HTTP server
import { createServer as createHttpServer } from 'node:http';
const httpServer = createHttpServer(attestation.handler());
httpServer.listen(3000);

// Verify tokens from downstream services
const payload = attestation.validateToken(token);
// Client (browser)
import { createCollector } from 'motion-attestation';

// 1. Init challenge
const { challengeId } = await fetch('/interactions/init', {
    method: 'POST',
}).then((r) => r.json());

// 2. Collect interactions
const collector = createCollector();
collector.attach();

// 3. Submit when ready
const data = collector.getData();
collector.detach();
const response = await fetch('/interactions/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ cid: challengeId, d: data, ts: Date.now() }),
});
const { cleared, score, token, flags } = await response.json();

Token Signing

import { signToken, verifyToken, generateKey } from 'motion-attestation';

const key = generateKey();
const token = signToken({ score: 0.95, iat: Date.now() }, key);
const payload = verifyToken(token, key); // null if invalid/expired

Protocol

Client                                    Server
  |                                          |
  |--- POST /interactions/init ------------->|  Create challenge
  |<-- { challengeId, ttl } -----------------|
  |                                          |
  |    +----------------------+              |
  |    | Collect for 3-15s:   |              |
  |    | * Mouse movement     |              |
  |    | * Click positions    |              |
  |    | * Keystroke timing   |              |
  |    | * Scroll patterns    |              |
  |    | * Touch + pressure   |              |
  |    | * Gyro/Accel sensors |              |
  |    | * Event ordering     |              |
  |    +----------------------+              |
  |                                          |
  |--- POST /interactions/verify ----------->|  Analyze biometrics
  |    { cid, d, ts }                        |
  |<-- { cleared, score, token, flags } -----|
  |                                          |
  |--- GET /protected ---------------------->|  Bearer token validation
  |    Authorization: Bearer <token>         |
  |<-- { message, score } -------------------|

Signals Collected

| Category | Data | Desktop | Mobile | | ------------------ | ------------------------------------------- | :-----: | :----: | | Mouse position | Sub-pixel x,y with timestamps | * | | | Click landing | Offset from target center + dwell time | * | | | Keystroke timing | Hold duration + inter-key gaps | * | * | | Scroll behavior | Position, delta, timestamps | * | * | | Touch events | Position, pressure, contact radius | | * | | Accelerometer | 3-axis acceleration readings | | * | | Gyroscope | 3-axis rotation rate | | * | | Device orientation | Alpha, beta, gamma angles | | * | | Event ordering | Timestamped sequence of all event types | * | * | | Bound element hits | Click offset from center per bound element | * | * | | Engagement | Time-to-first-interaction, session duration | * | * |

Analysis Algorithms

Mouse Movement — weight: 0.30

The core signal. Human motor control follows biomechanical constraints that are extremely hard to fake.

| Check | Human | Bot | Penalty | | ----------------------- | -------------------------------- | ---------------------------------- | --------- | | Curvature entropy | Variable curvature, high entropy | Near-zero curvature, low entropy | 0.05-0.12 | | Micro-tremor | 0.3-8px wobble (8-12 Hz tremor) | <0.05px smooth or >20px noise | 0.06-0.10 | | Velocity variance | CV >0.4 (Fitts' Law profile) | CV <0.15 (constant speed) | 0.05-0.12 | | Jerk analysis | High variance (corrections) | Low variance (smooth function) | 0.06 | | Straightness index | 1.02-1.5 (natural curves) | ~1.000 (ruler-straight) | 0.04-0.10 | | Direction entropy | >2.5 (normal distribution) | <1.2 (discrete angles) | 0.08 | | Timing regularity | Variable intervals | >70% identical intervals | 0.08-0.10 | | Teleportation | Never | >300px in <10ms | 0.08-0.15 | | Origin clustering | Never | Points at (0,0) | 0.08 | | Bezier detection | Irregular acceleration | >85% constant acceleration | 0.10 | | Timestamp format | Integer milliseconds | Non-integer (synthetic generation) | 0.10 | | Sub-pixel precision | 0-2 decimal places | >6 decimal places (Math.random) | 0.08-0.15 | | Autocorrelation | Aperiodic movement | Periodic patterns (sinusoidal) | 0.10 | | Movement continuity | Pauses >150ms (thinking) | No pauses (continuous automation) | 0.06 | | Velocity minima | Corrective sub-movements | No mid-trajectory corrections | 0.06 |

Click Landing — weight: 0.15

Humans almost never click the exact geometric center of an element.

| Check | Human | Bot | Penalty | | -------------------- | -------------------- | ---------------------------- | --------- | | Center offset | >5% offset, variable | >70% within 5% of center | 0.06-0.12 | | Offset variance | std >0.02 | std <0.02 (identical) | 0.08 | | Click dwell time | 60-250ms, variable | <10ms or perfectly uniform | 0.06-0.08 | | Zero-duration | Never | Dispatched without mousedown | 0.08 |

Pre-click Deceleration — weight: 0.10

Fitts' Law: humans decelerate as they approach a click target.

| Check | Human | Bot | | ----------------------------------- | --------------------------- | ---------------------------------------------- | | Velocity in last 500ms before click | Decreasing (approach phase) | Constant or increasing (no targeting behavior) |

Keystroke Dynamics — weight: 0.15

Every human has a unique typing rhythm that is impossible to perfectly replicate.

| Check | Human | Bot | Penalty | | --------------------- | ----------------------- | ----------------------------- | --------- | | Dwell time | 50-200ms, variable | <5ms or identical across keys | 0.08-0.10 | | Flight time | 30-500ms, variable | <15ms (impossible) or uniform | 0.08-0.10 | | Rhythm entropy | >2.0 (natural variance) | <1.5 (mechanical precision) | 0.06 | | Uniform detection | CV >0.15 | CV <0.08 (robotic uniformity) | 0.08 |

Scroll Behavior — weight: 0.10

| Check | Human | Bot | | ----------------------- | ------------------------ | ---------------------------- | | Velocity variance | Variable bursts + pauses | Constant velocity (CV <0.15) | | Direction reversals | Frequent (re-reading) | None (one-directional) | | Pauses | >300ms gaps (reading) | Continuous scrolling | | Delta uniformity | Variable scroll amounts | Fixed increments (CV <0.05) |

Touch Biometrics — weight: 0.10

Real fingers produce a pressure distribution and blob-shaped contact area.

| Check | Human | Bot | | ---------------------- | ---------------------------------- | ---------------------------------- | | Pressure variation | CV >0.01 (changing during gesture) | CV ~0 (constant or absent) | | Contact area | radiusX/Y vary as finger rolls | Constant or zero | | Trajectory wobble | Sub-pixel deviations | RMS <0.3px (geometrically perfect) | | End deceleration | Natural slowdown at swipe end | Constant velocity or abrupt stop |

Sensor Data — weight: 0.10

A human holding a device introduces involuntary micro-movement.

| Check | Human | Bot | | -------------------------- | ------------------------------------------- | ------------------------ | | Accelerometer noise | stddev 0.01-0.5 per axis (micro-vibrations) | Zero or perfectly static | | Gyroscope tremor | Detectable 8-12 Hz oscillation | Zero rotation rate | | Orientation drift | Slow natural drift over time | Perfectly fixed angles | | Cross-axis correlation | Correlated noise between axes | Independent or zero |

Event Ordering — weight: 0.05

Browser events fire in specific sequences. Violations indicate synthetic dispatch.

Expected:  mousemove -> mousedown -> mouseup -> click
           keydown -> keyup
           touchstart -> touchmove -> touchend

| Anomaly | Penalty | | ----------------------------------------- | --------- | | Click without preceding mousedown/mouseup | 0.02 each | | mousedown/mouseup at identical timestamp | 0.03 | | touchmove without preceding touchstart | 0.02 | | keyup without preceding keydown | 0.02 |

Synthetic Event Detection — weight: 0.15

Cross-signal analysis that catches automation frameworks even when individual signals look plausible.

| Check | Human | Bot | Penalty | | ------------------------------ | ------------------------ | ----------------------------------- | ------- | | Cross-signal fast dispatch | Normal dwell times | Both clicks AND keys <5ms | 0.10 | | Single-channel fast | Normal dwell times | Clicks OR keys <5ms | 0.04 | | Zero-time click pairs | mousedown/mouseup gap >0 | Identical timestamps (CDP dispatch) | 0.05 |

Engagement — weight: 0.05

| Check | Human | Bot | | ----------------------------- | ------------------------------- | -------------------- | | Time to first interaction | >200ms (visual processing) | <50ms (pre-scripted) | | Event density | Reasonable for session duration | >50 events in <500ms |

Scoring

Final Score = 1.0 - sum(category penalties)

Each category has a maximum penalty cap:
  Mouse:       0.30    Click:        0.15
  Pre-click:   0.10    Keystrokes:   0.15
  Scroll:      0.10    Touch:        0.10
  Sensors:     0.10    Event order:  0.05
  Synthetic:   0.15    Engagement:   0.05
                       -----------------
  Maximum total:       1.15 (capped at 1.00)

Score >= 0.5 -> CLEARED (human)     Token issued
Score <  0.5 -> BLOCKED (bot)       No token

The threshold is configurable via scoreThreshold option.

Configuration

| Option | Default | Description | | ---------------- | --------------- | --------------------------------- | | secretKey | Random 32 bytes | Token signing secret | | scoreThreshold | 0.5 | Minimum score to clear (0.0-1.0) | | debug | false | Include full analysis in response | | challengeTtl | 60000 | Challenge expiry in milliseconds |

Why This Works

| Attack | Defense | | ----------------------------- | ------------------------------------------------------------------------ | | Selenium/Puppeteer moveTo() | Produces straight lines with uniform velocity and zero curvature entropy | | Bezier curve mouse libraries | Detectable by constant second derivative (too-smooth acceleration) | | Recorded human replay | HMAC nonce prevents replay; timestamps won't match | | Synthetic dispatchEvent() | Missing mousedown/mouseup sequence; zero click dwell | | Headless touch simulation | Zero pressure, zero contact radius, no wobble | | Emulated sensors | Zero noise floor, no cross-axis correlation | | Fast sendKeys() | Flight times <15ms (physically impossible), zero dwell variance | | WindMouse / ghost cursor | Autocorrelation, missing velocity minima, no movement pauses | | Perlin/Catmull-Rom paths | Sub-pixel precision anomaly, periodic autocorrelation | | CDP mouse dispatch | Zero-time mousedown/mouseup pairs, cross-signal fast dispatch |

The fundamental insight: human motor control is governed by biomechanical constraints (Fitts' Law, physiological tremor, speed-accuracy tradeoff) that produce characteristic statistical signatures in movement data. These signatures are involuntary and extremely difficult to replicate at the distribution level, even when individual data points can be faked.

Element Binding

Track click accuracy on specific interactive elements:

const collector = createCollector();
collector.attach();

collector.bind(submitBtn, 'submit');
collector.bind(checkbox, 'agree');

// getData() includes:
// bc: [[offsetX, offsetY, dwell, width, height, time, index], ...]
// bl: ['submit', 'agree']

collector.unbind(submitBtn);

Bound element clicks are analyzed for center offset precision — bots click exact center, humans don't.

Example

npm run example
# http://localhost:3002

Test

npm test           # unit tests
npm run test:e2e   # playwright e2e (10 bot algorithms)
npm run test:all   # both

E2E Bot Algorithms

The e2e suite runs 10 humanization algorithms through real browsers and verifies all are detected:

| Algorithm | Technique | | --------------- | -------------------------------------------- | | Linear | Straight line with random jitter | | Bezier | Cubic bezier curve interpolation | | Sinusoidal | Sine wave path with variable amplitude | | WindMouse | Ghost Mouse algorithm (wind + gravity model) | | Overshoot | Target overshoot with correction | | Perlin | Perlin-like noise displacement | | Spring-Damper | Physics spring simulation | | Gaussian Jitter | Gaussian noise on linear path | | Catmull-Rom | Spline through random control points | | Bell Velocity | Bell-curve speed profile |

All 10 score below 0.5 (blocked) in both unit tests and real browser e2e tests.

Formatting

npx prtfm

License

MIT