Skip to main content

Guided Motion

English | 简体中文

PyPI version Python versions Test status License

Guided Motion turns a reference polyline into a bounded, timed sequence of pointer events. Unlike point-to-point cursor generators, it preserves the shape of an arbitrary multi-anchor path while varying its spatial sampling and timing inside explicit safety limits.

The core is pure Python with no runtime dependencies. A small optional adapter can execute a plan through Playwright.

Why

Many automation tools generate one curve between two points. That is useful for navigation but not for drawing, annotation, route replay, complex dragging, or externally recognized gesture paths. Guided Motion starts with the path you already have and makes controlled variations while guaranteeing:

  • every event remains inside rectangular bounds;
  • the result stays within a maximum deviation from the reference path;
  • the first and last events obey a tighter endpoint budget;
  • event times are strictly increasing;
  • the same seed produces the same complete plan.

Installation

Install the dependency-free planner:

pip install guided-motion

Install it with Playwright available:

pip install "guided-motion[playwright]"
playwright install

Plan a reference path

from guided_motion import Bounds, Constraints, MotionPlanner

reference = [
    (30, 80),
    (90, 35),
    (145, 95),
    (220, 55),
]

plan = MotionPlanner(seed=42).plan(
    reference,
    bounds=Bounds(x=0, y=0, width=260, height=140),
    constraints=Constraints(
        max_deviation=6.0,
        endpoint_deviation=1.5,
    ),
)

print(plan.duration)
print(plan.max_deviation)
for event in plan.events:
    print(event.x, event.y, event.t)

MotionPlan.events is an immutable tuple of MotionEvent(x, y, t). Time t is relative to the start of execution in seconds.

Execute with Playwright

Guided Motion sends every planned event once with steps=1 and schedules it against the plan's absolute relative deadline. It does not let Playwright add its own intermediate points.

from guided_motion.playwright import execute_motion

first = plan.events[0]
await page.mouse.move(first.x, first.y)
await page.mouse.down()
try:
    await execute_motion(page.mouse, plan)
finally:
    await page.mouse.up()

Pressing, releasing, retries, and application state remain the caller's responsibility.

VAPTCHA integration example

VAPTCHA gesture challenges are one possible source of a reference path. Guided Motion does not take screenshots or recognize the challenge. Supply those points from your own authorized recognition flow, convert them into page coordinates, and plan within the challenge image bounds:

from guided_motion import Bounds, Constraints, MotionPlanner
from guided_motion.playwright import execute_motion

# Returned by the caller's screenshot/recognition code in image-local coordinates.
recognized_points = [(18, 92), (46, 65), (81, 104), (126, 48), (178, 83)]

box = await challenge_image.bounding_box()
if box is None:
    raise RuntimeError("challenge image is not visible")

reference = [(box["x"] + x, box["y"] + y) for x, y in recognized_points]
plan = MotionPlanner().plan(
    reference,
    bounds=Bounds(box["x"], box["y"], box["width"], box["height"]),
    constraints=Constraints(max_deviation=6.0, endpoint_deviation=1.5),
)

first = plan.events[0]
await page.mouse.move(first.x, first.y)
await page.mouse.down()
try:
    await execute_motion(page.mouse, plan)
finally:
    await page.mouse.up()

Recognition accuracy, coordinate transforms, verification status, challenge refresh, retries, browser context lifecycle, and site authorization are deliberately outside this package.

Mechanical uniformity and detection

Fixed Bezier shapes, fixed event counts, equal-distance samples, and fixed durations can make a large set of automation traces mechanically uniform. Guided Motion introduces correlated spatial and temporal variation while keeping every result inside measurable constraints.

This does not make automation undetectable and is not a general risk-control bypass. Detection systems can also use network identity, browser state, surrounding interaction, account history, and server-side signals. Guided Motion only plans pointer movement. Use it for systems and flows you are authorized to automate.

Constraints

Constraints(
    max_deviation=6.0,
    endpoint_deviation=1.5,
    min_duration=0.55,
    max_duration=2.4,
    min_events=18,
    max_events=180,
)

The input reference must contain at least two distinct consecutive points and must already lie inside Bounds. Guided Motion fails clearly when that contract is not satisfied instead of silently changing coordinate systems.

Reproducibility

Create a new planner with the same seed to reproduce a complete plan:

first = MotionPlanner(seed=7).plan(reference, bounds=bounds)
second = MotionPlanner(seed=7).plan(reference, bounds=bounds)
assert first == second

A planner owns its random stream, so repeated calls on the same instance intentionally produce new plans.

Development

uv sync --all-extras --dev
uv run pytest -q
uv run ruff check src tests
uv run mypy src/guided_motion
uv build

Guided Motion supports Python 3.10 through 3.13 and is licensed under the MIT License.

Release files for guided-motion 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for guided-motion 0.1.1
File Size Uploaded
guided_motion-0.1.1.tar.gz 12.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for guided-motion 0.1.1
File Interpreter ABI Platform
guided_motion-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 24.7 kB

Release files / guided_motion-0.1.1.tar.gz

Download URL guided_motion-0.1.1.tar.gz
Size 12.9 kB
Tags Source
SHA-256 checksum
How to use checksums
f25c53065a7d58f927b8997804f3e07500a139c11f5a1e7351f50b6f18991a76
BLAKE2b-256 checksum
How to use checksums
2bf8e337fb6c6c0ac73a10f549ffecaf2b1ac07d34b97520375351ee1416a016
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.

Transparency log

Release files / guided_motion-0.1.1-py3-none-any.whl

Download URL guided_motion-0.1.1-py3-none-any.whl
Size 11.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a86f956d0884cadebb29e50145b89f6ebaea2a0bd0b0705411568151dd76ffff
BLAKE2b-256 checksum
How to use checksums
e2ba6ca27f4851f5aeba045e638da5147574bafd81b9992e41ff7b327b48d1af
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page