The obvious way to build pronunciation feedback is to record the learner, ship the audio to a speech-assessment API, and pay per minute for a score. I shipped the feature without any of that. Every annotated German sentence in my translation tutor now has a practice button: the browser listens while you say the sentence, and you get each word colored matched, close, or missed, plus a percentage.
The trick is admitting what the feature actually needs. I don't need phoneme-level acoustics. I need to tell a learner which word they dropped, mangled, or nailed — and a speech recognizer plus a diff already does that:
The browser recognizes. The Web Speech API transcribes the attempt in German mode. Chrome routes that audio through Google's servers, Safari does it on-device — both are disclosed inline, and neither involves my server or my wallet.
A diff algorithm grades. The expected sentence and the transcript are both normalized (lowercase, punctuation stripped, umlauts folded so "schoen" equals "schön") and aligned with Needleman–Wunsch, the same dynamic-programming alignment biologists use for gene sequences, with per-word Levenshtein similarity as the substitution cost. A word aligns cleanly: matched. It aligns to something close ("Hunt" for "Hund"): near miss — which is usually the recognizer hearing a genuine pronunciation problem. It aligns to nothing: missed. Fillers the learner adds don't cost points.
The score is honest about its ceiling. STT normalizes away some of what a phonetics coach would catch; that's the trade for €0.00. The grader catches dropped articles, wrong word order, and the words the recognizer couldn't parse into what you meant — which, for a learner drilling sentences, is most of the feedback that matters.
Everything is deterministic and client-side, so the tests are plain unit tests, the feature works offline-ish, and the privacy story is one sentence long. The only thing my analytics ever sees is a four-value score bucket.
The transferable lesson: "we need an AI grader" often decomposes into "we need a recognizer we already have, plus an alignment algorithm from 1970". Spend the model budget where only a model will do — the grading loop here needed exactly zero tokens.