A pixel-art frost mage lives on your desktop and slowly freezes your screen — right over whatever you're working on. Snow piles up, frost creeps in from the edges. The only way to win your screen back: speak the language you're learning. Every answer you get right melts some of it; ignore him and it only gets worse.
curl -fsSL https://raw.githubusercontent.com/AxeForging/snowlearner/main/install.sh | shThat's it: it installs, downloads the offline speech model and registers the shortcuts.
Then open Snowlearner from your app menu (or run snowlearner). More in Install.
- 🧙 The frost mage patrols the bottom of the screen throwing ice, calls icicle rain, and summons snowmen and penguins that burst frost onto the screen edges. He asks the questions — and taunts you.
- 🔥 Start a lesson and your fire mage teleports in. Answer out loud; get it right and a fireball melts part of the ice. Three in a row call the sun.
- ⚔️ A warrior tries to survive: builds fogueiras, sword-fights frost slimes and bats, gives you tips (false friends, pronunciation traps) — and freezes solid if you ignore him.
- 🪜 You start from zero: first single words (hello, water, thanks), then short expressions (good morning, the check please), and only then full phrases — easy (A1) to harder (B2). A few new items at a time; learning one opens the next, and it tells you so.
- 🗣️ Everything is a real situation (meetings, travel, restaurant, doctor, phone, small talk…): new items you hear and repeat, known ones you say from memory. Missed ones come back until you get them; a recognizer slip of a letter or two doesn't count against you.
- 📈 Progress (
Ctrl+Alt+P): what you already know, what you're learning now and what comes next, per stage and per topic. - 🎙️ It listens like a patient teacher: thinking time before you start, room to hesitate mid-sentence, a live mic meter, and your hotkey to say "done".
- 🔊 Voices: your OS's own, or Kokoro / any OpenAI-compatible TTS server, or any command (Piper…). Pick mics, speakers and voices — and test them — in the panel.
- 🌀 A magic orb sits in a corner: click to practice, Ctrl+click for the panel, right-click to pause — everything gets sucked into a black hole until you're back.
- ✋ Hold Ctrl+Alt (or
snowlearner grab) and a magic hand lets you pick the mage (grumpy) or the warrior (shy) up and drop them in the snow. - 🌙 At the end of the day it shows and reads back everything you practiced.
| A lesson (window mode) | Icicle rain, summons, mobs | Pause = black hole |
|---|---|---|
![]() |
![]() |
![]() |
No cloud, no LLM required: recognition is whisper.cpp running offline. No GPU needed: everything is drawn on the CPU and blitted to the window, redrawing only what changed. About 2–3% of one CPU core and ~30 MB RAM while running (release build; the speech model adds ~150 MB during a lesson and is freed 2 minutes after).
Linux (x86_64) and macOS (Apple Silicon):
curl -fsSL https://raw.githubusercontent.com/AxeForging/snowlearner/main/install.sh | sh
curl -fsSL https://raw.githubusercontent.com/AxeForging/snowlearner/main/install.sh | sh -s -- --lang es # SpanishWindows (PowerShell):
irm https://raw.githubusercontent.com/AxeForging/snowlearner/main/install.ps1 | iexThe installer downloads the latest release,
checks its SHA-256, puts snowlearner in ~/.local/bin (Windows: your user programs + PATH +
Start menu) and runs snowlearner setup, which is safe to run again any time:
- writes the config (
--lang en|es), downloads the ~142 MB offline speech model (--no-modelskips it), - registers the shortcuts on GNOME, adds Snowlearner to the app menu (Linux).
Then start it from the app menu or with snowlearner. snowlearner doctor tells you what works
on your machine and how to fix the rest. Linux needs a voice installed for the app to talk
(speech-dispatcher or espeak-ng, present on most desktops). No GPU required.
From source: cargo install --git https://github.com/AxeForging/snowlearner, then snowlearner setup.
Build dependencies: Rust ≥ 1.85, CMake and a C/C++ compiler (for whisper.cpp).
Linux also needs ALSA and libclang headers (dnf install alsa-lib-devel clang-devel /
apt install libasound2-dev libclang-dev) and, at runtime, speech-dispatcher or espeak-ng.
Feature flags: stt (default: mic + recognition), audio (devices + playback only),
gpu (default: only used for see-through windows on native Wayland/macOS/Windows),
none (hotkey confirms your answer, CPU-only drawing). SNOWLEARNER_RENDERER=cpu|gpu forces one.
| Action | Hotkey / mouse | Command |
|---|---|---|
| Practice a phrase · "I'm done talking" · next | Ctrl+Alt+M · click the orb |
snowlearner say |
| Control panel (game, audio, progress tabs) | Ctrl+Alt+K · Ctrl+click the orb |
snowlearner menu |
| Your progress: known · learning · next | Ctrl+Alt+P |
snowlearner progress (--print in the terminal) |
| Magic hand (15 s) | hold Ctrl+Alt · Ctrl+Alt+G |
snowlearner grab |
| Pause (black hole) / resume | right-click the orb · P |
snowlearner pause |
| Today's recap | Ctrl+Alt+J · S |
snowlearner summary |
| Shortcut cheat sheet | hover the orb · hold Ctrl+Alt |
— |
| Quit | — | snowlearner quit |
Window mode extras: H help, 1–4 commitment, L switch language, click characters to poke them.
Commitment level (compromisso): chill | steady | committed | relentless — how often the
mage attacks, summons and nudges you, and how much each answer melts (12–25%).
snowlearner audio mics # list microphones (* = configured)
snowlearner audio speakers
snowlearner audio test-mic # live meter, then shows what the recognizer heard
snowlearner voices list --lang pt-BR
snowlearner voices test --lang en --voice af_heartOr open the panel's ÁUDIO tab: microphone, Testar microfone, voice engine, pt-BR voice, target-language voice, Testar vozes, speaker. Changes apply live and are saved.
Voice engines (tts_engine):
| engine | what | config |
|---|---|---|
system |
OS voices: spd-say/espeak-ng, macOS say, Windows SAPI |
— |
http |
any OpenAI-compatible server, e.g. Kokoro-FastAPI — natural pt-BR/en/es voices | tts_url = "http://localhost:8880/v1", tts_model = "kokoro" |
command |
anything else: {text} {lang} {voice} {out} placeholders, run without a shell |
tts_command = "piper --model pt_BR.onnx --output_file {out}" |
New engines implement one trait (speech::voices::SpeechEngine: describe / available / voices /
speak) and get device choice, playback, the panel pickers and the CLI for free.
How listening works: the mic ignores the first 250 ms (echo of the voice), gives you 6 s to start
(10 s when recalling from memory — listen_seconds tunes it), calibrates on the quietest moments,
lets you pause ~1.8 s mid-phrase until you've said roughly what's expected, and caps the turn
relative to when you started. Stock whisper hallucinations ("Thank you for watching") count as silence.
| Windows | macOS | Linux X11 | Linux Wayland | |
|---|---|---|---|---|
| Default display | transparent overlay | transparent overlay | transparent overlay | window (--mode overlay runs through XWayland) |
| Global hotkeys | ✓ | ✓ | ✓ | snowlearner setup registers them (GNOME), or bind snowlearner say/menu/summary/grab/progress |
| Hold Ctrl+Alt (cheat sheet, hand) | ✓ | ✓ | ✓ | hover the orb / Ctrl+Alt+G |
| Voice (system) | SAPI via PowerShell | say |
spd-say / espeak-ng |
same |
| Recognition | whisper.cpp | whisper.cpp | whisper.cpp | whisper.cpp |
Tested hands-on on Fedora 42 (GNOME Wayland); Windows and macOS binaries are built by the
release workflow on every v* tag (CI itself runs on Linux: gh workflow run ci).
Everything is editable live in the panel; it's saved to config.toml (snowlearner config path):
learning = "en" # "en" or "es" built in, or your own decks/<name>.toml
native = "pt-BR"
commitment = "steady" # chill | steady | committed | relentless
practice = "auto" # auto | repeat | recall
topic = "" # "" = all, or e.g. "trabalho", "viagem"
max_level = "B2" # A1..C2
daily_goal = 10
listen_seconds = 6.0 # time to start answering (recall gets +4)
mic = "" # "" = system default (see `snowlearner audio mics`)
speaker = ""
tts_engine = "system" # system | http | command
voice_native = "" # "" = automatic
voice_learning = ""
mode = "auto" # auto | window | overlay
summary_time = "21:00"
model = "base" # tiny | base | smallDrop a deck at <config>/decks/en.toml (it overrides the built-in one):
language = "en"
native = "pt-BR"
language_name = "inglês"
title = "Trabalho"
[[tip]]
text = "Falso amigo: 'actually' é 'na verdade'."
[[phrase]]
topic = "trabalho"
level = "B1"
situation = "Você quer marcar uma reunião com o time."
say = "Let's schedule a meeting"
accept = ["Let's set up a meeting"]
meaning = "Vamos marcar uma reunião"
tip = "Schedule: 'skédjul' no americano."The built-in decks combine hand-written phrases, the first words and expressions
(decks/<lang>.basics.toml) and the Lexicaster curriculum
(node scripts/port-lexicaster.mjs ../learn-lang-game regenerates decks/*.lexicaster.toml).
make test # unit + binary tests
make test-speech # real speech recognition end-to-end (needs model + espeak-ng)
make lint # rustfmt --check + clippy -D warnings (all feature sets)
scripts/render-docs.sh # re-render the README clips (animated WebP) from the real sceneSee CLAUDE.md for layout and conventions and docs/specs for specs.



