WindMouse simulates cursor motion with a gravity-and-wind force model that produces spatially plausible paths but falls apart the moment a classifier inspects temporal dynamics. The wind force injects constant random perturbation, creating 15 or more velocity peaks per movement where a real human arm generates 1 to 3. Movement duration bears no relationship to distance or target size. Sample intervals follow a uniform distribution instead of the gamma distributed spacing real hardware produces. SigmaDrift discards the force metaphor entirely and builds trajectories from six components rooted in computational motor control: sigma-lognormal velocity primitives, a two phase surge architecture, Ornstein-Uhlenbeck (OU) lateral drift, Signal-Dependent Noise (SDN), speed-modulated physiological tremor, and gamma distributed timing. The output is statistically consistent with genuine human input under the same feature extractors that modern behavioral classifiers rely on.

This article examines why the WindMouse force model structurally cannot survive temporal analysis, introduces each motor control foundation that SigmaDrift draws on, traces how those foundations converge into a single generative pipeline, presents head-to-head metrics against recorded human data, and addresses the practical boundaries of the approach.

The WindMouse force model

WindMouse treats the cursor as a particle acted on by two forces. Gravity pulls it toward the destination. Wind pushes it in random directions. Each iteration accumulates velocity from both sources, clamps to a maximum step, and advances the cursor. The loop terminates when distance to target drops below one pixel.

for (int iter = 0; iter < 5000; ++iter) {
    double dist = std::hypot(x1 - xs, y1 - ys);
    if (dist < 1.0) break;

    double w = std::min(wind_str, dist);
    if (dist >= target_area) {
        wx = wx / std::sqrt(3.0) + randf(-w, w) / std::sqrt(5.0);
        wy = wy / std::sqrt(3.0) + randf(-w, w) / std::sqrt(5.0);
    } else {
        wx /= std::sqrt(3.0);
        wy /= std::sqrt(3.0);
    }

    vx += wx + gravity * (x1 - xs) / dist;
    vy += wy + gravity * (y1 - ys) / dist;
    // clamp velocity, step forward, repeat
}

Gravity acts as a constant-magnitude force directed toward the target, normalizing the direction vector by remaining distance. Wind operates as a decaying random walk that quiets as the cursor enters the target zone. Close to the destination, gravity dominates and the cursor homes in. The spatial result looks organic. Problems surface only when examining how the cursor moves through time rather than where it lands.

Temporal failures inherent to gravity-and-wind dynamics

The deficiencies below are structural. They persist across every parameter configuration because they arise from the model itself, not from poor tuning.

Velocity profile shape. A human reach follows an asymmetric bell-shaped speed curve: rapid acceleration to peak velocity around 30 to 40 percent of movement time, then a longer deceleration tail. Flash and Hogan established the smooth bell-shaped profile in their 1985 minimum jerk model, though their formulation predicts a symmetric peak at 50 percent. The observed asymmetry, peak velocity arriving before the midpoint, is better captured by Plamondon’s sigma-lognormal framework. WindMouse produces nothing resembling a bell. The wind force injects random perturbation every tick, yielding a velocity trace that looks like a seismograph, with 10 to 20 or more distinct peaks across a single movement.

Sub-movement count. Each wind perturbation creates a small acceleration-deceleration cycle. Count the velocity peaks above 15 percent of maximum and WindMouse typically registers around 15 sub-movements. A real human pointing motion consists of one primary ballistic stroke followed by one or two small corrections. The gap is an order of magnitude.

No Fitts’ Law compliance. Human movement time scales logarithmically with the ratio of distance to target width. This relationship, known as Fitts’ Law, is among the most replicated results in experimental psychology. A 600 pixel movement to a 20 pixel target takes measurably longer than the same distance to a 50 pixel target. WindMouse iterates until the cursor is within one pixel of the target, and how many iterations that requires depends on stochastic wind values. Task geometry has no influence on duration.

Uniform sample timing. WindMouse spaces samples by drawing from randf(5.0, 15.0) milliseconds. Real Inter-Sample Intervals (ISI) from physical mouse hardware follow a gamma distribution: values cluster around a mean with a characteristic right-skewed tail. A basic distribution test distinguishes uniform spacing from gamma distributed spacing trivially.

No biomechanical basis. Gravity and wind are metaphors without grounding in motor control science. There is no tremor model, no noise that scales with motor command magnitude, no direction dependent curvature from wrist geometry. The algorithm contains no representation of how a human arm actually produces movement.

A classifier that examines angular velocity distributions, velocity zero-crossings, acceleration reversals, or sub-movement decomposition flags WindMouse output on sight. The trajectory may pass a screenshot test, but its temporal signature is unmistakably synthetic.

