A local, offline German pronunciation coach
0
stars
18
commits
Python
primary language
Sep 13, 2026
updated
Runs offline on an Intel i5-1135G7 + 8 GB RAM laptop with a hard 2 GB RAM ceiling. No GPU required. Combines Wav2Vec2 + Whisper dual-model ASR, a 5-layer adaptive scorer (A1–C1), SQLite persistence, and SM-2 spaced repetition — all on localhost.
| Metric | Value |
|---|---|
| RAM ceiling | < 2 GB (peak ~1.5 GB with Wav2Vec2, ~420 MB with Whisper) |
| Target levels | A1–C1 (Goethe & ÖSD aligned) |
| Scoring latency | ≤ 2× audio duration (5s clip → ~2s scoring) |
| Scoring layers | 5 (Levenshtein → word diff → suffix → umlaut → phonetic) |
| Models | Wav2Vec2 large xlsr-53-german + Whisper tiny int8 |
| Persistence | SQLite (WAL mode) + SM-2 spaced repetition |
| Backend | Python 3.14 + FastAPI + torch CPU-only |
| Frontend | SvelteKit 4 + Tailwind CSS |
| Controller | Always-on orchestrator (port 8766, ~80 MB) |
| Backend port | 8765 (on demand, started/stopped by controller) |
| Architecture | Controller + Backend two-tier (start/stop from webUI) |
| Freeze detection | CPU >95% for 30s OR RAM >2.5 GB → kill backend |
| Idle auto-stop | 15 min configurable |
/api/score → Wav2Vec2 (character-level phonetic accuracy, ~1.2 GB RAM)/api/live → Whisper tiny int8 (streaming, ~150 MB RAM)/api/status doesn't respond for 30sfull / --quick / --offline / --models-only / --dev-deps / --check / --force~/.cache/huggingface (survives venv rebuild)┌─────────────────────────────────────────────────────────────────────┐
│ Browser → http://127.0.0.1:8766 │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Controller (port 8766, ALWAYS ON, ~80 MB) │ │
│ │ - Serves SvelteKit static frontend │ │
│ │ - /api/controller/status, start, stop, logs, analytics │ │
│ │ - Proxies /api/* → backend (port 8765) │ │
│ │ - Monitors backend CPU/RAM, kills on freeze │ │
│ │ - Auto-stops backend after 15 min idle │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ spawns/kills subprocess │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Backend (port 8765, ON DEMAND) │ │
│ │ - /api/score → Wav2Vec2 (~1.2 GB, character-level) │ │
│ │ - /api/live → Whisper tiny int8 (~150 MB, streaming) │ │
│ │ - 5-layer adaptive scorer (A1-C1) │ │
│ │ - SQLite + SM-2 SRS │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
git clone https://github.com/bif26/Local_DE_Coach.git
cd Local_DE_Coach
./setup.sh # one-time setup (idempotent)
./start.sh # starts controller on http://127.0.0.1:8766
Then open http://127.0.0.1:8766 in your browser, go to the System page, click Start backend.
git pull origin main
./setup.sh # only downloads what changed
./start.sh
./stop.sh # stops controller + backend
Local_DE_Coach/
├── backend/
│ ├── server.py # FastAPI backend (port 8765)
│ ├── controller.py # Always-on orchestrator (port 8766)
│ ├── config.py # Settings (ports, RAM, model IDs)
│ ├── db.py # SQLite access layer
│ ├── launcher.py # Optional pystray system-tray
│ ├── api/
│ │ ├── routes_score.py # POST /api/score
│ │ ├── routes_live.py # POST /api/live (SSE)
│ │ ├── routes_progress.py # GET /api/progress, /api/srs/*
│ │ └── routes_system.py # GET /api/status, POST /api/warmup
│ ├── core/
│ │ ├── engine.py # Wav2Vec2 + Whisper dynamic swap
│ │ ├── preprocessor.py # 16kHz + VAD + normalize
│ │ ├── scorer.py # 5-layer scorer, takes level param
│ │ ├── phonetics.py # German suffix/umlaut rules
│ │ └── srs.py # SM-2 scheduler
│ ├── data/
│ │ ├── schema.sql # users, sessions, word_errors, srs_queue
│ │ └── exam_corpus/ # Goethe + ÖSD reference texts
│ └── tests/
│ └── test_scorer.py
├── frontend/ # SvelteKit + Tailwind
│ └── src/
│ ├── lib/ # api.ts, controller-api.ts, components/
│ └── routes/ # 9 routes
├── docs/ # Documentation (Markdown + PDF + Web UI)
│ ├── markdown/ # ← Source of truth (19 chapters)
│ ├── index.html # Web UI (renders Markdown at runtime)
│ ├── architecture.pdf # 33-page PDF spec
│ └── assets/ # CSS, JS, images, logo
├── systemd/ # Controller + backend services
├── setup.sh # Idempotent setup (7 modes)
├── start.sh # Start controller (+ optional --dev)
├── stop.sh # Stop everything
└── README.md
| Component | RAM | Notes |
|---|---|---|
| Controller (always on) | ~80 MB | Serves frontend + orchestrates |
| Backend (on demand) — cold | ~190 MB | uvicorn baseline, no model |
| Backend — scoring (Wav2Vec2) | ~1,470 MB | Wav2Vec2 large loaded |
| Backend — live (Whisper) | ~420 MB | Whisper tiny int8 loaded |
| Idle auto-stop | — | Default 15 min, configurable |
| Hard ceiling | 2,000 MB | Controller kills at 2,500 MB |
| Layer | Focus | Active from | Pass threshold |
|---|---|---|---|
| 1. Global | Levenshtein ratio (0–100%) | A1 | 60% |
| 2. Word tracking | Missing / extra / substituted words | A1 | 65% |
| 3. Suffix quality | Dropped endings (-en, -er, -e, -n, -s) | A2 | 75% |
| 4. Umlaut accuracy | ä/ö/ü vs a/o/u confusion | B1 | 80% |
| 5. Phonetic features | Final devoicing, vowel length | C1 | 85% |
| Format | Location | Best for |
|---|---|---|
| Markdown (source of truth) | docs/markdown/ | Reading, editing, searching |
| Web UI | bif26.github.io/Local_DE_Coach | Interactive browsing with charts, search, dark mode |
docs/architecture.pdf | Printing, offline reading |
./setup.sh # full (default) — installs only what's missing
./setup.sh --quick # skip pacman + skip model pre-download
./setup.sh --offline # skip ALL downloads; use only cached deps
./setup.sh --models-only # only download ASR models
./setup.sh --dev-deps # only install pytest + httpx
./setup.sh --check # check what's missing; don't install anything
./setup.sh --force # re-download and rebuild everything
| Endpoint | Method | Purpose |
|---|---|---|
/api/controller/status | GET | Backend running? RAM, CPU, idle |
/api/controller/start | POST | Start backend |
/api/controller/stop | POST | Stop backend |
/api/controller/logs | GET (SSE) | Live log stream |
/api/controller/analytics | GET | CPU/RAM charts |
| Endpoint | Method | Purpose |
|---|---|---|
/api/score | POST | Score audio (uses Wav2Vec2) |
/api/live | POST (SSE) | Live transcription (uses Whisper) |
/api/progress | GET | Weekly trend, top errors |
/api/srs/queue | GET | Words due for review |
/api/status | GET | RAM, model, health |
Interactive docs at http://127.0.0.1:8766/docs (Swagger UI).
| Version | Highlights |
|---|---|
| v0.6.3 | Rebuilt docs UI: no sidebar on cover, new indigo theme, AI logo |
| v0.6.0 | PDF spec implemented: dual-model auto-swap engine |
| v0.5.0 | Whisper-only architecture |
| v0.4.0 | Controller architecture, live logs, analytics |
| v0.1.0 | Initial release |
MIT — see LICENSE.
18 commits
Python
43.0%
Svelte
33.3%
Shell
15.7%
TypeScript
6.0%
A local, offline German pronunciation coach
0
stars
18
commits
Python
primary language
Sep 13, 2026
updated
Runs offline on an Intel i5-1135G7 + 8 GB RAM laptop with a hard 2 GB RAM ceiling. No GPU required. Combines Wav2Vec2 + Whisper dual-model ASR, a 5-layer adaptive scorer (A1–C1), SQLite persistence, and SM-2 spaced repetition — all on localhost.
| Metric | Value |
|---|---|
| RAM ceiling | < 2 GB (peak ~1.5 GB with Wav2Vec2, ~420 MB with Whisper) |
| Target levels | A1–C1 (Goethe & ÖSD aligned) |
| Scoring latency | ≤ 2× audio duration (5s clip → ~2s scoring) |
| Scoring layers | 5 (Levenshtein → word diff → suffix → umlaut → phonetic) |
| Models | Wav2Vec2 large xlsr-53-german + Whisper tiny int8 |
| Persistence | SQLite (WAL mode) + SM-2 spaced repetition |
| Backend | Python 3.14 + FastAPI + torch CPU-only |
| Frontend | SvelteKit 4 + Tailwind CSS |
| Controller | Always-on orchestrator (port 8766, ~80 MB) |
| Backend port | 8765 (on demand, started/stopped by controller) |
| Architecture | Controller + Backend two-tier (start/stop from webUI) |
| Freeze detection | CPU >95% for 30s OR RAM >2.5 GB → kill backend |
| Idle auto-stop | 15 min configurable |
/api/score → Wav2Vec2 (character-level phonetic accuracy, ~1.2 GB RAM)/api/live → Whisper tiny int8 (streaming, ~150 MB RAM)/api/status doesn't respond for 30sfull / --quick / --offline / --models-only / --dev-deps / --check / --force~/.cache/huggingface (survives venv rebuild)┌─────────────────────────────────────────────────────────────────────┐
│ Browser → http://127.0.0.1:8766 │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Controller (port 8766, ALWAYS ON, ~80 MB) │ │
│ │ - Serves SvelteKit static frontend │ │
│ │ - /api/controller/status, start, stop, logs, analytics │ │
│ │ - Proxies /api/* → backend (port 8765) │ │
│ │ - Monitors backend CPU/RAM, kills on freeze │ │
│ │ - Auto-stops backend after 15 min idle │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ spawns/kills subprocess │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Backend (port 8765, ON DEMAND) │ │
│ │ - /api/score → Wav2Vec2 (~1.2 GB, character-level) │ │
│ │ - /api/live → Whisper tiny int8 (~150 MB, streaming) │ │
│ │ - 5-layer adaptive scorer (A1-C1) │ │
│ │ - SQLite + SM-2 SRS │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
git clone https://github.com/bif26/Local_DE_Coach.git
cd Local_DE_Coach
./setup.sh # one-time setup (idempotent)
./start.sh # starts controller on http://127.0.0.1:8766
Then open http://127.0.0.1:8766 in your browser, go to the System page, click Start backend.
git pull origin main
./setup.sh # only downloads what changed
./start.sh
./stop.sh # stops controller + backend
Local_DE_Coach/
├── backend/
│ ├── server.py # FastAPI backend (port 8765)
│ ├── controller.py # Always-on orchestrator (port 8766)
│ ├── config.py # Settings (ports, RAM, model IDs)
│ ├── db.py # SQLite access layer
│ ├── launcher.py # Optional pystray system-tray
│ ├── api/
│ │ ├── routes_score.py # POST /api/score
│ │ ├── routes_live.py # POST /api/live (SSE)
│ │ ├── routes_progress.py # GET /api/progress, /api/srs/*
│ │ └── routes_system.py # GET /api/status, POST /api/warmup
│ ├── core/
│ │ ├── engine.py # Wav2Vec2 + Whisper dynamic swap
│ │ ├── preprocessor.py # 16kHz + VAD + normalize
│ │ ├── scorer.py # 5-layer scorer, takes level param
│ │ ├── phonetics.py # German suffix/umlaut rules
│ │ └── srs.py # SM-2 scheduler
│ ├── data/
│ │ ├── schema.sql # users, sessions, word_errors, srs_queue
│ │ └── exam_corpus/ # Goethe + ÖSD reference texts
│ └── tests/
│ └── test_scorer.py
├── frontend/ # SvelteKit + Tailwind
│ └── src/
│ ├── lib/ # api.ts, controller-api.ts, components/
│ └── routes/ # 9 routes
├── docs/ # Documentation (Markdown + PDF + Web UI)
│ ├── markdown/ # ← Source of truth (19 chapters)
│ ├── index.html # Web UI (renders Markdown at runtime)
│ ├── architecture.pdf # 33-page PDF spec
│ └── assets/ # CSS, JS, images, logo
├── systemd/ # Controller + backend services
├── setup.sh # Idempotent setup (7 modes)
├── start.sh # Start controller (+ optional --dev)
├── stop.sh # Stop everything
└── README.md
| Component | RAM | Notes |
|---|---|---|
| Controller (always on) | ~80 MB | Serves frontend + orchestrates |
| Backend (on demand) — cold | ~190 MB | uvicorn baseline, no model |
| Backend — scoring (Wav2Vec2) | ~1,470 MB | Wav2Vec2 large loaded |
| Backend — live (Whisper) | ~420 MB | Whisper tiny int8 loaded |
| Idle auto-stop | — | Default 15 min, configurable |
| Hard ceiling | 2,000 MB | Controller kills at 2,500 MB |
| Layer | Focus | Active from | Pass threshold |
|---|---|---|---|
| 1. Global | Levenshtein ratio (0–100%) | A1 | 60% |
| 2. Word tracking | Missing / extra / substituted words | A1 | 65% |
| 3. Suffix quality | Dropped endings (-en, -er, -e, -n, -s) | A2 | 75% |
| 4. Umlaut accuracy | ä/ö/ü vs a/o/u confusion | B1 | 80% |
| 5. Phonetic features | Final devoicing, vowel length | C1 | 85% |
| Format | Location | Best for |
|---|---|---|
| Markdown (source of truth) | docs/markdown/ | Reading, editing, searching |
| Web UI | bif26.github.io/Local_DE_Coach | Interactive browsing with charts, search, dark mode |
docs/architecture.pdf | Printing, offline reading |
./setup.sh # full (default) — installs only what's missing
./setup.sh --quick # skip pacman + skip model pre-download
./setup.sh --offline # skip ALL downloads; use only cached deps
./setup.sh --models-only # only download ASR models
./setup.sh --dev-deps # only install pytest + httpx
./setup.sh --check # check what's missing; don't install anything
./setup.sh --force # re-download and rebuild everything
| Endpoint | Method | Purpose |
|---|---|---|
/api/controller/status | GET | Backend running? RAM, CPU, idle |
/api/controller/start | POST | Start backend |
/api/controller/stop | POST | Stop backend |
/api/controller/logs | GET (SSE) | Live log stream |
/api/controller/analytics | GET | CPU/RAM charts |
| Endpoint | Method | Purpose |
|---|---|---|
/api/score | POST | Score audio (uses Wav2Vec2) |
/api/live | POST (SSE) | Live transcription (uses Whisper) |
/api/progress | GET | Weekly trend, top errors |
/api/srs/queue | GET | Words due for review |
/api/status | GET | RAM, model, health |
Interactive docs at http://127.0.0.1:8766/docs (Swagger UI).
| Version | Highlights |
|---|---|
| v0.6.3 | Rebuilt docs UI: no sidebar on cover, new indigo theme, AI logo |
| v0.6.0 | PDF spec implemented: dual-model auto-swap engine |
| v0.5.0 | Whisper-only architecture |
| v0.4.0 | Controller architecture, live logs, analytics |
| v0.1.0 | Initial release |
MIT — see LICENSE.
18 commits
Python
43.0%
Svelte
33.3%
Shell
15.7%
TypeScript
6.0%