A Morse code trainer written in Dart / Flutter, with support for a USB iambic
Morse key (USB id 413d:2107).
Pre-built desktop applications are published on the Releases page. The links below always point at the latest release:
| Platform | Download |
|---|---|
| Windows (x64) | Morsey-windows-x64.zip |
| macOS | Morsey-macos.dmg |
| Linux (x64) | .AppImage · .deb · .tar.gz |
| Linux (arm64) | .AppImage · .deb · .tar.gz |
On Linux, audio uses pacat from pulseaudio-utils; install it if it is not
already present. USB-key access needs read permission on the key's
/dev/hidraw* node (see Permissions).
The main window has a left column listing the parts of the program; click one to open it:
- About — a description of the program and how it works.
- Settings — choose the keyer mode (iambic paddles, or a straight key where the press length makes the dits and dahs), the paddle orientation, the keying speed (WPM), the side-tone volume and frequency, the training character set, the appearance (follow system, light, or dark), and the language. There is no input-device selection: the USB key and the keyboard paddles are both always active, and the key can be plugged in (or unplugged) at any time.
- Input Train — a character is shown; you key it in Morse and the trainer decodes what you send and tells you whether it was correct.
- Listen Train — the trainer plays a character as Morse audio; you type the character you heard.
- Listen Tutorial — a guided, 26-level listening course. Each level introduces one new letter (Koch-method order, easiest-to-distinguish sounds first): the letter is shown and its Morse is played, you type it to begin, then a random drill of every letter unlocked so far runs until each has been answered correctly three times. Completing a level unlocks the next; progress is remembered, and a dropdown lets you revisit any unlocked level.
- Input Tutorial — the same 26-level course with the roles reversed, to teach sending. Each level shows the new letter's dots and dashes with the letter beside them; key the pattern to begin. In practice the pattern is taken away — only the letter is shown — and you key its Morse from memory (USB paddle or keyboard, exactly as in Input Train) with a live display of what you are keying. A Hear it button plays the target's rhythm (it is disabled while you are keying, since the keyer owns the side-tone), and a per-letter hint reveals the pattern if you get stuck. Its progress is tracked separately from the Listen Tutorial — hearing a letter and keying it are different skills.
- Free Type — type any text in the top box and watch it rendered as Morse in the box below. The dots and dashes appear element by element at the keying speed set in Settings, and a toggle at the top plays the Morse audio in sync with the display.
- Free Key — the reverse: key Morse with the paddle or keyboard and watch it decoded into text. The dots and dashes appear in the Morse box as you key; each completed character is decoded into the text box, and pausing seven dits inserts a word space. The same toggle mutes the keying side-tone.
- Timing — shows a line from a public-domain book (1000 bundled lines from Dickens, Austen, Melville, Franklin, Adam Smith and others) with its Morse; key it and every pulse and gap is timed against the expected sequence. Alignment is per character using the keyer's decode: a miskeyed letter shows what you sent, discards that attempt's timings, and waits for a re-key (or resyncs if you skipped a letter) — one slip never poisons the rest of the line. On completion you get a histogram and consistency score (100 − coefficient of variation) per category — dits, dahs, letter gaps and word gaps with a straight key, or the gaps alone in iambic mode — plus your actual sending speed in WPM (nominal PARIS units ÷ elapsed time). Restart picks a new line.
The iambic keyer that I have used is the Putikeeg MCT II.
It is listed on Amazon as the "CW Key Morse Code Key Morse Telegraph Key Morse Code Key Aluminum Alloy CW Square-Shaped Base Red (Grey)"
This provides a 3.5mm jack interface. To connect to a computer you need a USB interface. The one I use is listed on Amazon as the "Morse Code Training Adapter USB Key Trainer for/Key Mobile Computer Support 3.5mmPlug Key Trainer Connectors"
The key enumerates as a standard USB HID keyboard with the identifier 413d:2107. On Linux it is read directly
from its /dev/hidrawN node — no drivers needed. Each paddle is reported in the
HID modifier byte:
- Left-Ctrl bit (
0x01) → one paddle - Right-Ctrl bit (
0x10) → the other paddle
The device sends raw paddle up/down state; a software iambic keyer turns those presses into correctly-timed dits and dahs (squeeze both paddles for alternating elements) and decodes them into characters. Which paddle is dit vs dah is set in Settings ("Paddle orientation").
On MacOs:
When first run, you should be prompted to open the input monitoring section in the settings. Grant input monitoring to Morsey then close and reopen Morsey.
On linux:
You need read access to the key's /dev/hidraw* node. On this machine the node
is owned by group plugdev with an ACL, and the user is in plugdev, so it
works out of the box. If not, add a udev rule such as:
# /etc/udev/rules.d/99-morse-key.rules
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="413d", ATTRS{idProduct}=="2107", MODE="0660", GROUP="plugdev"
then sudo udevadm control --reload && sudo udevadm trigger.
The side-tone is a synthesised sine wave with a per-platform backend, all
speaking the same morsey/tone_engine contract: an AVAudioSourceNode on
macOS and iOS, WASAPI on Windows, an AudioTrack render thread on Android,
and — on Linux — PCM synthesised in Dart and streamed to PulseAudio/PipeWire
via pacat (from pulseaudio-utils). No gstreamer or extra Flutter audio
plugin is required. If no audio backend is found the app still runs (Input
Train works silently; Listen Train needs sound).
Nothing to configure — the keyboard paddles are always active alongside the
USB key. The Left-Arrow key is the dit paddle and the Right-Arrow key is the
dah paddle (./- and Left/Right-Ctrl also work). Click the Input Train area
first so it has keyboard focus. If you plug the USB key in mid-session it
connects automatically.
The UI is internationalized with Flutter's gen-l10n. Translations live in
lib/l10n/app_<locale>.arb; currently English (en), Welsh (cy), German
(de), Spanish (es), French (fr), Hindi (hi), Japanese (ja), Chinese
(zh), and Klingon (tlh). The language can be set in Settings ("Follow
system" uses the platform locale, falling back to English). To add a
language, copy app_en.arb, translate the values, add the locale to
supportedLocales and AppLanguage, and rebuild.
flutter run -d linux
flutter build linux
flutter test
Covers the Morse table, settings/timing, the app shell, and the iambic keyer/decoder (driven by a simulated paddle).
lib/
main.dart app entry + main window / left nav column
app_scope.dart shared Settings + AudioEngine (InheritedWidget)
models/settings.dart settings model (input, speed, tone, char set)
morsey/morse_code.dart Morse alphabet + character sets
morsey/iambic_keyer.dart software iambic keyer + live decoder
audio/audio_engine.dart Dart tone synth streamed via pacat
input/paddle_source.dart paddle source abstraction
input/hid_paddle_source.dart reads the USB key (hidraw / IOKit), hotplugs
input/keyboard_paddle_source.dart keyboard keys as dit/dah paddles
input/combined_paddle_source.dart keyboard + USB key active together
screens/ About / Settings / Input Train / Listen Train /
Listen Tutorial / Input Tutorial