Sigma-lognormal velocity primitives

Plamondon’s Kinematic Theory models human pointing and handwriting as the superposition of lognormal velocity pulses. Each pulse corresponds to an agonist-antagonist muscle synergy firing. A single pulse yields an asymmetric bell-shaped speed profile: a sharp rise followed by a slower tail. This lognormal kernel captures the pre-midpoint peak that Flash and Hogan’s symmetric minimum jerk model misses.

Mathematically, path progress at time t reduces to the Cumulative Distribution Function (CDF) of the lognormal distribution, which evaluates through erf. No numerical integration is necessary.

The mu parameter governs where peak velocity falls. Deriving mu from the lognormal mode equation mode = exp(mu - sigma^2) and targeting the peak at roughly 35 percent of movement time reproduces the characteristic human asymmetry. Corrective sub-movements use the same primitive with different onset times, durations, and magnitudes, superimposed additively onto the running position.

Two phase surge architecture

Costello’s Surge Model (1968), as later applied to mouse pointing by Muller et al. (2017), splits human pointing into a ballistic phase and a corrective phase. The primary ballistic stroke covers 92 to 97 percent of total distance on most movements. Roughly 15 percent of trials overshoot to between 102 and 108 percent of the distance. The remainder of the gap is closed by zero to two corrective sub-movements, each parameterized independently. This two phase structure explains why real pointing data shows 1 to 3 velocity peaks, not the 15 that WindMouse generates.

Ornstein-Uhlenbeck lateral drift

An OU process models mean-reverting hand drift during movement. Unlike white noise, which accumulates without bound, the OU process includes a decay term that pulls displacement back toward zero. This matches the empirical observation that hand deviation is bounded rather than an unbounded random walk. The decay rate theta and diffusion coefficient sigma together govern how far the cursor wanders laterally and how quickly it returns.

Signal dependent noise

Harris and Wolpert demonstrated in 1998 that motor noise magnitude scales proportionally with the motor command driving it. Faster movements demand larger neural signals, and those larger signals carry proportionally greater noise. SDN naturally degrades endpoint accuracy as a function of movement speed and produces scatter that scales with task difficulty. A slow, deliberate movement to a small target exhibits less jitter than a fast ballistic sweep across the screen.

Speed-modulated physiological tremor

Physiological tremor occupies the 8 to 12 Hz band. During fast ballistic movement, proprioceptive feedback dampens tremor gain. During the slow corrective phase and at rest, tremor expresses at full amplitude. SigmaDrift models this suppression explicitly: tremor amplitude scales inversely with instantaneous speed. This speed dependent gain modulation is absent from every other movement generation algorithm in common use.

Gamma distributed sample timing

Physical mouse hardware does not emit samples at perfectly regular intervals. ISI measurements from real devices follow a gamma distribution, clustering around a mean with a right-skewed tail. SigmaDrift draws each sample interval from a gamma distribution parameterized to match standard 125 Hz polling behavior, replacing the uniform randf(5.0, 15.0) millisecond spacing that WindMouse uses.

How the components converge

SigmaDrift fuses the six foundations above into a three stage generation pipeline. The components do not act in isolation. They interact: tremor suppression during the ballistic phase shifts noise characteristics with movement phase, SDN ties endpoint scatter to movement speed, and OU drift introduces organic lateral wandering that fixed-frequency jitter cannot replicate.

Planning. Given start coordinates, target coordinates, and target width, SigmaDrift computes a movement plan. Movement time derives from Fitts’ Law in its Shannon formulation, with an 8 percent lognormal Coefficient of Variation (CV) for trial-to-trial variability:

MT = (a + b * log2(D/W + 1)) * exp(N(0, 0.08))

This compliance is prescribed, not emergent. In a fully principled model, movement time would arise naturally from SDN dynamics: larger movements demand larger motor commands, which produce more noise, which forces longer correction time. Harris and Wolpert showed this mechanism in 1998. SigmaDrift includes SDN, but the loop is not closed. SDN influences endpoint scatter and trajectory jitter, but movement duration itself comes directly from the Fitts equation. The trajectory is then shaped to fill that duration. The 8 percent CV introduces realistic inter-trial variability, though the underlying timing remains prescribed rather than derived from the motor dynamics.

During planning, the algorithm also resolves the primary stroke reach fraction and overshoot probability:

bool overshoot = uniform(0.0, 1.0) < cfg.overshoot_prob;
double reach = overshoot
    ? uniform(cfg.overshoot_min, cfg.overshoot_max)
    : uniform(cfg.undershoot_min, cfg.undershoot_max);

double primary_D = distance * reach;

