Ditty · working prototype

Four screens, and every one of them runs.

The direction is settled: Tape and Lamp are the two media inside Key, Rhythm is what Quests looks like, and Explore is the alphabet you can play. Tap the tabs — it's a real app, not a picture of one. On a computer you can type on your actual keyboard in Explore.

Ditty Zoey's Lab 12 WPM
tapedit 1 · dah 3
tap the key to begin

tap = dit · hold = dah · pause = new letter

tap a letter to hear it · every key wears its own code

R Send the letter R 0% match · di-dah-dit ☆☆☆
target
yours

match the rhythm — length matters, not speed

Letter speed 12 WPM

How quickly each letter is sent. Keep this near real speed so the rhythm you learn is the right one.

Thinking time 6 WPM

Extra silence between letters. Slide it left for longer pauses — the letters stay crisp, you just get more room to think.

Pitch 620 Hz

Some ears find a lower tone easier to listen to for a long time.

Play mode uses these · Studio adds dit length in ms and Farnsworth ratio

Key

Two media, one timing engine.

Tape and Lamp sit behind a segmented toggle — the same control as Huely's Ink/Light, over the same medium-aware state. Tape draws marks whose width is their duration: dit one unit, dah three. Lamp is the dark-room sibling, where the screen goes quiet so the ear can lead.

you key it — the app reads you

Explore

The alphabet, playable.

Every key wears its own code underneath, so the whole alphabet is visible at once. Tap one to hear it and add it to the strip; type your name and press play. This absorbs what a Learn tab would have been — the reference is the toy, so nothing has to be a chart.

you type it — the app sends it

Quests

Two lanes, one grade.

Target rhythm above, yours below, in the same width-is-duration language. It's Huely's quest banner with the swatch swapped for a tune — same live match percentage, same 90 / 95 / 99 → ★★★, same confetti, and it matches how the Koch method actually teaches.

Settings

Slow the silence, not the letters.

You were right that standard Morse is too fast for a child — but the fix isn't one speed slider. Slowing the letters teaches a shape that has to be unlearned later, because a slow "S" doesn't sound like a fast one. So there are two controls: letters stay near real speed, and thinking time stretches the silence between them.

That's the Farnsworth method, and it's what every serious Morse course uses. Move the sliders and key something — the app responds live.

letters at 12 · spacing at 6 = gentle but honest

Still to decide

Four tabs, and one thing I merged.

Explore does the job a Learn tab would have — the alphabet reference became the playground instead of a chart — so the app lands on four tabs that each earn their place. If you want the culture and history (why SOS isn't “save our souls”, CQD on the Titanic, "What hath God wrought"), that's a scrolling section underneath the keyboard rather than a fifth tab.

Engine

One timing spec, three renderers

lib/morse.ts owns units, WPM, Farnsworth spacing, the table and the decoder. Tape, Lamp and the Quest lanes are pure presentation over the same element list — exactly how Beaker and LightStage share one drop curve.

Play / Studio

Forgiving, then exact

Play shows the two friendly sliders above. Studio exposes dit length in milliseconds, the Farnsworth ratio, and sidetone shaping — same duality as Huely, no new architecture.

Saving

Where the jar went

Nothing saves yet. When it should, it belongs in Explore as a small “my words” shelf — reusing JarContext and the Apple backup — rather than a tab of its own.

From HuelyCarries over as
TabBar, TopBar, ToggleSwitchVerbatim, once TabBar takes a tabs array instead of hardcoded SVGs
Ink / Light medium toggle + pinned providerTape / Lamp, unchanged
QuestContext, starsForMatch, per-pack laddersUnchanged — only “what counts as a match” differs
QuestBanner / QuestPlayScreenThe rhythm lanes, target renderer swapped
sound.ts audio() + silent-buffer warm-upThe sidetone — same iOS latency hardening
useDropInteraction's pointer hardeningNew duration-based hook, same hard-won iPad fixes
functions/** Apple auth + KV syncVerbatim; new App ID, Services ID, KV namespace, secret
i18n provider + reachability testsDay one — a letter/prosign library invites the same dead-key bug