A keyboard input simulator that types a block of text at a configurable speed, with timing jitter and occasional corrected typos.
Built to explore a narrow question: what makes synthetic keystrokes look mechanical, and what does it take to make the timing distribution resemble a person's? Constant-interval typing is trivially distinguishable from human input. The interesting part is the noise model.
Generating realistic keystroke streams for testing input handling, demoing UIs without typing by hand, and recording screencasts at a consistent pace.
It is not intended for typing tests, proctored exams, or anywhere output
is represented as your own unaided work. pyautogui drives the real system
keyboard, so it types into whatever window has focus — treat it accordingly.
Three things separate this from a loop that emits a character every n milliseconds:
Variable inter-key delay. Base delay is 60 / wpm seconds, and each
keystroke samples uniformly from ±20% of it. Real typing has variance;
a fixed interval does not.
Adjacency-based typos. A 5% per-character chance of hitting a nearby key
instead of the intended one. The substitution comes from a hand-built QWERTY
adjacency map — d mistypes as one of s r f c e, never as a random letter —
because physical typos are a function of finger travel, not chance.
Visible correction. A typo is typed, held 100–300 ms, backspaced, then the correct character is typed after another 100–200 ms. The pause before noticing is the part that reads as human; an instantaneous correction does not.
def get_mistake_char(self, correct_char):
nearby_keys = {'a': 'sqwz', 'b': 'vghn', 'c': 'xdfv', ...}
return random.choice(nearby_keys[correct_char.lower()])The model is uniform where real typing isn't. Human inter-key intervals are
roughly log-normal, not uniform, and speed depends heavily on digraph
frequency — th is much faster than qz for any touch typist. Error rate is
also constant here, where a person's climbs with fatigue and speed. A more
faithful version would sample from per-digraph timing distributions.
Scheduling uses Tkinter's after(), so the whole thing is single-threaded and
delays are subject to the event loop's resolution.
pip install -r requirements.txt
python typer-bot.pySet a WPM, paste your text, click Start, then click into the target window
during the 5-second countdown. Shift+Enter stops it.
macOS requires granting Accessibility permission to your terminal or Python
under System Settings → Privacy & Security, since pyautogui posts synthetic
keyboard events.
Python, tkinter for the GUI, pyautogui for input. ~150 lines, no other
dependencies.