Velocity profile generation. Each sub-movement follows a sigma-lognormal velocity primitive. Path progress evaluates analytically from the lognormal CDF. There is no numerical integration and no Euler stepping for the primary motion curve.

// progress along path at time t
double s = lognormal_cdf(t, 0.0, primary_mu, primary_sigma);

// position = start + direction * distance * progress
double bx = x0 + tx * primary_D * s;
double by = y0 + ty * primary_D * s;

Corrective sub-movements superimpose additively onto the running position, each with its own onset, duration, and magnitude:

for (const auto& c : corrections) {
    double cs = lognormal_cdf(t, c.t0, c.mu, c.sigma);
    bx += c.dir_x * c.D * cs;
    by += c.dir_y * c.D * cs;
}

Noise composition. Three noise sources apply at every timestep. OU lateral drift advances via an Euler-Maruyama step:

// euler-maruyama step for OU process
ou_x += -cfg.ou_theta * ou_x * dt_s + cfg.ou_sigma * sqrt(dt_s) * N(0,1);
ou_y += -cfg.ou_theta * ou_y * dt_s + cfg.ou_sigma * sqrt(dt_s) * N(0,1);

Physiological tremor gain decreases with speed, modeling proprioceptive suppression:

// tremor gain drops with speed (proprioceptive suppression)
double trem_mod = 1.0 / (1.0 + speed * 0.3);
double tr_x = tremor_amp * trem_mod
    * sin(2.0 * pi * tremor_freq * t_s + phase_x);

SDN scales noise proportionally with motor command magnitude:

double sdn_x = cfg.sdn_k * speed * N(0,1);
double sdn_y = cfg.sdn_k * speed * N(0,1);

The final position at each timestep sums all components: ballistic path, corrective sub-movements, curvature offset, OU drift, tremor, and signal dependent noise.

Direction dependent curvature from wrist biomechanics

Movements in different directions exhibit different curvature because of wrist and forearm anatomy. Horizontal movements, driven primarily by wrist rotation, tend to be straighter. Vertical movements, driven by forearm flexion and extension, curve more. SigmaDrift applies a perpendicular offset along the path:

curvature_amplitude = distance * 0.025 * direction_factor(angle) * N(0, 1)

The direction factor yields minimal curvature (~0.35) for horizontal movements, moderate curvature (~0.96) at 45-degree diagonals, and maximum curvature (~1.30) for vertical movements. The offset follows an asymmetric profile s^2 * (1-s)^3 that peaks at 40 percent of the path, during the acceleration phase when the motor command is largest, rather than at the midpoint a Bezier curve would produce.

inline double curvature_profile(double s) {
    if (s <= 0.0 || s >= 1.0) return 0.0;
    double v = s * s * (1.0 - s) * (1.0 - s) * (1.0 - s);
    constexpr double norm = 0.4 * 0.4 * 0.6 * 0.6 * 0.6;
    return v / norm;
}

inline double direction_factor(double angle) {
    double sa = std::abs(std::sin(angle));
    double ca = std::abs(std::cos(angle));
    return 0.5 + 0.8 * sa - 0.15 * ca;
}

Head-to-head metrics

Same start and end points, same distance (~630 pixels), same target width (20 pixels):

MetricSigmaDriftWindMouseReal Human
Movement time827 ms499 ms~750-850 ms (Fitts’)
Fitts’ predicted803 msN/A803 ms (using a=50, b=150)
Path efficiency0.9850.9730.95-0.99
Peak speed3.05 px/ms2.18 px/ms2.5-3.5 px/ms
Sub-movements2151-3
Endpoint error0.9 px0.5 px1-3 px
Velocity profileBell-shapedJaggedBell-shaped
Fitts’ complianceYes (~8% error)NoYes

Sub-movement count is the most decisive metric. WindMouse registers 15 velocity peaks because the wind force injects constant oscillation. Real humans produce 1 to 3. SigmaDrift produces 2, one ballistic and one corrective. That single number is enough for any competent behavioral classifier to flag WindMouse.

Movement time is the second critical discriminator. SigmaDrift’s 827 ms lands within 3 percent of the Fitts’ Law prediction for this distance and width pairing (803ms using standard parameters a=50, b=150). A detector that correlates movement time with log2(D/W + 1) across a session catches WindMouse’s 499 ms immediately. SigmaDrift’s timing falls inside the expected distribution.

WindMouse actually achieves lower endpoint error (0.5 pixels versus 0.9 pixels), which might seem beneficial until one considers that real humans scatter endpoints by 1 to 3 pixels. Excessive accuracy is its own detection signal. SigmaDrift’s endpoint error sits within the human range because SDN naturally degrades precision as movement speed increases.

