How do typing tests detect cheating? How we check ours

Typing tests detect cheating by checking the keystroke log behind each score. Ours recomputes every result from its keystroke log, checks a hash, tests the log's structure and timing, and holds any timed result above 130 WPM whose timing also looks unusual off the public boards until you pass a re-test. Timing checks catch careless bots; the re-test makes a careful one do it all again.

The first bot that beat our timing checks was ours.

Our end-to-end test helper pressed keys at a fixed 40 ms interval, because that's what a test script does. The server recomputed the result, confirmed the hash, and scored the run as suspicious: zero variance between keystrokes, zero rollover, identical hold times. We had to add random per-word jitter and occasional 80 to 300 ms "thinking" pauses before our own suite could post a human-looking result. The option is still in the code, labelled humanLike.

The checks work against anything that doesn't try, and anything that tries can pass them, so the design puts the real weight on a re-test.

Step one: recompute everything

Your browser doesn't send us a WPM. It sends the keystroke log (every keydown, keyup, input and composition event, timestamped with the monotonic performance.now() clock), the metrics it computed, and a SHA-256 hash of the canonical result.

The server runs the same metric code on the log and compares. If any number differs by more than 0.01, the result is rejected as metrics_mismatch. If the hash doesn't match, it's rejected as hash_mismatch. A browser extension that edits the WPM field, the commonest cheat on sites that trust the client, fails here. The same functions produce the number on your screen and in the database, which is why our metrics page can promise its worked example reproduces to two decimals.

Why not time the test on the server? Then your score would include the network. Every keystroke would reach us a little late, by an amount that varies with your connection, and a fast typist on a slow line would lose speed they really had. The log gives us the client's clock for accuracy and the server's recomputation for honesty.

Step two: does the log make sense?

Before any timing analysis, plain checks on the shape of the submission:

  • The test must last at least 15 seconds or 10 words. Shorter runs are too noisy to rank and the easiest to inflate.
  • A result can't arrive before the test could have finished: its start plus its own length. A 60-second result that arrives twenty seconds after it started didn't happen.
  • The number of events must fit the characters typed.
  • On desktop, every input event must follow a keydown within 50 ms. This catches pasted text, scripted input events and most keystroke-injection tools, which fire text without a physical key. Typing through an input method editor (IME composition) is exempt, and phone results stay off the public boards.
  • A log with no start, no end or non-monotonic timestamps is malformed and rejected outright.

None of this is clever, but it removes most of the volume.

Step three: what can keystroke timing reveal?

This is the part we deliberately don't fully document: the feature list is public, the cut-offs and weights are not. A published cut-off is a target. If we said "a standard deviation under N milliseconds is flagged", the next version of every script would add N plus one. The features we score:

The average gap between keys and how much it varies. Human typing is irregular. In Dhakal and colleagues' 2018 study of 168,960 typists, the average inter-key interval was about 239 ms with a standard deviation of about 112 ms. A script typing at a fixed interval has almost no variation; a 2025 arXiv study of a keystroke-based CAPTCHA saw exactly that in its test bot, which typed with a fixed 50 ms delay. This is our strongest signal, and the one our helper tripped.

The share of gaps under 40 ms. A 40 ms gap, kept up, is 1,500 characters a minute, or 300 WPM at five characters a word. Fingers do produce single gaps that short when they overlap keys during rollover, so one short gap means nothing. A log where a large share of gaps is that short is not typing.

Key hold times. Real fingers vary by tens of milliseconds; synthetic events often have identical or near-zero hold times, because the tool fires keydown and keyup back to back.

Rollover ratio. How often the next key goes down before the last one comes up. Dhakal measured an average rollover ratio of 25%, with most fast typists rolling over on 40 to 70% of keypresses, and keys overlapping by 30 ms on average. Slow typists use almost none, so a fast result with no rollover stands out. Bots rarely overlap keys.

Longest pause, second-to-second speed swings, and the share of gaps over one second. People hesitate in ways a script has to be told to imitate.

A scoring model combines these and can flag a result as suspicious. Below 130 WPM, a flag doesn't reject anything on its own. Your result is never hidden from you; these checks decide only whether it's ranked.

