A mobile-first progressive web app for learning to read Persian through adaptive multiple-choice practice.
The public landing page explains how Ravân complements other Farsi-learning methods. The
installable practice app lives separately at /app/.
- Builds from short, frequent letter patterns toward longer everyday words
- Adds a sixth level of short phrases, including word-boundary recognition and optional ezafe help
- Uses spaced repetition: misses return quickly; successful words wait longer
- Introduces English-meaning questions only after that word has first been matched to its transliteration correctly; a missed meaning relocks the word until its pronunciation is answered correctly again
- Teaches recurring visual chunks such as
میـ,نمیـ, pluralـها, and common verb endings through scored questions—first in isolation, then highlighted inside real words - Tracks accuracy, answer speed, daily streaks, and per-word progress locally
- Introduces the method through a focused first-run onboarding
- Installs to a phone Home Screen with dedicated app icons and full-screen presentation
- Opens a pre-filled recurring reminder in Google Calendar, with a calendar-file fallback
- Uses a short wrong-answer haptic and a longer level-unlock haptic on supported Android devices
- Includes an About and Settings area for installation, reminders, sharing, vowel marks, haptics, privacy, and replaying onboarding
- Works offline after the first visit
- Uses privacy-conscious, aggregate Umami events for product improvement; answer content, reminder times, learning progress, and personal identifiers are never sent
The intended first native release keeps the existing React learning experience and adds optional scheduled notifications—configured by time of day and an every-X-days cadence—plus simple haptics. Audio, payments, cloud sync, widgets, and deeper native integrations are intentionally deferred.
The starter set lives at src/data/words.json. Each entry has a stable id, Persian spelling, formal transliteration, English meaning, difficulty level, frequency rank, and letter list. An optional spokenTransliteration records a common colloquial Iranian pronunciation when it differs from the formal reading. The current 121-word set includes every Persian letter in at least two quiz words. Optional pedagogical vowel forms are keyed by the same IDs in src/data/vowels.json; words without an entry safely fall back to their standard spelling. Replace or extend these files to scale toward 500+ words.
Short multi-word reading items live separately in src/data/phrases.json. Phrase entries add
explicit word segments and optional phrase-specific reading help for ezafe and other potentially
confusing pronunciation details.
Open /app/?debug=1 to use isolated test progress with every level unlocked. Add a level number to
open directly at that level, for example /app/?debug=1&level=6. Test-mode activity is stored
separately from normal progress. To preview a level-unlock celebration, add unlock=4 (or another
level number), for example /app/?debug=1&level=3&unlock=4. Use bud=6 for the latest-level Bud
screen, complete=6 for its Bouquet screen, or allComplete=1 for the all-levels mastery screen.
Reaching Bud naturally in test mode also shows the appropriate celebration even though every level
is already available.
Test-mode activity is excluded from explicit analytics events.
npm install
npm run devThe workflow in .github/workflows/deploy-pages.yml builds and deploys every push to main.
- On GitHub, open Settings → Pages.
- Under Build and deployment, set Source to GitHub Actions.
- Push to
main, then follow the deployment in the Actions tab.
The landing page will be available at https://baldertencate.github.io/ravan/, with the
practice app at https://baldertencate.github.io/ravan/app/.
Production builds send Umami a small set of explicit events such as practice starts, one aggregate
summary per ten answers, installs, reminders, and level changes. Individual correct and incorrect
answers are not sent. Events contain only coarse counts and context; the tracker is restricted to
baldertencate.github.io, so local development is not recorded.