It’s online. Right now. At boom.nkh.do. Bring a friend, bring an enemy, bring someone you tolerate at work. I have a multiplayer game on the internet. I am a serious person now.
If you missed the earlier devlogs: #1 was rectangles and bots, #2 was sprites and a map editor. This one is the multiplayer one, plus a fistful of mechanics I’m copying straight out of CrazyArcade because the original was right and I am not above stealing from giants.
Multiplayer, finally

The fixed tick from devlog #1 was always a setup for this. Colyseus does the heavy lifting — authoritative rooms, schema-encoded state diffs, and a websocket protocol I did not have to write. The dream.
Architecture:
- Server owns the simulation. Same tick loop as #1, just with seeded RNG and inputs arriving over the network.
- Clients send
{ dx, dy, placingBomb }and render whatever the server sends back. They do not predict. They do not rollback. They are dumb terminals with opinions about CSS.
One change since #1: the tick is now 60Hz instead of 20Hz. 50ms per frame felt fine when only one character moved. With two humans watching each other slide between tiles, it looked like a flipbook. 16.67ms looks like a movie. The sim math didn’t change — sub-cells per tick scaled down so movement speed stayed identical — only the framerate. Bandwidth tripled in the worst case, which still rounds to “nothing” because Colyseus only ships diffs.
No client-side prediction. Your ping is your ping. If a stranger from across the planet joins my room and tries to dodge a bomb, that’s the day I learn what client-side prediction is. Today is not that day.
Hosting is a single Node process behind nginx on its own VPS — separate box from the one serving this blog, because mixing a websocket game server with a static site is the kind of decision you regret the first time one of them needs a restart. If 200 people show up at once, the box catches fire and you get devlog #4 early.
Joining flow: open boom.nkh.do, enter a superb name, join or create a room, pick class and team, hit ready. Room codes are six alphanumeric characters. Yes, they can spell rude words. No, I will not be filtering them.
Tile pushing, finally for real
Pushable tiles were a flag in the bitmask since #1 but never actually pushed. They sat there, smug and immobile, pushable in spirit. Now they push.
Walk into a SOLID | PUSHABLE tile. If the next tile over is empty, it slides one cell and you continue your motion as if the wall had gotten out of your way. Which it had. Because you bullied it. If the next tile is solid, nothing moves and we all stand in mutual disappointment.
Edge cases, kept boring on purpose:
- Push a bomb? No.
- Push into a player? No.
- Push onto a powerup? Powerup dies. RIP.
The map editor got a “Pushable” checkbox with the same auto-constraint as before: pushable requires solid. A pushable non-solid tile is a contradiction my game refuses to entertain.
1/2 bomb placement
If you played CrazyArcade, you already know. If you didn’t, go try it — explaining it in text would do it a disservice and we’d both feel cheated.
Class stats, recalibrated
The class stats table from devlog #2 was a vibe-based guess. Numbers I made up while squinting at the screen. Turns out: the original CrazyArcade balance is, against all odds, good — the result of twenty years of players yelling at the developers — and I am not going to out-design that with a spreadsheet I wrote at midnight.
So I copied the base stats too. Speed, max bombs, max fire — straight from the CrazyArcade character tables. My five classes now map cleanly onto the archetypes from the original, just with Tiny Swords sprites instead. The warrior is balanced because the corresponding CrazyArcade character was balanced. The pawn spams bombs because its CrazyArcade analogue spammed bombs. Twenty years of community-tuned balance, applied via Ctrl+C.
The lesson, repeated for my own benefit: when you’re cloning a thing, clone the thing. Don’t reinvent the parts that are already solved. The parts the original game got wrong are the ones worth changing. Everything else is borrowed time.
New tiles and blocks

The tile palette in #2 was “walls, breakable blocks, bushes.” Functional. Boring. Now:
- Multi-tile buildings. A single building can span multiple tiles — the tower is 2×3, the gate is 3×2, etc. Each footprint tile is solid; the sprite is anchored to one cell and rendered at full size so it visually overlaps the others. The pathfinder doesn’t care, the renderer doesn’t blink, and the map suddenly looks like a place instead of a grid.
- More flora. New bush variants, new trees, all of them assigned via the random
assetIdsystem from #2. Every patch of greenery looks slightly different. Nature: implemented. - VFX. New explosion particles, dust puffs when a tile gets pushed, screen shake on big chains. The game finally feels like it weighs something.
UI, less embarrassing now

The lobby grew up. The HUD grew up. The results screen exists, properly, instead of a single “you won” in Arial.
- Lobby: room codes with a copy-to-clipboard button. Class picker animates the actual sprite — the warrior idles, the lancer does its 12-frame calisthenics — with stats next to each so you don’t have to re-read devlog #2 to remember which one is “bomb spam.”
- Room chat. Every room has a chat panel. Trash talk before the match, trash talk during the match, trash talk after the match. Messages route through the Colyseus room so latency is identical to gameplay state — your insult arrives at the same speed your bomb does. No moderation, no filtering, no logs. If 2008 internet was a feature, this is it.
- HUD: live in-game status — bombs remaining, fire range, speed, item slot.
- Player nameplates: floating nameplate per entity with HP segments, team color underline, and a small icon when carrying an item. At depth 3.5 from the layer stack in #2, same as the bubble — no conflict because a trapped player has the bubble as the nameplate.
- Results screen: kills, deaths, rescues, blocks broken, powerups collected, distance walked. Distance is in tiles, not pixels. Nobody walks 6,400 pixels. Everybody walks 100 tiles.
What’s next
- Items. The big one. CrazyArcade-style consumables: needle (pop your own bubble — self-rescue), shield (eat one explosion), banana trap (drop it, next walker slips), and whatever else my brother and I argue about on Discord for the next two weeks.
- More maps. Five is a tutorial. I want twenty. Half hand-crafted, half user-submitted via the map editor JSON export. Eventually a map browser. Eventually ratings. Eventually stop scope-creeping, Hoang.
- Monster mode (maybe). PvE with predetermined movement patterns: a slime that splits when bombed, a bat that flies over walls, a ghost that goes intangible on a timer. Firmly in the “if I have time” pile, which is also the “I’m absolutely going to build this instead of the items” pile. This is a confession.
Until then, the game is up at boom.nkh.do, the tiles push, and somewhere a player is realizing they placed the bomb on the wrong sub-cell. See you in #4.