Can typing test cheating get past timing checks?

Yes, if the cheat is built with care. A January 2026 arXiv paper by David Condrey tested keystroke-timing detectors meant to separate human-typed from AI-generated text. They separated fully automated injection perfectly, but attacks with human timing (a person retyping AI text, or a program sampling delays from human typing data) evaded all five classifiers at rates of at least 99.8%. The paper is about authorship detection, not typing tests, but the lesson carries over: step three catches the careless and not much else.

Our own test helper makes the same point on a small scale: random jitter and a few pauses moved it from obviously synthetic to plausible.

So step three is a filter. The verdict comes from step four.

Step four: the verification re-test

If a timed result is above 130 WPM and its timing also looks unusual, you see it and it's saved, but it stays off the public boards until you pass a verification re-test. That's a fresh 30-second test, and you need to reach at least 75% of the speed you claimed. A one-off fluke or a borrowed log doesn't survive a second run.

Why 130 WPM? It's about two and a half times the 51.56 WPM mean in Dhakal's data, well beyond most typists, and anyone who really types that fast can do it twice. A sudden fast result with odd timing is the shape a borrowed log or a script produces, and if it's honest, one extra test is a small price.

The re-test is an ordinary typing test, in ordinary text, not a distorted image or a page of odd symbols. Hard-to-read text slows everyone down, and the people it slows most are the honest fast typists the re-test exists to clear. A verification that honest fast typists fail is worse than none. Ordinary text also makes the check fair: the re-test's own log goes through the same timing checks as any other result.

Rate limits and bans

Submissions are rate-limited: thirty a minute per signed-in user, sixty per IP address. An account that keeps tripping the checks is shadow-banned, meaning quietly hidden from the boards, and three serious anti-cheat events in 24 hours trigger that automatically. Counting events inside a window means one odd result never costs you an account, while a script that fails again and again stops quickly.

All-time leaderboards also require 30 minutes of typing on the account, so a fresh account with one perfect run can't top them.

What gets you flagged by accident

Most flagged results aren't cheating. The honest ways to trip a check:

Autocorrect on a phone. A replacement arrives as one input event that changes several characters. We count it as one keypress, flag it, and keep the result off the public boards with a note, not a rejection.

Pasted text. Pasting is blocked, and a result where it was attempted is excluded from boards.

Switching tabs. Focus leaving the page for more than two seconds during a test marks it interrupted; it's saved and shown but not ranked.

Pausing. A gap over five seconds in a word or quote test marks the result paused. Same treatment.

IME composition. Japanese, Korean and Chinese input arrive through composition events, so we skip the keydown rule for them. If something still looks wrong, that's a bug, and we'd like the log.

Offline results. A result synced more than ten minutes after it finished stays in your history unranked, because we can't validate its clock.

Being genuinely fast. Above 130 WPM you'll meet the re-test, probably on your first good day.

How to appeal

Every exclusion shows its reason in your history. If you think we're wrong about a result or a restriction, email the appeal address on our anti-cheat page from the address on your account, with the result link or the date and time of the test. A person watches the keystroke replay and replies within a few days. Appeals are never decided by the automated model alone.

Have we just published a how-to for passing the checks? Partly. The feature list was always discoverable from the behavior, and the cut-offs stay private. The re-test is the part that matters, and the way to pass it is to type.

Frequently asked questions

How do typing tests detect bots?

Good ones read the keystroke log: whether every character has a key press behind it, how much the gaps between keys vary, and how keys are held and overlapped. Scripts tend to be too regular; careful bots can imitate humans, which is why we add a re-test.

Can a bot pass a typing test's anti-cheat?

A careful one can pass timing checks: research on keystroke-timing detectors found forged human-like timing evaded them almost every time. That's why we hold fast results for a re-test, which a bot has to pass again with a second log that clears the same checks.

Why was my typing test result not ranked?

Usually a test under 15 seconds or 10 words, a pause over five seconds, a tab switch, or a result above 130 WPM waiting for its re-test. Your history shows the reason.

Sources