How to Build Your Own Typing Test Website from Scratch
- A typing test has five core components: text source, input handler, timer, scorer, and results display.
- Use performance.now() for keystroke timing — it gives sub-millisecond precision in any modern browser.
- Net WPM requires tracking both correct and incorrect characters separately throughout the test.
- Anti-cheat requires server-side validation of keystroke timing patterns — client-side scores can be faked.
- The hardest part isn't the WPM formula — it's handling the input edge cases (backspace, spaces, paste blocking).
The Architecture Overview
A production-quality typing test has five distinct systems working together. Understanding each one separately makes the whole thing easier to build.
| Component | What it does | Key technology |
|---|---|---|
| Text source | Provides the words/passage for the test | Word list array, passage database, or API |
| Input handler | Captures keystrokes and compares against expected text | JavaScript keydown events |
| Timer | Tracks elapsed time with sub-millisecond precision | performance.now() |
| Scorer | Calculates WPM, accuracy, and error counts | Formula engine (client + server) |
| Results display | Shows final score and per-second speed graph | DOM manipulation or charting library |
The Input Handler: The Hardest Part
The input handler is where most of the complexity lives. You need to track:
- The current position in the text
- Whether each typed character is correct or incorrect
- What happens when the user presses backspace (move position back, mark character as untyped)
- Whether the user can advance past an error (some tests block this, others allow it)
- Paste events (should be blocked in a fair test)
The cleanest data structure is a character state array: one entry per character in the text, with a state of "untyped", "correct", or "incorrect". The current cursor position is an index into this array. Every keydown event either advances the index (on any character key) or decrements it (on backspace).
Timing with performance.now()
The browser's performance.now() API returns the number of milliseconds elapsed since the page loaded, with sub-millisecond resolution. This is the right tool for timing a typing test because:
- It doesn't drift like
Date.now() - It's monotonically increasing (never goes backward)
- It has ~0.1ms resolution on most browsers
Record a start timestamp when the first character is typed, and an end timestamp when the test completes. Elapsed time = (endTime - startTime) / 1000 / 60 in minutes.
The WPM Formula
The standard formula for WPM: WPM = (characters_typed / 5) / elapsed_minutes
For net WPM: net_WPM = gross_WPM - (uncorrected_errors / elapsed_minutes)
One "word" = 5 characters (including spaces). This standardises across different word lengths so long words don't penalise speed unfairly.
Anti-Cheat Architecture
Client-side scores can be manipulated. Anyone can open developer tools and send a fake score submission. A production typing test with a leaderboard needs server-side validation. The most reliable method is sending the full keystroke timestamp array to the server with the score, then validating:
- Does the timing data produce the claimed WPM?
- Are inter-keystroke intervals within physically plausible ranges? (Under 20ms per key is suspicious)
- Is the timing pattern consistent with a human? (Bots often have uniform inter-keystroke timing)
- Do the keystrokes match the expected characters for the claimed accuracy?
This is the same approach TypingTest.now uses for its leaderboard validation. For more detail, see understanding the logic behind typing speed algorithms.
Building the Results Display
The minimum results display shows WPM, accuracy percentage, and error count. A better display adds a per-second WPM graph (plot the rolling WPM at each second of the test). This graph reveals consistency — a flat line is better than a high peak with a long valley.
For a reference implementation to compare against, take the 1-minute test and inspect the results page. The architecture described here is what powers it. For the Python implementation version, see coding a typing speed test in Python.
Ready to put it into practice?
Take a free typing test and start tracking your progress.
Free Typing Test →