Every typing test I tried feeds you paragraphs of English. But the characters that actually slow me down while coding ({}, =>, ::, ?.) almost never appear in prose, so I was practicing the wrong thing.
So I built Typre: a typing trainer that only serves real code.
What it does
2,590 hand-picked snippets across 14 languages (JavaScript, TypeScript, Python, C#, Java, Go, Rust, SQL, Bash, C/C++, HTML/CSS/JSON), plus a punctuation-only drill
15, 30, and 60 second runs
WPM, accuracy, consistency, and the characters you mistype most
Timed drills for VS Code and Vim keyboard shortcuts
Real-time races with a friend over a shareable link
A few decisions I cared about
No repeated snippets. Snippets come from a shuffle bag, so you won't see the same one twice until you've gone through the whole pool. The state survives page reloads, without a backend.
Offline-first. It's a PWA and every snippet ships in the bundle, so it keeps working on a plane.
No account needed to type. Signing in only adds the leaderboard, friends, and cross-device sync.
Lazy-loaded highlighter. Shiki went from 11 MB across 309 files to 1.3 MB across 12, because only the grammar for the language you're practicing gets loaded.
Stack: React 19, TypeScript, Vite, Tailwind v4, Zustand, Shiki, and Supabase (optional, only for accounts and the leaderboard).
It's free, with no ads. The code is on GitHub if you want to dig into the snippet pools or the service worker.
I'd love feedback, especially if a snippet in your language feels off or hard to type for the wrong reasons.
Top comments (4)
this solves the exact pain point i had. typing "the quick brown fox" doesn't prepare your muscle memory for
=>,::, or{}.the shiki optimization is what really caught my eye though. shrinking it from 11mb to 1.3mb by lazy-loading just the active grammar is god-tier. i build koda (an ai coding mentor) on a $150 phone, so keeping the bundle tiny for mobile 3g networks is my biggest obsession.
curious about the offline shuffle bag: are u storing the consumed snippet ids in localstorage to survive the reload? would love to peek at how u handle that state without a backend.
amazing tool. definitely bookmarking this for my daily warmup. 🐯⌨️
Thanks a lot! Yes, I use localStorage, but I save the opposite: the snippets you haven't done yet, not the ones you finished.
Each language and time length has its own list. Every new run takes the next snippet from that list. When the list is empty, I shuffle all the snippets again and start a new round.
When the page loads, I remove any saved ids that no longer exist, because I add and change snippets sometimes. If the browser blocks localStorage, it still works. It just forgets your progress when you reload.
Good luck with koda!
saving the "undone" list instead of the "done" list is way smarter for storage limits, especially as the snippet pool grows. genius optimization. thanks for the breakdown, and thanks for the koda love! 🐯
Is the shuffle bag stored in
localStorage, or do you serialize it somewhere else?