βΆ PLAY NOW β free, in your browser, no install
Design docs Β· Mechanics Β· Level design Β· Contributing
The world only moves when you type. Type better.
Vimberman is a browser Bomberman-like where every move is a Vim command. No
arrow keys. No spacebar. You survive with hjkl, counts, word hops, find
motions, real edits, and undo β and the world advances one tick per
completed command. Twelve taps of l give the zombies twelve turns. 12l
gives them one.
That's the whole game: Vim's editing philosophy β move farther with fewer
keys β becomes the difference between a clean escape and a very educational
explosion. By the final level you're setting marks, bouncing off %,
searching with /, and recording macros to solve puzzles. Congratulations:
you accidentally learned the world's most portable skill.
The game coaches you toward fluency. Rudely, if necessary.
- Turn-based on your keystrokes. Enemies, fuses, and hazards move only when you complete a command. It's chess where the clock is your WPM in Vim.
- Bombs come from editing. Stand on a code tile, press
i, and fix the broken word βbpmbβbombβ with real Vim edits (x,r,~,s,cw,ciw). A correct word arms a bomb. Drop it withx. Now run. - Motions are Metroid powerups. Keycap pickups
?permanently add motion families to your vocabulary. You learnfbecause a gap is uncrossable without it, not because a tutorial said so. - Undo is your lives.
urewinds one world tick β even death β but charges are scarce and no undo crosses an explosion. - Golf scoring. Every level has a hard keystroke budget and a par for the 3-star clear. Deterministic, seeded enemies make every retry identical: plan like a puzzle, execute like a speedrun.
![]() |
![]() |
Fix bpmb with real Vim edits to arm a bomb |
β¦then get out of the plus-shaped blast |
![]() |
![]() |
The linter sweeps its row β 0 and $ save lives |
Ctrl-u rides an updraft into the cloud layer |
The difficulty curve is a Vim curriculum. Each level makes exactly one new trick load-bearing:
Spoiler: the full campaign
| # | Level | Teaches |
|---|---|---|
| 01 | BABY STEPS | h j k l |
| 02 | COUNT THE CORRIDORS | counts: 10l 6j |
| 03 | LEAP OF FAITH | f F t ; β dash, stop short, repeat |
| 04 | BUGFIX BOMBS | i x r β edit words, earn bombs |
| 05 | WORD BRIDGES | w b e β hop between words |
| 06 | THE LONG WAY | 0 $ gg G β slam across lines |
| 07 | FLIP THE SCRIPT | flight flips toads |
| 08 | REWRITE THE RULES | cw β change a whole word |
| 09 | AGAINST THE CURRENT | one-way tiles β commit or route around |
| 10 | MIND THE MARGINS | 0 $ β anchors when the row goes hot |
| 11 | WARPED WORDS | ~ and ciw β case flips, inner words |
| 12 | HEAD IN THE CLOUDS | Ctrl-u Ctrl-d β the sky layer |
| 13 | CUMULUS GOLF | kites β flight cuts the string |
| 14 | BOOKMARKED | m{a} `{a} β bookmarks |
| 15 | BALANCED BRACKETS | % β jump to the matching bracket |
| 16 | GREP | /{word} n β search the whole file |
| 17 | DON'T REPEAT YOURSELF | . β repeat the last edit |
| 18 | THE FINAL REFACTOR | everything β and a merciless budget |
| 19 | BABY STEPS, PROMOTED | worn keys β lead with a count |
| 20 | AUTOMATE YOURSELF | q @ β record and replay macros |
| Keys | Effect |
|---|---|
h j k l |
Step. Bonking a wall still costs a turn. |
5l, 3j, β¦ |
Counted motion: many tiles, one enemy tick. |
w b e |
Hop between words of lettered tiles, soaring over gaps and enemies. |
f{c} F{c} t{c} T{c} ; , |
Dash along the row to (or just before) character {c}; repeat, reverse. |
0 $ gg G |
Slam to row/column ends, sweeping up items on the way. |
m{a} `{a} |
Drop a mark, teleport back to it. |
% |
Bounce between paired bracket doors. |
/{word} n |
Search the level; n jumps to the next hit. |
. |
Repeat your last terminal edit β one keystroke, one golf stroke. |
q{a} @{a} |
Record a macro, replay it β one tick per replayed command. |
Ctrl-u Ctrl-d |
Ride an updraft into the cloud layer / drop back down. |
i |
Open the code tile underfoot: one-line Vim terminal, real edits. |
x |
Drop an armed bomb (in the world) / delete a char (in the terminal). |
u |
Rewind one world tick β even death. Charges are precious. |
: |
Ex commands: :help :map :hint :q :q! |
The playfield reads like a buffer with relativenumber: the gutter counts
rows for you (see a 4 in the gutter β type 4j), the ruler counts columns,
and a pending count lights up its landing tiles.
The dungeon is source code with teeth, tuned to punish inefficient editing:
| Enemy / hazard | Role |
|---|---|
Zombie Z |
Half-speed chaser. It loves wasted keystrokes. |
Imp & |
Drops its own bombs. Bait it into mining rocks β or fragging its friends. |
Toad Q |
Hops pits every third turn. Fly over it (w, fβ¦) to flip it; squash for a bonus. |
Mage M |
Teleports on a readable cycle, fires down rows and columns. Never idle aligned. |
Kite Y |
Sky-native. Any flight motion that crosses its string cuts it down. |
The linter ! |
Not a creature β a hazard. Sweeps its whole row on a cycle; only the margins survive. 0 and $ are one keystroke from anywhere. |
| The flytrap | Lives in level 12's shadows. That's all you get. |
Boulders bomb open Β· steel needs a widened blast Β· starfield gaps only yield
to flight motions Β· chevron tiles are one-way commitments Β· bushes hide
budget, bombs, radius, and undo charges Β· keycaps ? unlock motions Β· the
glowing portal is the exit.
Requirements: Node.js 22+ (what CI uses).
git clone https://github.com/mikeldking/vimberman.git
cd vimberman
pnpm install
pnpm dev # Vite dev server with HMROther useful commands:
pnpm test # vitest: solvability proofs, engine rules, UI smoke, sprites
pnpm build # strict typecheck + production build into dist/
pnpm preview # serve the production build locallyTypeScript throughout (strict), bundled by Vite, zero runtime dependencies.
The engine is pure logic β no DOM β and notifies the UI through overridable
fx hooks. Everything you see is a procedural 16Γ16 pixel-art sprite atlas
drawn to a canvas with a CRT shell.
| Path | Role |
|---|---|
src/levels.ts |
20 levels as ASCII maps + terminals, bushes, keycaps, linters, sky layers, budgets |
src/engine/ |
Pure game logic: motions, terminals, bombs, AI, undo, deterministic ticks |
src/render/sprites.ts |
Procedural pixel-art atlas β every sprite is a validated character grid |
src/render/renderer.ts |
Canvas draw loop: animation, tweening, particles, shake, glow |
src/ui/ |
Menus, HUD/statusline, WebAudio synth, saves, the code-tile editor |
docs/ |
The full design bible β premise, mechanics, bestiary, levels, story, architecture |
The tests are part of the design, not just safety rails:
test/solve.test.tsreplays hand-authored keystroke scripts through the real engine β every level ships with a machine-checked proof that par is achievable.test/engine.test.tspins movement, bonk, blast, and undo rules.test/ui-smoke.test.tsboots the app against a stub DOM and clears level 1 through the real keydown handler.test/sprites.test.tsvalidates every sprite grid in the atlas.
Progress saves to localStorage. Deploys ship automatically:
pages.yml typechecks, tests, builds, and
publishes to GitHub Pages on every push to main.
Vimberman was built entirely with Hermes Agent and Fable 5, and the door is wide open for more chaos. PRs are welcome β especially ones that make the game unimaginably more fun: new levels, absurd enemies, sharper tutorials, better juice, tastier sound, funnier terminal copy, wilder ideas that somehow still fit the Vim/Bomberman premise.
The core contract, from CONTRIBUTING.md:
- Every keystroke is a decision. No filler input.
- Teach by doing. A new trick becomes load-bearing in the level that introduces it.
- Efficiency is the score. Budgets reward Vim fluency, not reflexes.
- Determinism makes it a puzzle. If randomness changes retries, seed it and test it.
- Fiction and mechanic are the same object. If a feature needs a paragraph of lore, simplify it.
If you add a level, add a solver route so CI proves it can be won. If you enjoy the game, consider sponsoring β it buys more levels and meaner little Vim jokes.
MIT. Take the bombs. Learn the motions. Share both.



