The method

How Fastfingaz measures typing.

TWO CLOCKS / AN EXPLICIT IDLE MODEL / AN HONEST SIGNAL SCORE / v0.4.0

The short version: standard WPM measures transcription. Fastfingaz measures composition. That requires two clocks, an explicit idle model, and an honest signal score.

Two clocks

Every sent prompt produces one session with two WPM numbers.

Think WPM — finished thought

thinkWpm = (finalCharacters / 5) / (wallMs / 60000)
  • finalCharacters is the length of the text that was actually sent.
  • wallMs runs from the first editing keystroke in an empty composer to the send action.
  • This includes every pause, every re-read, every “what am I actually trying to say”.

This is the number that does not exist anywhere else. It is slower than your hands, by design.

Flow WPM — hands-only cadence

flowWpm = (insertedCharacters / 5) / (activeMs / 60000)
  • insertedCharacters counts every character inserted, including ones later deleted.
  • activeMs is wall time with long gaps clamped (see below).
  • This is close to what a typing test measures, except the text is yours.

Five characters per word is the conventional definition used by Monkeytype and most typing tools. Spaces count. A separate word count is shown for humans, but all WPM math uses the five-character word so the numbers are comparable across tools.

Live WPM — the big number

The big number while typing is a live flow reading, not think. It is the last eight seconds of actual typing; a gap longer than the thinking threshold is skipped rather than wiping the window, while the session flow score still charges the clamped gap. Before the run is eight seconds old, the denominator is the typing span it actually used, so a short burst is not diluted by seconds that never happened. Manual pauses are removed entirely. The reading updates only when a key lands, so it holds through a pause instead of bleeding down with the clock.

The big number at idle is LAST THINK: the last sent prompt’s think WPM, including every pause. It is not your typing speed.

Why 130 on Monkeytype can be 100 here

Monkeytype gives you the words and a short timer, so its WPM is sustained transcription. Fastfingaz gives you a blank composer. Flow is the fair comparison, peak is the closest thing to a sprint, and think is deliberately lower because it includes the thinking. A 130 WPM typing test and a 100 WPM flow score are both true: one is transcription, the other is composition.

Active time and the thinking threshold

A naive “time between first and last key” makes a thoughtful writer look slow. Ignoring pauses entirely makes a distracted session look fast. Fastfingaz clamps each gap instead:

for every gap between editing keys (and the final gap to send):
  activeMs += min(gap, idleThresholdMs)

Default threshold: 2000 ms. A 30-second pause contributes 2 seconds of active time. You can set it from 1 to 5 seconds in settings.

Why clamp rather than drop? Dropping makes a session with many pauses artificially fast, because the pauses vanish from the denominator. Clamping treats a pause as a short reset — you still pay for it, but it does not swallow the whole session.

The threshold is also used to define “thinking” for the live HUD. If no key has arrived for max(1800ms, threshold), the HUD switches to THINKING. The live number is held, not reset to zero; a pause is not a failed run.

The HUD also has an explicit pause button and the Alt+Shift+P shortcut. A manual pause freezes the wall and active clocks and shows PAUSED; the paused duration is excluded from both. Typing resumes automatically. This is a declared break, not a reset or a receipt.

Peak and consistency

Peak is the best 5-second window, measured against the real span of that window:

peak = max over i of (keys in [t_i, t_i + 5000] / 5) / (actualSpan / 60000)

A window needs at least 1.5 seconds of real span and 5 keys to count, so a 2-key session cannot produce a fake 300 WPM peak. Peak is a “what were you capable of for a moment” number, not an average.

Consistency is the coefficient of variation of inter-key intervals inside the idle threshold:

intervals = [t_i - t_{i-1}] where 0 < interval <= idleThreshold
cv        = stdev(intervals) / mean(intervals)
consistency = clamp(100 * (1 - cv), 0, 100)

This is deliberately the Monkeytype definition. A perfectly even rhythm scores 100. Real prose scores lower than a typing test because spaces, punctuation and capital letters change interval lengths. Treat it as a relative signal across your own sessions, not an absolute grade.

Correction rate

correctionRate = deletedCharacters / (insertedCharacters + deletedCharacters) * 100

There is no target text, so accuracy cannot be measured. Correction rate is the honest proxy: how much of your editing was spent removing things. Backspacing a whole clause is a normal part of thinking in text; it is not “wrong”.

Signal quality

The percentage on a receipt and card answers: how much of this final text can we attribute to keys we actually observed?

netTyped = max(0, inserted - deleted - pasted)
base     = clamp(netTyped / finalCharacters, 0, 1)

if pasted > 0:        base *= 0.55
if ime:               base *= 0.75
if undoCount > 0:     base *= 0.92
if replacements > 0:  base *= 0.97

signal = clamp(base, 0.05, 1)

A pasted draft, a heavily autocorrected message, or an IME-composed message is not hidden. But a session needs at least 50% signal to be recorded at all, and paste-dominated runs are rejected before they reach history.

Eligibility

Only sent prompts are measured. Drafts and abandoned runs are never written to history. A run must clear all of these gates:

GateRule
Sent promptComposer cleared after Enter or the send button.
Minimum wall time3 seconds.
Minimum charactersYour setting, with a hard floor of 16.
SignalAt least 50%.
PasteLess than 50% of the final text.
Resumed draftRecorded in the draft bucket; excluded from best and averages.
IMECounts toward averages above 50% signal; excluded from best below 85%.

A personal best is only possible for a best-eligible run that beats the old value. A lower run can never overwrite a higher best.

Aggregation

Averages are weighted by the underlying time and characters, not by taking a mean of per-session WPMs:

averageThinkWpm = sum(finalChars) / 5 / (sum(wallMs) / 60000)
averageFlowWpm  = sum(insertedChars) / 5 / (sum(activeMs) / 60000)

The idle HUD shows the last eligible run, the best think WPM, and a rolling average of the last 20 eligible sessions. The popup also stores an all-time average over the locally retained history (up to 500 sessions). Drafts, paste-dominated runs and low-signal runs are excluded.

What is deliberately not measured

  • Accuracy vs a target. There is no target. Correction rate is the substitute.
  • Thinking before the first keystroke. The wall clock starts at the first key, not when the page loaded. Think of it as “how fast does a thought become text once you start writing it”.
  • Speech, dictation, AI autocomplete. Not typing; paste and dictation paths lower signal.
  • Absolute skill. These numbers are most useful against your own baseline, on your own topics.

Validation plan

  1. Baseline against a known test. Type a 60-second Monkeytype run and a Fastfingaz prompt of similar length. Flow WPM should land in the same neighbourhood; a large gap means the idle model or character accounting needs work.
  2. Threshold sweep. Run the same three prompts at 1s, 2s and 3s thresholds. Pick the threshold that best matches when you feel you stopped typing versus stopped thinking.
  3. Repeated prompts. Write the same explanation three times on different days. The spread is your noise floor; any “improvement” smaller than that is not real.
  4. Paste and autocorrect probes. Confirm that a pasted prompt and an autocorrected prompt both report low signal, and that a normal prompt reports high signal.
  5. UI drift probes. ChatGPT ships composer changes regularly. The selector fallbacks should keep attaching; add a case whenever a new composer shape appears.
This is a measurement instrument, and instruments need calibration. Treat the first week as a baseline, not a scoreboard.