Hablas
View source on GitHub →
Result
Per-skill CEFR tracking gives an honest picture of real ability, and pre-generated audio makes every spoken line sound the same on every phone without loading the server.
The challenge
Most language apps are built to keep you tapping, not to make you fluent. I wanted to learn Spanish and then Portuguese the way the research suggests: material paced to the CEFR levels, plenty of comprehensible input, and spaced repetition tuned to how memory really decays, without a streak counter nudging me towards whatever keeps the app open.
What I built
- A Laravel, Inertia and Vue app with an FSRS-based spaced repetition engine. Spanish (Spain) and Portuguese (Portugal) decks are kept strictly apart and never mix in one review session.
- CEFR level tracked separately for reading, listening, speaking and writing. The headline level is the lowest of the four, not an average, so it cannot flatter me.
- A lesson engine with units, listening and speaking exercises, several exercise formats and a retake rule that sends you back to the weak spot instead of letting you scroll past it.
- Natural-sounding speech, generated ahead of time. Every spoken line is synthesised once with an open text-to-speech model, stored as a small audio file and served from the web server, with normal and slow playback and a choice of voices. Nothing is generated while you wait, and the audio sounds the same on every phone.
- Grammar mistakes tagged by kind (wrong gender, ser against estar, false friends, wrong tense) so review can target what actually goes wrong.
- An installable web app that works well on a phone, and a credits page for the voices and the licence of the model behind them.
Key decisions
- The weakest skill sets the level. A strong reader who cannot yet speak should not see an inflated badge.
- Pre-generated audio over live synthesis. It keeps the shared server light, makes every clip identical everywhere, and falls back to the browser voice where a clip is missing.
- Content is reviewed before it ships. New units stay hidden until a native speaker has checked them, and a test fails if Portuguese content drifts into Brazilian forms.
Interested in this kind of work?