Algorithm Explained

This page provides an in-depth explanation of the WindMouse algorithm, its physics principles, and the mathematics behind human-like mouse movement generation.

Overview

The WindMouse algorithm generates realistic, non-linear mouse trajectories by simulating physical forces acting on the cursor. Unlike simple linear interpolation (which moves in a straight line at constant speed), WindMouse creates curved paths with variable speed—closely mimicking how humans naturally move a mouse.

The Problem with Traditional Mouse Movement

Most automation tools use linear interpolation:

# Naive linear movement (easily detectable)
for t in range(steps):
    x = start_x + (dest_x - start_x) * (t / steps)
    y = start_y + (dest_y - start_y) * (t / steps)
    move_mouse(x, y)

This approach has several problems:

  • Perfect straight lines: Humans rarely move the mouse in perfectly straight lines

  • Constant speed: Real human movement accelerates and decelerates

  • No variation: Repeated movements follow identical paths

  • Easily detectable: Bot detection systems can identify these patterns

The WindMouse Solution

WindMouse solves these problems by simulating a physics-based system with three key forces:

  1. Gravity: Attracts the cursor toward the target (directed force)

  2. Wind: Adds randomness and curvature to the path (random force)

  3. Velocity Damping: Slows down as the cursor approaches the target

The algorithm operates in discrete time steps, updating velocity and position at each iteration.

Mathematical Model

Physics Simulation

At each time step, the algorithm updates the cursor state based on forces:

Position Update:

\[ \begin{align}\begin{aligned}x_{t+1} = x_t + v_x\\y_{t+1} = y_t + v_y\end{aligned}\end{align} \]

Velocity Update:

\[ \begin{align}\begin{aligned}v_x = v_x + W_x + G_x\\v_y = v_y + W_y + G_y\end{aligned}\end{align} \]

Where: - \(v_x, v_y\) are velocity components - \(W_x, W_y\) are wind force components - \(G_x, G_y\) are gravity force components

Gravity Force

Gravity provides the directed force pulling the cursor toward the target:

\[ \begin{align}\begin{aligned}\begin{split}G_x = g \\cdot \\frac{(dest_x - x)}{dist}\end{split}\\\begin{split}G_y = g \\cdot \\frac{(dest_y - y)}{dist}\end{split}\end{aligned}\end{align} \]

Where: - \(g\) is gravity_magnitude (default: 9) - \(dist = \\sqrt{(dest_x - x)^2 + (dest_y - y)^2}\) is the distance to target

This creates a force vector pointing directly at the target with magnitude proportional to gravity_magnitude.

Wind Force

Wind adds randomness and curvature. Its behavior changes based on distance from target:

Far from target (\(dist \\geq damped\\_distance\)):

\[ \begin{align}\begin{aligned}\begin{split}W_x = \\frac{W_x}{\\sqrt{3}} + \\frac{(2r - 1) \\cdot w}{\\sqrt{5}}\end{split}\\\begin{split}W_y = \\frac{W_y}{\\sqrt{3}} + \\frac{(2r - 1) \\cdot w}{\\sqrt{5}}\end{split}\end{aligned}\end{align} \]

Close to target (\(dist < damped\\_distance\)):

\[ \begin{align}\begin{aligned}\begin{split}W_x = \\frac{W_x}{\\sqrt{3}}\end{split}\\\begin{split}W_y = \\frac{W_y}{\\sqrt{3}}\end{split}\end{aligned}\end{align} \]

Where: - \(r\) is a random value in [0, 1] - \(w = \\min(wind\\_magnitude, dist)\) is the effective wind magnitude - \(\\sqrt{3} \\approx 1.732\) is the damping factor - \(\\sqrt{5} \\approx 2.236\) is the fluctuation factor

The wind force: - Decays over time (divided by √3 each step) - Adds random fluctuations when far from target - Only decays (no new randomness) when close to target

Velocity Clamping

The velocity magnitude is clamped to prevent excessive speed:

\[\begin{split}|v| = \\sqrt{v_x^2 + v_y^2}\end{split}\]

If \(|v| > max\\_step\):

\[ \begin{align}\begin{aligned}\begin{split}clip = \\frac{max\\_step}{2} + r \\cdot \\frac{max\\_step}{2}\end{split}\\\begin{split}v_x = \\frac{v_x}{|v|} \\cdot clip\end{split}\\\begin{split}v_y = \\frac{v_y}{|v|} \\cdot clip\end{split}\end{aligned}\end{align} \]

Where \(r\) is random in [0, 1].

This ensures the cursor doesn’t move too fast while adding slight speed variation.

Algorithm Pseudocode

Here’s the complete algorithm in pseudocode:

