A terminal typing test with a vim mode, and an arcade: keycaps and words fall toward a glowing RGB keyboard, and you type them before they land.
Web: snipetype.com · add ?mode=arcade for the arcade
snipetype arcade # Words game: lines of words and quotes fall
snipetype arcade --game keys # Keys game: keycaps, vim motions, Ctrl chords, :wq
snipetype arcade --pace 10x --speed 2Your shift runs from 09:00 to 18:00, one new stage every 30 seconds, and then Overtime. You lose health when a word or keycap lands, or when you press a wrong key. Combos raise your multiplier and make the hit effects bigger, from confetti and frost up to supernovas and black holes. The shift ends with a results card and a rank.
| Screen | Keys |
|---|---|
| Title | tab game (words / keys) · ←/→ pace (junior, senior, staff, 10x) · ↑/↓ speed ×1–×16 · enter clock in · esc quit (native) |
| Playing | type what falls · esc pause |
| Paused | esc resume · q title |
| Results | enter again · esc title |
Options: --game words|keys · --pace junior|senior|staff|10x ·
--speed 1|2|4|8|16 · --gfx auto|kitty|cells.
On the web: ?mode=arcade&game=keys.
The arcade draws real pixels, so it looks best in a terminal that supports the kitty graphics protocol:
| Terminal | Rendering |
|---|---|
| Ghostty, kitty, WezTerm, Konsole | kitty graphics (smooth pixel layer with bloom) |
| tmux (any terminal) | half-block cells |
| other truecolor terminals (iTerm2, Alacritty, foot, …) | half-block cells |
- Auto-detection:
--gfx autoprobes the terminal at startup. - Forcing a backend:
--gfx cells/SNIPETYPE_GFX=cellsforces half-blocks.--gfx kittyforces kitty graphics. - Truecolor: half-block rendering needs 24-bit color.
- Three-key chords (Keys game, 17:00) need the kitty keyboard protocol. Other terminals get a vim-macro stage instead.
Game design, scoring and rendering: docs/arcade.md.
bunx snipetype@latest
# or
npx snipetype@latestsnipetype # 10 common words
snipetype -w 25 --punct # 25 words with capitals and punctuation
snipetype -t 30 --numbers # 30 seconds, numbers mixed in
snipetype --tier easy # the 200 most common words (also: common, 5k, full)
snipetype -m sentences # sentences and quotes
snipetype -m code -l rust # multi-line code snippets
snipetype -m code --vim -l go # retype code snippets with real vim keys
snipetype -m vim # vim drills
snipetype --daily # today's daily challenge
snipetype -m zen # free typing, esc for the results
echo "your text" | snipetype # type your own text (also --text, --file)
snipetype stats # your history in the terminal (--csv to export)The engine works word by word, like Monkeytype: a typo stays in its word, and space moves to the next one. Extra letters show up after the word and skipped letters are underlined. Accuracy counts every keystroke, so mistakes you fixed still count. The results show wpm, raw, accuracy, consistency, the correct / incorrect / extra / missed letters, a per-second graph, your missed and slowest words, and how the run compares with your personal best for the same setup.
- Word lists (
--tier):easyis the 200 most common words;commonis a 1k list and the default;5kis a hand-curated 5,000-word list;fullis the big dictionary insrc/core/words_en.txt. - Timed tests (
-t 15|30|60|120) never run out of words. - Punctuation and numbers (
--punct,--numbers) add capitals, sentence marks, commas, quotes, years and decimals to words mode (--no-punct,--no-numbersturn a saved setting off for one run). - Code mode types real multi-line snippets (zig, rust, go, ts, python,
c, bash, lua, sql, java, yaml):
entergoes to the next line and the indentation is skipped for you. Enter mid-line skips the rest of the line: the untyped letters count as missed (like space mid-word skips the rest of a word) and the caret moves to the next line. On a line you haven't started, enter does nothing, so a double enter is harmless. Untyped code is syntax-coloured (--no-syntaxturns it off). - Code with vim keys (
--keys vim,-m code --vim,ctrl+kon the code idle screen, orctrl+p→ code → keys): the snippet is retyped in the real vim engine instead, scored on keystrokes like--vim type. - Pace caret (
--pace off|pb|<wpm>,ctrl+p→ options): a second caret moves at your personal best for the current setup (on by default once you have one) or at a fixed speed. - Strict typing:
--stop-on-error letterdoesn't type a wrong letter,--stop-on-error wordwon't let you leave a wrong word, and--no-backspaceturns corrections off (withwordit stops on the letter instead, since a wrong letter could never be removed). - Layout emulation (
--layout dvorak|colemak|colemak-dh): practise a layout on a QWERTY keyboard. Leave it atqwertywhen your OS layout is already switched, or every key is remapped twice. - Weak keys: the results show a per-key accuracy heatmap and per-key
speed;
won the results (or--weak) gives you words weighted toward your weakest keys, from all your history. - Zen (
-m zen): free typing with no target;escorshift+entershows the results (terminals without the kitty keyboard protocol, such as Terminal.app, sendshift+enteras a plainenter, so useescthere). - Custom text (
-m custom,--text,--file, piped stdin, or a paste on the custom idle screen; on the web an "edit text" dialog): the whole text once, or timed with-t. It is kept incustom.txtnext to the config. - Daily challenge (
--daily): 50 common words with punctuation, the same for everyone on a UTC date, with its own personal best.
| While typing | |
|---|---|
ctrl+w, ctrl+backspace, alt+backspace |
delete the word |
ctrl+u (web on macOS: cmd+backspace) |
delete back to the line start |
←/→, ctrl+←/ctrl+→ |
move inside the current word |
enter |
code: next line; mid-line it skips the rest of the line |
esc, tab |
restart with new text (zen: esc finishes) |
ctrl+k |
code idle screen: switch plain keys ↔ vim keys |
f2 |
history (idle screen) |
ctrl+p |
settings: theme, mode, test options, options |
ctrl+c |
quit (web: restart) |
| On the results | |
|---|---|
enter, space |
next test |
r |
retry the same text |
m |
practise the words you missed |
w |
practise your weakest keys |
h, f2 |
history |
tab, esc |
new text |
q, ctrl+c |
quit |
ctrl+p sets the length (10 / 25 / 50 / 100 words or 15 / 30 / 60 / 120 s),
the word list, punctuation, numbers and word focus, the code language, keys
and syntax colours, and the vim drill lesson without leaving the app. Its
options panel (tab to it) holds the pace caret, stop on error,
backspace, layout and (web) typing sounds. Pasted text is never typed, except
on the custom idle screen, where a paste becomes the custom text.
f2 (idle or results) or h (results) opens the history screen: recent runs,
personal bests per setup with a wpm trend sparkline, and per-key accuracy.
tab/←/→ switch tabs, j/k/pgup/pgdn/g/G scroll, esc closes.
snipetype stats # totals, last 10 runs, bests, trend, weakest keys
snipetype stats --limit 30 # more recent runs
snipetype stats --csv > runs.csvsnipetype stats needs no terminal, so it works in scripts and pipes.
Legacy terminals send ^H for ctrl+backspace, so ctrl+h deletes a word
too. When the tty's erase character is ^H (stty erase ^H, a terminal
whose backspace key sends ^H), ^H is read as a plain backspace instead.
Results keys are ignored for half a second after a test ends, so a trailing space or the next word of a timed test doesn't skip the results (or quit).
snipetype -m vim is vim practice. Each drill gives you a buffer and a goal:
edit the buffer until it matches, in as few keystrokes as you can. Beat par
for three stars. The drills are grouped in lessons, from hjkl and word
motions to text objects, registers, the dot command, search, :s and macros
(ctrl+p picks a lesson). You start at the first drill you haven't 3-starred
yet.
| Key | |
|---|---|
f1 or :hint |
show a hint (a hinted run can't get 3 stars) |
:e! |
retry the drill |
:w |
submit |
:q, tab (normal mode) |
skip to the next drill |
ctrl+c |
esc; twice in normal mode quits |
--drill <id or lesson> |
start somewhere else, e.g. --drill text_objects |
A drill counts as solved once the buffer matches and you are back in
normal mode (press esc after the last insert). --vim type keeps the old
mode: retype a code snippet into an empty buffer
with vim keys, scored on keystrokes and efficiency. Code mode can do the
same with its own snippets (-m code --vim). The engine covers counts,
motions, operators, text objects, registers, visual mode, search, undo/redo,
., macros and insert-mode ctrl+w/ctrl+u, plus visual block mode
(ctrl+v), :g/:v with :normal, :s capture groups, gn, sentence
motions, search offsets and the jump list (ctrl+o/ctrl+i; legacy
terminals send tab for ctrl+i, so jumping forward needs the kitty
keyboard protocol). Details: docs/vim.md.
The theme, mode and test options you pick are saved, and every valid run is appended to a history file that personal bests come from:
| Native | Web | |
|---|---|---|
| Settings | ~/.config/snipetype/config.json |
localStorage["snipetype.config.v1"] |
| History | ~/.local/state/snipetype/history.jsonl |
localStorage["snipetype.history.v1"] (newest 1,000 runs + one line per personal best) |
| Custom text | ~/.config/snipetype/custom.txt |
localStorage["snipetype.custom.v1"] |
XDG_CONFIG_HOME and XDG_STATE_HOME move the native files (macOS uses the
same paths), and SNIPETYPE_HOME=<dir> puts both in one directory.
--no-save writes nothing. Command-line flags and URL parameters
(?mode=code&words=25&theme=nord) beat the saved settings; -w/words=
without a time means a word-count test even when a time limit is saved, and
--drill/drill= switches to vim drills. Runs shorter than a second and
pasted runs are not recorded. A symlinked config.json (dotfiles) stays a
symlink when settings are saved. A history file over 16 MB is compacted at
startup to one line per personal best plus the newest 5,000 runs.
On Windows and Linux the browser keeps ctrl+w (close tab), ctrl+t and
ctrl+n for itself, and a page can't stop that. Use ctrl+backspace or
alt+backspace to delete a word; they work everywhere, including vim insert
mode. If you are mid-run, the page asks before it closes. In fullscreen (the
page's fullscreen button) Chromium browsers let the page keep ctrl+w, so
it deletes a word there. In vim mode (and code with vim keys) on the web,
ctrl+r (redo) and ctrl+v reach the app; ctrl+shift+v still pastes and
f5 still reloads. ctrl+k reaches the app too.
Requires Zig 0.17.0. The easiest way to get it is mise:
mise use zig@0.17.0
zig build run -- arcade # native
zig build test
zig build web-dist # wasm → web/dist/
cd web && bun run dev # http://localhost:8080/?mode=arcadeMore: docs/building.md · docs/web.md · docs/architecture.md.
MIT