Velocity profile comparison

The speed trace makes the distinction self-evident. SigmaDrift produces a smooth asymmetric bell: fast rise to peak velocity at roughly 35 percent of movement time, gradual deceleration, and one visible corrective bump near the end. WindMouse produces a noisy oscillation with no discernible structure.

The bell-shaped velocity profile is the most extensively studied characteristic of human pointing. Flash and Hogan formalized it in 1985. BeCAPTCHA-Mouse’s 37-dimensional feature extractor specifically decomposes the velocity signal into sigma-lognormal components. An algorithm that does not produce a bell-shaped profile does not survive analysis.

SigmaDrift generates output in the same feature space these detectors analyze, because it is built on the same motor control research they are built on. The sigma-lognormal primitives that BeCAPTCHA-Mouse uses to identify bots by decomposing trajectories are the same primitives SigmaDrift uses to construct them.

Configuration surface

The defaults produce reasonable output without adjustment. The parameters with the greatest impact on behavior:

ParameterDefaultEffect
fitts_a / fitts_b50 / 150Movement time scaling. Calibrate to the target application.
target_width20.0Wider targets yield faster movement; narrower targets yield slower movement with more corrections.
overshoot_prob0.15Fraction of movements that overshoot. Raise for jittery behavior.
curvature_scale0.025Path curvature magnitude. Zero produces straight lines.
ou_sigma1.2Lateral drift intensity. Higher values produce more visible wobble.
tremor_amp_max0.55Hand tremor ceiling. Raise for less steady hands.
sdn_k0.04Signal dependent noise gain. Higher values produce more jitter during fast movements.
sample_dt_mean8.0Mean polling interval in milliseconds. 8.0 matches standard 125 Hz polling.

target_width has the most dramatic effect. Smaller targets produce longer, more careful movements with more corrections, exactly as real human behavior dictates. Setting this parameter to match the actual element dimensions causes Fitts’ Law compliance to automatically adjust movement time, correction count, and endpoint precision according to task difficulty.

motor_synergy::config cfg;
cfg.target_width = 16.0;    // small button
cfg.overshoot_prob = 0.20;  // slightly nervous

auto path = motor_synergy::generate(x0, y0, x1, y1, cfg);

Implementation

The entire algorithm lives in a single header, motor_synergy.h. It requires C++20, has zero external dependencies, and runs in O(N) time where N is the number of trajectory samples. There is no numerical integration, no iterative solver, no neural network inference. Path progress evaluates analytically from the lognormal CDF via erf, and every noise source computes in a single arithmetic expression per timestep.

#include "motor_synergy.h"

auto path = motor_synergy::generate(start_x, start_y, target_x, target_y);

for (auto& pt : path) {
    // pt.x, pt.y = position
    // pt.t = timestamp in milliseconds
}

The included Win32 harness provides visual comparison. Press Space for a SigmaDrift trajectory, W for WindMouse, R to record real mouse input. The bottom panel overlays velocity profiles for all three sources.

Known limitations

SigmaDrift is a parametric model, not a learned one. It does not adapt to any individual user’s movement style without manual parameter adjustment. A Generative Adversarial Network (GAN) or diffusion model trained on recorded data would offer stronger personalization, but that requires training infrastructure and inference overhead. SigmaDrift trades personalization for simplicity: one header file, no dependencies.

The algorithm generates single-segment point-to-point movements. Chaining segments for tasks like menu navigation requires calling generate() multiple times with appropriate pauses between calls. There is no built-in model for sequential movement planning.

Human movement characteristics shift over long sessions. Fatigue reduces precision, slows reaction time, and alters movement profiles. SigmaDrift does not model fatigue. Every invocation of generate() produces movement with identical baseline characteristics regardless of how many movements precede it.

Fitts’ Law compliance is prescribed rather than emergent. Movement time is computed from the Fitts equation with an 8 percent lognormal CV, and the trajectory is shaped to fill that window. A more principled design would have timing arise from SDN dynamics directly. The SDN is present in the model but does not feed back into movement planning. The constants fitts_a and fitts_b ship as hardcoded defaults that produce reasonable timing for general pointing tasks; for best results in a specific application context, calibrate them against observed movement times in that environment.

WindMouse fails under temporal feature extraction because gravity-and-wind dynamics produce the wrong velocity shape, the wrong sub-movement count, the wrong timing distribution, and no relationship between movement duration and task geometry. SigmaDrift closes that gap by constructing trajectories from the same motor control primitives, sigma-lognormal pulses, SDN, OU drift, and speed-modulated tremor, that modern behavioral classifiers decompose input into. The source is on GitHub.