function WindMouse(start_x, start_y, dest_x, dest_y, gravity, wind, max_step, damped_dist):
    x, y = start_x, start_y
    vx, vy = 0, 0
    wx, wy = 0, 0

    while distance(x, y, dest_x, dest_y) >= 1:
        dist = distance(x, y, dest_x, dest_y)
        wind_current = min(wind, dist)

        # Update wind
        if dist >= damped_dist:
            wx = wx/sqrt(3) + (2*random() - 1) * wind_current/sqrt(5)
            wy = wy/sqrt(3) + (2*random() - 1) * wind_current/sqrt(5)
        else:
            wx = wx/sqrt(3)
            wy = wy/sqrt(3)
            if max_step < 3:
                max_step = random() * 3 + 3
            else:
                max_step = max_step/sqrt(5)

        # Update velocity with wind and gravity
        vx = vx + wx + gravity * (dest_x - x) / dist
        vy = vy + wy + gravity * (dest_y - y) / dist

        # Clamp velocity
        v_mag = sqrt(vx^2 + vy^2)
        if v_mag > max_step:
            v_clip = max_step/2 + random() * max_step/2
            vx = (vx / v_mag) * v_clip
            vy = (vy / v_mag) * v_clip

        # Update position
        x = x + vx
        y = y + vy

        yield (round(x), round(y))

Parameter Effects

Understanding how each parameter affects the generated path:

gravity_magnitude

Effect: Controls the strength of attraction to the target.

  • Low (3-6): Weak attraction
    • Path wanders more

    • Takes longer to reach target

    • More influenced by wind

    • Very curved trajectories

  • Medium (7-10): Balanced (Default: 9)
    • Natural-looking curves

    • Reasonable convergence time

    • Good balance of control and randomness

  • High (11-15): Strong attraction
    • More direct paths

    • Faster convergence

    • Less curve, more purposeful

    • Dominates wind effect

Visual Effect:

Low gravity (5):     Medium gravity (9):    High gravity (13):
     *----->              *---->                  *--->
    /       \\            /     \\                 |    \\
   /         \\          /       \\                |     \\
  o           T         o         T               o      T
(very curved)        (natural)              (fairly direct)

wind_magnitude

Effect: Controls the amount of random curvature and unpredictability.

  • Low (0-2): Minimal wind
    • Nearly straight paths

    • Predictable movement

    • Less human-like

    • Good for speed-priority scenarios

  • Medium (3-5): Moderate wind (Default: 3)
    • Natural curves

    • Good human-like appearance

    • Balanced unpredictability

  • High (6-10): Strong wind
    • Very curved paths

    • Highly unpredictable

    • May look drunk/erratic

    • Good for testing detection systems

Visual Effect:

Low wind (1):        Medium wind (3):       High wind (8):
     *-->                 *~>                    *~~>
     |  \\                /  \\                  / |\\
     |   \\              /    \\                /  | \\
     o    T             o      T              o   |  T
(nearly straight)     (gentle curve)      (chaotic path)

max_step

Effect: Controls the maximum speed (pixels per step).

  • Low (5-10): Slow movement
    • Takes longer to reach target

    • More controlled, deliberate

    • Good for precision tasks

  • Medium (12-18): Normal speed (Default: 15)
    • Balanced speed and control

    • Natural-looking pace

  • High (20-30): Fast movement
    • Quick traversal

    • May look less natural at very high values

    • Good for efficiency-priority scenarios

Note: The actual movement speed also depends on tick_delay and step_duration in move_to_target().

damped_distance

Effect: Distance from target (in pixels) where behavior changes.

  • Low (5-10): Late damping
    • Maintains speed and wind longer

    • Sharper approach to target

    • Less cautious

  • Medium (12-15): Balanced (Default: 12)
    • Natural slowdown

    • Smooth approach

    • Human-like deceleration

  • High (20-30): Early damping
    • Slows down far from target

    • Very cautious approach

    • May look hesitant

Visual Effect:

Low damped_dist (8):       Medium (12):         High (25):
     *=======>                  *=====>              *==>
     |||||||||                  ||||-->              ||---->
-----o-------T            -----o-------T       -----o-------T
(fast until close)       (gradual slow)      (early slowdown)

Why These Specific Constants?

You might wonder why the algorithm uses \(\\sqrt{3}\) and \(\\sqrt{5}\):

√3 ≈ 1.732 (Wind Damping)
  • Provides gradual decay of wind force

  • Balances persistence and decay

  • Empirically found to produce natural-looking curves

  • Fast enough to converge, slow enough to maintain influence

√5 ≈ 2.236 (Wind Fluctuation & Velocity Reduction)
  • Scales random fluctuations appropriately

  • Prevents wind from overwhelming gravity

  • Creates smooth randomness rather than jitter

  • Also used for max_step reduction near target

