Human behavior simulation library for browser automation. Generates realistic mouse movements (Bezier curves + Fitts' Law), keyboard input (WPM + typos), scroll patterns (easing curves), and human-like timing.
Framework-agnostic — works with Selenium, Playwright, Puppeteer, or any automation tool.
pip install synthetic-behaviorOnly dependency: numpy>=1.24
Generates curved paths with Bezier interpolation, Fitts' Law timing, jitter, and occasional overshoot.
from synthetic_behavior import MouseSimulator
mouse = MouseSimulator()
path = mouse.generate_path(100, 100, 500, 300)
len(path) # 55 points (varies by distance)
path[0] # (98.59, 97.16, 50.31) — (x, y, delay_ms)
path[-1] # (499.49, 299.09, 54.86)Each point is (x, y, delay_ms). Total movement time follows Fitts' Law — longer distances take more time. The velocity profile is bell-shaped (minimum-jerk model): slow start, fast middle, slow end.
offset = mouse.click_offset(40, 40) # gaussian offset from center
delay = mouse.hover_delay() # delay before clicking (~80ms mean)Generates keystroke events with realistic timing, shift handling, and occasional typos with backspace correction.
from synthetic_behavior import KeyboardSimulator
kb = KeyboardSimulator(wpm=60)
events = kb.generate_keystrokes("Hello")
len(events) # 12 events
# [
# {'key': 'Shift', 'type': 'keydown', 'delay_ms': 41},
# {'key': 'H', 'type': 'keydown', 'delay_ms': 50},
# {'key': 'H', 'type': 'keyup', 'delay_ms': 21},
# {'key': 'Shift', 'type': 'keyup', 'delay_ms': 23},
# {'key': 'e', 'type': 'keydown', 'delay_ms': 44},
# {'key': 'e', 'type': 'keyup', 'delay_ms': 19},
# ...
# ]Typos are injected at ~3% rate by default, using nearby keys on a QWERTY layout, then corrected with Backspace.
Generates scroll events with ease-in-out cubic easing and optional reading pauses.
from synthetic_behavior import ScrollSimulator
scroll = ScrollSimulator()
events = scroll.generate_scroll(0, 800)
len(events) # 16 steps
events[0] # {'delta_y': 1.0, 'delay_ms': 29.1}Each event is {'delta_y': float, 'delay_ms': float}. The scroll accelerates then decelerates (ease-in-out). For long scrolls (>500px), there's a 30% chance of a reading pause mid-scroll.
from synthetic_behavior import TimingEngine
TimingEngine.wait_human(500, 2000) # 1196.0 ms (gaussian, clamped)
TimingEngine.page_read_time(5000) # 231700.6 ms (based on ~250 WPM)
TimingEngine.click_delay() # 144.4 ms (delay before click)| Method | Returns | Description |
|---|---|---|
generate_path(start_x, start_y, end_x, end_y, target_width=20) |
list[(x, y, delay_ms)] |
Bezier path with Fitts' Law timing |
click_offset(target_width=20, target_height=20) |
(dx, dy) |
Gaussian offset from target center |
hover_delay() |
float |
Pre-click hover delay in ms |
| Method | Returns | Description |
|---|---|---|
generate_keystrokes(text) |
list[dict] |
Keystroke events with timing |
| Method | Returns | Description |
|---|---|---|
generate_scroll(start_y, target_y) |
list[dict] |
Scroll events with easing |
| Method | Returns | Description |
|---|---|---|
wait_human(min_ms=500, max_ms=2000) |
float |
Human-like wait duration |
page_read_time(content_length) |
float |
Estimated reading time in ms |
click_delay() |
float |
Pre-click delay in ms |
# Selenium
from synthetic_behavior import MouseSimulator, KeyboardSimulator
from selenium.webdriver.common.action_chains import ActionChains
mouse = MouseSimulator()
kb = KeyboardSimulator()
# Move mouse to element
path = mouse.generate_path(0, 0, elem.location['x'], elem.location['y'])
for x, y, delay in path:
ActionChains(driver).move_by_offset(x, y).perform()
time.sleep(delay / 1000)
# Type text
for event in kb.generate_keystrokes("search query"):
if event['type'] == 'keydown':
elem.send_keys(event['key'])
time.sleep(event['delay_ms'] / 1000)MIT