Morse Code Timing: The Hidden Math Behind Dots, Dashes, and the Farnsworth Method
If you've ever tried to build a Morse code feature — a translator widget, a practice app, a hardware keyer — you've discovered that the "code" part is trivial. It's a lookup table. The hard part is the timing. Get the timing wrong and your decoder produces garbage, your audio sounds drunk, and your users quietly leave.
This post is the timing reference I wish I'd had: the exact ratios, the WPM math, and the Farnsworth method that makes learning actually work.
The five timing rules
International Morse code timing is defined by the ITU, and it all derives from one unit: the dit (dot) duration. Everything else is a multiple of it:
- Dit = 1 unit
- Dah (dash) = 3 units
- Gap between dits/dahs within a character = 1 unit
- Gap between characters = 3 units
- Gap between words = 7 units
That's it. Five rules, one unit. But the consequences are surprisingly deep.
Take the letter A (.-). Its total duration is: 1 (dit) + 1 (intra-character gap) + 3 (dah) = 5 units. The word SOS (... --- ...) is: (1+1+1+1+1) + 3 + (3+1+3+1+3) + 3 + (1+1+1+1+1) = 27 units. Once you internalize the unit system, you can compute the duration of any message before a single beep plays.
The WPM formula (and why PARIS matters)
"Words per minute" in Morse doesn't mean English words — it means PARIS, the standard reference word. PARIS in Morse is .--. .- .-. .. ..., and if you count the units (including the trailing word gap), it comes to exactly 50 units.
So the math is clean: dit duration (seconds) = 1.2 / WPM.
At 20 WPM, one dit = 60ms. A dah = 180ms. A word gap = 420ms. Reference: 5 WPM → dit 240ms; 12 WPM → dit 100ms; 20 WPM → dit 60ms; 30 WPM → dit 40ms.
If you're building a player, schedule everything up front against audioContext.currentTime — never use setTimeout chains for the gaps, or your timing will drift within seconds.
Why decoders fail: the tolerance problem
Real-world Morse never hits exact ratios. A naive decoder with fixed thresholds breaks immediately. The robust approach is adaptive thresholding: measure the distribution of observed element durations, then split at the natural valley. Set the dit/dah boundary at the geometric mean of the two cluster centers, not the arithmetic mean — durations are log-normally distributed, so the geometric mean splits them far more reliably.
The Farnsworth method: timing as a learning hack
Beginners learn at 5 WPM, but 5 WPM Morse sounds nothing like 20 WPM Morse — so learners have to relearn every character when they speed up. The Farnsworth method fixes this: send the characters themselves at full speed, but stretch the gaps between characters and words. You learn the true sound of each character from day one; only the spacing is generous.
If you're building a learning tool, Farnsworth spacing isn't a nice-to-have — it's the difference between users who plateau at 8 WPM and users who reach 20. Any online Morse code translator with a trainer mode should expose it as a setting.
Putting it together: a minimal timing engine
The skeleton of a correct timing engine: UNIT = 1.2 / WPM; DAH = 3*UNIT; gaps of 1/3/7 units between symbols/characters/words. Add gaps after elements — the most common DIY bug is mishandling the character gap.
The takeaway
Morse code looks like a 19th-century curiosity, but its timing system is a beautifully minimal protocol: one unit, five rules, and from those you derive transmission speed, decoder thresholds, and even a pedagogy method. Whether you're building a translator, a trainer, or a decoder, get the timing right first — everything else is a lookup table.
The author builds free Morse code tools at Morse Zone — translator, trainer, and reference charts, no signup needed.