These constants were likely chosen through experimentation to balance: - Convergence speed - Path naturalness - Computational stability - Aesthetic appeal

Algorithm Characteristics

Convergence Guarantee

The algorithm is guaranteed to converge to the target because:

  1. Gravity always points toward target and doesn’t decay

  2. Wind decays over time (divided by √3 each step)

  3. As distance decreases, wind becomes less effective

  4. Close to target, only gravity remains (no new wind)

The stopping condition is distance < 1 pixel, which is always eventually satisfied.

Non-Determinism

The algorithm is non-deterministic due to random wind fluctuations. Running it multiple times with the same parameters produces different paths.

This is a feature, not a bug: - Prevents pattern recognition - Mimics human inconsistency - Makes bot detection harder

Scale Independence

The algorithm works well across different screen resolutions and distances because:

  • Parameters are pixel-based (scale-agnostic)

  • Wind magnitude adapts to distance (min(wind_magnitude, dist))

  • Gravity is normalized by distance (/ dist)

However, you may want to tune parameters for very large distances (e.g., 4K displays) or very short distances (small UI elements).

Comparison to Other Algorithms

Linear Interpolation

# Linear
x = start_x + (dest_x - start_x) * t

WindMouse advantages: - ✅ Non-linear paths - ✅ Variable speed - ✅ Unpredictable - ❌ More computationally expensive

Bézier Curves

Bézier curves can create smooth paths but:

  • ❌ Require predetermined control points

  • ❌ Path is deterministic given control points

  • ❌ No physical simulation

  • ❌ Speed is uniform (unless manually varied)

WindMouse advantages: - ✅ Physics-based (more natural) - ✅ Emergent behavior - ✅ No need to specify control points

Simplex/Perlin Noise

Noise functions can add randomness but:

  • ❌ Don’t inherently move toward target

  • ❌ Require combining with directed movement

  • ❌ May produce unnatural patterns

WindMouse advantages: - ✅ Built-in target seeking - ✅ Balanced randomness and purpose - ✅ Complete solution in one algorithm

Implementation Details

Generator-Based Architecture

The wind_mouse() function is a generator, yielding coordinates one at a time:

def wind_mouse(...) -> Generator[tuple[Coordinate, Coordinate], None, None]:
    # ...
    yield Coordinate(move_x), Coordinate(move_y)

Benefits:

  • Memory efficient (doesn’t store entire path)

  • Allows real-time movement

  • Can be interrupted mid-path

  • Enables custom tick-based control

Coordinate Type Safety

WindMouse uses NewType for coordinates:

Coordinate = NewType("Coordinate", int)

This provides type safety without runtime overhead, helping catch errors at type-check time.

Optimization Opportunities

For performance-critical applications, consider:

  1. Pre-compute sqrt(3) and sqrt(5): Already done in the library

  2. Use NumPy vectorization: Already used for np.hypot()

  3. Reduce tick_delay: Set to 0 for maximum speed

  4. Reduce step_duration: Use smaller values (0.01-0.05)

  5. Increase max_step: Higher values = fewer total steps

Example optimized configuration:

from windmouse.pyautogui_controller import PyautoguiMouseController
from windmouse import Coordinate

mouse = PyautoguiMouseController(
    max_step=20,  # Increase speed
    gravity_magnitude=12,  # More direct
)

mouse.dest_position = (Coordinate(800), Coordinate(600))
mouse.move_to_target(
    tick_delay=0,  # No delay
    step_duration=0.01  # Fast transitions
)

Visualizing the Algorithm

To better understand the algorithm, you can visualize the path:

import matplotlib.pyplot as plt
from windmouse.core import wind_mouse, Coordinate

# Generate path
path = list(wind_mouse(
    Coordinate(0), Coordinate(0),
    Coordinate(500), Coordinate(500),
    gravity_magnitude=9,
    wind_magnitude=3,
    max_step=15,
    damped_distance=12
))

# Extract x and y coordinates
xs, ys = zip(*path)

# Plot
plt.figure(figsize=(10, 10))
plt.plot(xs, ys, 'b-', linewidth=1, alpha=0.7)
plt.plot(xs[0], ys[0], 'go', markersize=10, label='Start')
plt.plot(xs[-1], ys[-1], 'ro', markersize=10, label='End')
plt.legend()
plt.grid(True, alpha=0.3)
plt.axis('equal')
plt.title('WindMouse Path Visualization')
plt.show()

Further Reading

For more information on human-computer interaction and mouse movement patterns:

Related Algorithms:

  • Inverse Kinematics (robotics)

  • Path smoothing algorithms

  • Particle systems in computer graphics

Next Steps

Now that you understand the algorithm, explore:

  • The complete API Reference reference

  • Practical examples in Usage Guide

  • Experiment with parameters to find optimal settings for your use case