NATO Alphabet Coach

Replit Agent built a working app in one hour. I spent the next nine days fixing it.
The idea
The NATO phonetic alphabet. Alpha, Bravo, Charlie. You think you know it until someone asks you to spell “Zurich” over a radio and your brain goes blank at Z. I wanted a small drill app with spaced repetition, voice input, multiple languages, themes, a converter. Not a startup. Just a useful tool. You can try it yourself. I opened Replit, described what I wanted, and let the Agent build.
The magic hour
Replit Agent took the full brief and scaffolded the entire application in a single session. 77 files. North of 16,000 lines. React, TypeScript, Tailwind, shadcn components. Everything I asked for, wired up and running. First deploy: same day.

The most impressive piece was the spaced repetition algorithm:
export function selectLetterForReview(
userProgress: UserProgress[]
): string {
const now = new Date();
// Priority 1: letters past their review date
const needReview = userProgress.filter(
(p) => new Date(p.nextReview) <= now
);
if (needReview.length > 0) {
// Higher difficulty, older reviews first
needReview.sort((a, b) => {
const diff = b.difficulty - a.difficulty;
if (Math.abs(diff) > 0.5) return diff;
return new Date(a.lastReviewed).getTime()
- new Date(b.lastReviewed).getTime();
});
return needReview[0].letter;
}
// Priority 2: letters below 70% accuracy
const poor = userProgress.filter((p) => {
const total = p.correctCount + p.incorrectCount;
return total > 0 && p.correctCount / total < 0.7;
});
if (poor.length > 0) { /* worst accuracy first */ }
// Priority 3: unpracticed letters (random)
// Fallback: least-attempted letter
}
The Agent nailed this. Exponential backoff, a 70% accuracy threshold, difficulty that creeps up on success and nudges down on failure. Better than what I would have written by hand.
And then the first hour ended.
What I had to fix
The theme system spiral
I asked for multiple themes, and the Agent delivered five. Getting them built was the easy part. Getting them to look right was something else entirely.
Text was unreadable in dark mode. Invisible against the rainbow gradient. Wrong contrast on the NATO theme. The Agent produced eight consecutive commits trying to fix visibility. Eight commits. Same problem. Each fix broke a different theme.
Then an attempted Tailwind 4 migration touched 49 files and broke everything. The rollback was immediate, 1,377 lines changed back. Building a feature and sustaining it through change are two different skills, and the Agent had only the first.
Speech recognition: “fix mike hopefully”
Voice input was part of the original brief. The Agent delivered it on day two. It broke immediately.
What started as a voice input button touching four files turned into a 19-file odyssey. Platform detection, browser-specific error handlers, audio monitoring, recognition event factories. Six major rewrites over six days. The commit messages tell the story:
“Try to fix speech recognition”
“attempting to fix microphone”
“fix mike hopefully”
“Try to fix speech on android”
“try to fix ios speech”
Read those again. Watch the capitalization disappear. Watch “fix” go from a confident verb to a plea.
// What voice input started as: 4 files, one hook
export function useSpeechRecognition(config) {
// ~80 lines of straightforward Web Speech API
}
// What it became: 19 files across two directories
import { getAndroidInfo, getBrowserInfo, getIOSInfo,
isSpeechRecognitionSupported } from './speech/platform-detection';
import { handleAndroidError,
handleIOSError } from './speech/platform-error-handlers';
import { createRecognitionHandlers } from './speech/recognition-handlers';
import { initializeSpeechRecognition,
testMicrophoneAccess } from './speech/speech-recognition-utils';
import { stopAudioMonitoring } from './speech/audio-monitoring';
Deployment friction
Replit generates dynamic development URLs. Vite rejects unknown hosts. Five config attempts later, the Agent landed on allowedHosts: 'all'. The notification system called a backend API that didn’t exist and had to be removed entirely. A dozen commits, zero user-visible features.
What surprised me
The RTL support. I asked for multiple languages including Arabic, and the Agent inferred that Arabic needs right-to-left layout. The wrapper detects the language, flips the document direction, the UI mirrors correctly. Shipped clean on the first pass. When the problem is well-defined and the browser APIs cooperate, the Agent excels.

The git history became its own artifact. The Agent’s commits are verbose: “Enhance user experience with voice control, hints, and compact display.” The manual developer commits are terse: “fix mike hopefully.” You can see the exact moment the human had to take over.
The lesson
There is a trap in vibe coding, and it’s shaped like Pareto’s principle. The first 80% is dazzling. The remaining 20% is cross-browser APIs, deployment configuration, visual consistency across states you didn’t test.
The Agent builds the house. You make the plumbing work.
The Web Speech API across iOS Safari, Android Chrome, and desktop browsers has no clean, copyable solution. You cannot Google a great design and a sound implementation. You have to build one, through trial and error, on real devices, with real microphones. That kind of work is exactly what the 80/20 gap is made of. The NATO Alphabet Coach works. People use it to drill their Alphas and Bravos. It took a developer to get it there.