Departed Spring 2024
MorseCode-Learner
the wrist, eyes-free · WatchOS app · Archived
[image: Apple Watch on a white screen displaying 'MORSE CODE' in bold red, with red haptic-wave arcs radiating outward from the watch to indicate buzzing on the wrist]
Departure
Wanted to learn Morse for a future project, but every trainer I found was a screen with audio — useless in class, rude in a quiet room. The wrist already taps me for notifications; that channel was sitting unused as a teaching surface. Built a WatchOS app that buzzes a word into the skin and then asks me to tap it back. Eyes-free, silent, one-handed — I could practice in a lecture or on a walk without pulling out a phone or making a sound.
Approach
- Swift
- WatchOS
- WatchKit
- Core Haptics
WKInterfaceDevice.play() ships with a small vocabulary — .click, .success, .notification — and none of them is 'a dash three times longer than a dot.' Whatever the dash was, I had to build it out of the dot.
Field log
Spring 2024 — a silent skill
Wanted Morse for a future project. Every trainer I tried was a screen and a speaker — useless in class, rude in a quiet room. The wrist already taps for notifications, and nothing was using that channel to teach.
Picking the dot
Cycled every WKInterfaceDevice.play() case looking for one that read as a Morse pulse on the skin. .click won — short, dry, unmistakable. Everything else either rang too long or sounded like a system alert.
Faking the dash
A real dash is three dots long, but the Taptic Engine has no native concept of duration. So playTripleDashHapticImmediate fires three .clicks back-to-back, tight enough that the wrist reads them as one longer pulse. Not elegant. The dot/dash line became unmissable.
[image: Two stacked timelines on a wrist — top: one short .click pulse labeled DOT; bottom: three .click pulses fired immediately back-to-back labeled DASH (playTripleDashHapticImmediate), since the Taptic Engine has no native duration]
Dot is one click. Dash is three clicks pretending to be one. Tapping back
Input is the inverse problem — was that a dot or a dash, and was that gap a letter break or a word break? Built an adaptive timer that watched the last few taps and slid its dot/dash threshold around the user's actual rhythm. Slow tappers and fast tappers both got read correctly.
The loop
App feeds a word, taps it on the wrist, user taps it back. That's the whole interface.
[image: Circular four-step diagram of the practice loop — (1) app picks a word, (2) watch buzzes it on the wrist as dots and dashes, (3) user taps it back on the watch face, (4) app scores and picks the next word]
Feed → buzz → tap-back → score. No screen reading required. Tap-back-from-memory
The intended loop was feel-the-word, tap-it-back. The loop that actually taught me was: feel the word, walk to the next class, then tap it back from memory thinking about the letters, not the buzzes. The delay forced the translation through language instead of muscle.
Inventory
Most of the alphabet now lives in my wrist. X and G still don't — they don't come up enough in the word list to drill in. Not enough to chat with a telegraph operator, but more Morse than anyone I know.
[image: Morse alphabet chart laid out as a 5x6 grid of letters with their dot-dash patterns; most cells are filled in solid, with X (-..-) and G (--.) faded out and marked 'still missing']
Stuck on X and G. Everything else is in the wrist.
From the gallery
[image: Apple Watch on a wrist mid-drill — bright colored screen flashing a single capital letter in the center, prompting the user to tap it back in Morse on the next screen]
[image: Adaptive-timing chart — horizontal axis is tap duration in milliseconds, with the dot/dash threshold drawn as a vertical line that slides left or right based on the user's last few taps]
What I came back with
Most of the alphabet — still missing X and G
Lesson from the terrain
The Taptic Engine isn't a speaker — it has maybe four distinguishable voices and no native concept of duration, which means anything expressive on it has to be assembled out of the small set of pulses it'll give you. That constraint turned out to be fine for a two-symbol language. The bigger lesson was about the channel itself: a haptic loop the user can run without looking is a teaching surface most apps leave on the table.
Cross-links
This fed into / from