Skip to content
Arcade

Normal Mode.

You are the cursor. Eleven files stand between you and the exit, each one a key or two wider than the last, and the only way out is :wq. It is a real Vim tutor wearing an arcade cabinet — every command you press is the command, and the score is the one Vim golfers actually keep.

  • Canvas 2D
  • Web Audio
  • No engine
  • No assets
  • Game loads on start
  • Par is solved, not authored

mj-arcade · cab 01

no record

normal mode

You are the cursor. Eleven files, one key at a time, and the only way out is :wq.

takes the whole screen · the engine downloads when you press this

How to play

Swipe to move
The playfield is a trackpad. Drag with one thumb and a ghost cursor shows where you would land while the command line spells out what you are about to run — 5j, not “down a bit”. Lift to run it.
Or type the real thing
On a keyboard you type Vim: hjkl, 3w, f", daw, ci(, ., u, :wq. Both roads go through the same parser, so a tap on the deck and a keystroke are the same event by the time the game sees them.
The deck only offers legal keys
Press d and the strip under the screen becomes the motions and text objects d can take. Press f and it becomes the characters actually on this line. It cannot get cluttered, because a cluttered deck would mean the parser had accepted something it should not.
Par is solved, not guessed
Every task carries the fewest keystrokes that would finish it with the keys this file has taught — a shortest-path search over the buffer, run when the task opens. Match par and the streak holds. Go over and it resets. The number drops when you learn a command, which is the entire lesson.
The fuse is the pressure
Each task has one. Run out and the file reloads from where the task began — nothing you break is permanent — and the buffer loses stability. Four of those and the run is over. You restart at the file you died in, not at hello.txt.
Undo is a command, not a mercy
u works, and it arrives in the same file as your first operator. It costs a keystroke like everything else, so it blows par — and it is always cheaper than watching the fuse run out.

Keyboard

  • hjklLeft, down, up, right
  • 19A count in front of any command
  • wbeBy word — punctuation counts as one
  • 0^$Column one, first non-blank, end of line
  • ft;Find a character on this line, and again
  • ggG{}Top, bottom, paragraph
  • dcyDelete, change, yank — each waits for a motion
  • iaText objects: diw, daw, ci", ci(
  • xp.One character, put it back, do it again
  • uUndo
  • :ZZWrite and quit — the way out
  • EscCancel a half-typed command
  • CtrlZSuspend, which is what it does in a real Vim

One thumb

  • Insert coin takes the whole screen. Exit, top right, hands the page back.
  • Drag anywhere on the playfield — the further you drag, the bigger the count. A long drag down is 5j and it says so before you commit.
  • The h j k l pad under the screen does the same one cell at a time, and is the legend for what the swipes are.
  • Everything else is a chip on the command deck, thumb-height, showing the real key it presses.
  • Two quick taps on the buffer is the dot command, once you have one.
  • No software keyboard is ever needed — where a command types text, the game types it for you.

Sound is off until you turn it on, and effects drop to a calm variant automatically if your system asks for reduced motion. Both are toggles under the screen.

Eleven files

Each file teaches its keys, then asks for them under a fuse. Clear the last one and endless mode opens: generated buffers, the whole vocabulary, and a fuse that keeps tightening.

  1. 01hello.txthjkl
  2. 02todo.md1-9
  3. 03words.txtwbe
  4. 04indent.py0^$
  5. 05config.jsonft;
  6. 06changelog.mdggG{}
  7. 07bugs.jsdxu
  8. 08objects.tsciwawi"
  9. 09repeat.ts.
  10. 10registers.jsyp
  11. 11exit.vim:ZZ

Field notes

Par is the part I am pleased with. It is not a number I typed next to each task — it is a shortest-path search over every cursor position the unlocked motions can reach, plus the cheapest single edit command issued from any of them. Which means the solver works out, on its own, that daw beats dw when you are standing in the middle of a word and loses to it when you are standing at the start. Nobody had to write that down.

The Vim in it is the real Vim, including the parts that annoy people. cw behaves like ce. An exclusive motion that ends in column one backs up to the line above and turns inclusive, which is why dw on the last word of a line leaves the newline alone. There is one deliberate departure, and the game says so on the card that teaches it: a change command types its replacement for you, because a phone should never have to open a software keyboard mid-run.

The simulation never calls Math.random. Endless mode builds its files from one seeded stream, and every generated task is produced by running a real command on a scratch buffer and keeping the result as the goal — so an unsolvable task is not a bug that can happen. The whole thing is driven headless in Node, where the test suite solves all forty-nine tasks of the campaign and fails if any of them needs a key it has not taught yet.

It pauses itself when the page goes to the background and when you walk away from the route. On a desktop, a window that loses focus counts as leaving too — on a phone it deliberately does not. Mobile browsers blur the page to collapse their own toolbars, and the game was reporting that as though you had walked off mid-drag.

None of it — the engine, the solver, the renderer, the synth — is on this page until you press insert coin. The rest of the site never downloads a byte of it. That part is not a joke about arcade cabinets; it is the actual reason this is a separate chunk.