Coding a Simple Typing Speed Test in Python

Key Points
  • Python's time.perf_counter() gives nanosecond resolution — much better than time.time() for short intervals.
  • The main challenge is reading single keystrokes without waiting for Enter — the standard input() function won't work.
  • The readchar or curses library solves the raw input problem on all platforms.
  • The WPM formula is the same as browser tests: (correct_chars / 5) / elapsed_minutes.
  • Adding backspace support requires maintaining a mutable buffer of the typed text, not just comparing at the end.

Why Terminal Typing Tests Are Worth Building

A Python terminal typing test is a good learning project because it forces you to solve a set of interesting problems: raw keyboard input, high-precision timing, character-by-character comparison, and score calculation. The same concepts appear in browser-based tests, just expressed differently. Understanding the terminal version makes the web version easier to reason about.

It's also practically useful — some developers genuinely prefer a terminal-based test because they already live in the command line. See what TTYPER offers for developers for the best ready-made version, or read on to build your own.

The Core Components

ComponentPython toolNotes
Raw keyboard inputreadchar or cursesBypasses line buffering
High-precision timingtime.perf_counter()Nanosecond resolution
Character comparisonString indexingCompare buffer[i] against expected[i]
WPM calculationFormula(correct_chars / 5) / minutes
Text displaysys.stdout or cursesShow expected and typed text side by side

The Timing Problem

Use time.perf_counter(), not time.time(). The difference matters for short intervals:

  • time.time() returns the wall clock in seconds, with platform-dependent resolution (sometimes as coarse as 10ms on Windows)
  • time.perf_counter() uses the system's highest-resolution timer, typically with nanosecond precision

Record the start time when the first character is typed, not when the test begins. This avoids penalising the time spent reading the passage before starting. Record the end time on the last correct character or when the timer expires.

Handling Backspace

The trickiest part of a terminal typing test is backspace handling. You need a mutable buffer of the characters typed so far. When backspace is pressed, pop the last character from the buffer and move the cursor back. The buffer approach also makes it easy to track the state of each position: correct, incorrect, or untyped.

A simple data structure:

expected = list("the quick brown fox")
typed = []

# On each keystroke:
if key == BACKSPACE:
    if typed:
        typed.pop()
else:
    typed.append(key)

# At each step, compare typed[i] against expected[i]

The WPM Calculation

The standard WPM formula: WPM = (correct_characters / 5) / elapsed_minutes

Count only characters that were typed correctly at the final cursor position. If a character was typed incorrectly and then corrected, it counts as correct. If it was typed incorrectly and left, it counts as an error.

For net WPM: net_WPM = gross_WPM - (uncorrected_errors / elapsed_minutes)

A Minimal Working Script

The minimal version needs about 50–80 lines of Python:

  1. Load a word list or passage
  2. Display the passage to the screen
  3. Enter a raw input loop using readchar
  4. On each keystroke: update the typed buffer, update the display, check if the test is complete
  5. On completion: calculate elapsed time, correct characters, error count, WPM, and accuracy
  6. Display results

For the browser-based version of these same concepts, see how to build a typing test website from scratch. For a command-line implementation in a language better suited to terminal UIs, see how to create a command-line typing test script.

Ready to put it into practice?

Take a free typing test and start tracking your progress.

Free Typing Test →