Nooki
July 202614 min readOngoing

It all started with a game between friends. We wanted a little world of our own, a place to meet up, play, chat and mess around together, even when we were far apart.
First we played a game like that, with proximity voice chat: you walk up to someone to talk to them, and their voice fades as you walk away. But the map was flat, there were no interiors, no shops, no life around. We only played it for a few days, and that was enough to make me want to go further and put my own spin on it. Being able to make your own home, to have a real life in there, my own little world basically.
That’s what Nooki is. You create a room, send the code to your friends, and everyone ends up on the same island. The game is live at nooki.kevin-dev.com. The easiest way to show it is to walk you through an evening there.
An evening on the island
When you come back, you pick up where you left off last time. First, you look for the others. The M key opens the island map, with its neighborhoods, the houses for sale and, on the right, who’s where. You click a place, then “Y aller” (“go there”), and your character gets there on its own, on foot or by car.

Once you’re together, you hear each other like in the original game, louder or quieter depending on the distance. Then everyone does more or less what they want: you fish by the water, catch bugs, sell your catch, furnish your house. And when you want to talk privately, there are the Offices, in the Palace. Each room has its own voice chat, so you can split into small groups, and you can even see each other on video in a full-screen view that looks like a Teams meeting.

The island runs on Paris time, the same for everyone. So when you play in the evening, it’s night in the game too. The sun moves, the shadows follow, then the street lamps, the car headlights and the windows light up, and the locals who were strolling around town head home. Sped up, at the crossroads outside the hotel, it looks like this:

That’s when you head to the beach bar, which is actually a nightclub. Léo, the DJ, runs the jukebox. You hand over a YouTube link, the video plays on the screen on the wall and the whole bar listens to it at the same time.

Behind the scenes
On screen, all of this looks simple. But an island can hold ten players, and they all have to see the same thing at the same moment. So the server is in charge. Thirty times a second, it moves the players based on the keys they send, drives the cars and approves purchases. When I press E in front of a door, my browser just sends the key, and the server checks for itself what’s in front of me.
The problem is lag. If my character waited for the server’s answer at every step, the game would feel sluggish. So the browser guesses my steps ahead of time, with the same rules as the server, and corrects itself when the answer comes in. To keep the two in agreement, those rules only exist in one place, a folder shared by the server and the browser: walking, collisions, the reach of the E key, furnishing a house.
You can see it clearly in the house editor. When I try to put a sofa on top of another one, the editor says no right away, without waiting for the server, because it’s the same rule on both sides.

Voice and video, on the other hand, don’t go through my server. As soon as a player arrives, they’re connected directly to everyone else over WebRTC, with no library, and the server is only there to introduce them. After that, each player’s browser works out the distance on its own. It sends nothing to the players who are too far away, and sets the volume of the ones it can hear. I ruled out two other options. A media server was one more service to host, when ten players connect to each other just fine without one. And only opening connections when you walk up to someone left up to two seconds of silence every time two players met.
The jukebox and the traffic lights follow the room’s clock. The server remembers when the song started, and each player starts the video at the right second. The lights change at the same time for everyone, without a single message being sent.
As for the stack, the server is a single Bun process, with Colyseus for real-time, Better Auth for accounts, and SQLite and Drizzle for the data. The world is drawn with Phaser 4, and everything around it (menus, windows, shops) with React 19 and Tailwind. It all runs in a container on my VPS, under Dokploy.
Three versions
I didn’t land on this architecture on the first try. Nooki is on its third version.
I made the first one in a single day, on July 3. A map, click to move, multiplayer, accounts and already proximity voice, with PixiJS and Elysia.
The second one took the whole summer. I switched to the LimeZu packs, and most of my time went into the map, with town generators, scripts to lay out the neighborhoods, and even a homemade map editor (more on that below). Meanwhile, each feature was moving forward separately, on its own branch: soccer, the jukebox, achievements, the market, trading, photos, mounts.
By the end of September, I’d open the project and couldn’t read any of it anymore. It was spaghetti code, with no rules and no direction, and I was starting to get tired of the game myself.
First, I deleted all the map generators. Their checking tool was flagging problems all over a town I had since touched up by hand, so it was no use anymore. Then on September 28, I merged all the branches one last time and deleted everything, over 75,000 lines. I only kept the map sources and the images.
This time, I started with a spec. I wrote down all my ideas in it, and for each one exactly what I wanted. Starting over from scratch pulled me back into the game, and made me really choose what I wanted.
Here’s the intersection by the construction site in v2:

And the same one today, with the new interface and a town touched up by hand:

Code you understand on the first read
After v2, I set myself a rule: anyone who opens the code should understand it on the first read, without knowing its history. The repo stays private for now, because some things in it don’t belong to me and I’m not allowed to share them.
Every important decision is written down in an ADR, a short document that says what we decided, how to add something in that area, and what we ruled out, like the two options for voice. There are about twenty of them, from how the code is organized to the players’ cars. Before working on traffic, a session reads the traffic ADR, so it won’t retry an idea that was already dropped.
Three days after starting over, the game worked, but the code was hard to read. I kicked off a big cleanup: files under 200 lines, one responsibility per class. Except that, applied to the letter, the rule produced classes that held nothing and functions with too many parameters. By splitting everything up, we had added complexity instead of removing it. One big pull request set it all straight: whatever queries the island became a method of the island, and a simple calculation became a function. Since then, the rule has been “no class without its own state”.
The tests got the same treatment. They waited for the island’s real ticks and hashed a password on every sign-up, so the suite took three minutes. With a fake clock that jumps straight from one event to the next, and no hashing in tests, it went down to about thirty seconds, without touching the game code.
Working with Claude
I didn’t type any of this code myself. On Paradise, I had let Claude Code write everything, and by the end I didn’t know how the project worked anymore. Nooki is written entirely with Claude too, but this time I keep it on track. Everything I expect from a session is written in the repo, and every time I correct something, it becomes a rule that the next session will read.
| What keeps a session on track | Where it’s written |
|---|---|
| What we build, in order | The spec, one layer at a time. A box gets ticked when the feature is done and verified |
| How we write the code | The repo’s instructions, and lint rules for whatever can be checked |
| How the code is organized | The ADRs, to read before touching their topic |
| How we design a map | The map skill |
| When it’s done | Lint, types and tests green, then a check in the browser or on a screenshot |
For each task in the spec, I have Claude write the prompt, following the Opus 5.5 prompting guides. Each prompt goes to its own session, sometimes several at once, and then the session asks me to review. That’s where I give the most feedback, on code quality, comments or a bit of code that serves no purpose. Throughout development, I also kept repeating the same instruction: “the game isn’t in production, we can break everything”. Without that, a session plans migrations for data that doesn’t exist.
Of course, it doesn’t always work. Some sessions brought back useless classes despite the written rule, just because the files next to them had some. Every brief now restates the rules first, and asks the session to review its own diff before committing. Twice, Claude also told me it had fixed a display bug without having reproduced it. The bug only showed up in a browser without hardware acceleration, like mine.
Throwaway tools
What changed the most for me is being able to make a tool for one specific need, in an evening. Before, that would have taken me weeks, and I’d never have started on something that’s only useful for a few days. Now I have it built, I use it, and I delete it when I don’t need it anymore.
One night in July, between midnight and two in the morning, I had a full map editor built, with ground, roads, objects, zones and copy-paste. That same morning, I was drawing a new island with it. Five days later, I deleted it and went back to Tiled. In the meantime, I had also tried having AI generate the neighborhoods, with a loop where a model reviews a screenshot of the result. About ten neighborhoods came out that afternoon, and that same evening I deleted it all to build the town by hand. One day was enough to know it wasn’t the right way.
Collisions were the biggest job of that kind. Most of the graphics come from LimeZu’s packs, which I bought, and I’ll take this chance to say a huge thank you to LimeZu for all that work, for the quality of these packs, for everything. The rest of the game’s credits are on its credits page. For every object in those packs, I had to say what blocks the way. The game is seen in three-quarter view, so on the ground a house only takes up the shape of its roof, set at the foot of its facade. When you walk behind it, the facade hides your legs, and you never walk into it.

In red, what actually blocks you on those same houses. Flowers, foliage and the tops of the roofs let you through, and a fence or a street lamp only blocks at its base. I didn’t want a player to get stuck because of some bit of scenery.

For these masks, I set up a two-stage workflow: a first model suggests the mask for an object, and a second one looks for what’s wrong. Hundreds of agents ran, and the review caught quite a few mistakes. Then I went over everything myself, theme by theme, in a small tool made for the occasion. The tool was deleted with v2, but the masks are among the few things v3 kept.
Other tools stuck around. When Claude touches up a map, it shows me its renders on a page made for that, before and after. I draw arrows and numbered boxes on them, each with its comment, and a button sends it all back. The comments that kept coming back, like a piece of furniture that gets placed by its base or a fence on the wrong side, are now written in a skill that Claude reads before touching a map.
Drawing and music
What surprised me the most was the drawing. The backgrounds of the pages outside the game were drawn by Claude, pixel by pixel, each one by a script that lays down its layers one at a time. Almost every picture I asked for came out really well, so I turned them into a slideshow: the background shows the scene that matches the current time on the island.

The music comes from Claude too, with no audio files at all. Each track is a score written in code, which the browser plays live with the Web Audio API. Claude wrote the engine and the first track, then subagents composed the others in parallel, without ever being able to hear them. The harmony is worked out on paper, and each instrument’s volume is checked with measurements. On the first tracks, I barely had anything to change. Now there’s one for each time of day, plus a theme for the home page, a slow waltz for the hotel and a little tune for the shops.
What’s still mine
Today, Claude can do pretty much everything: the code, the maps, the drawings, the music. But the starting idea and the final call are still mine. For the hotel, it suggested five interiors, from Art Deco to a ryokan, and I kept the Belle Époque grand hotel. For the island map, it made me several designs, all good, but none of them fit the style of the game. In the end, the map simply shows the real image of the island, lit by the current sky.

To make those calls, you still need to know your job: which libraries exist and what their flaws are, what makes code maintainable, how to host an app, where the security holes hide and how to guard against them. Without that, I might have had the same game on screen, but every change would have broken something else, and the project would have been Claude’s more than mine. I never asked for “make me a game where friends can hang out”. I chose every part of it from A to Z, and that’s why I really consider Nooki mine.
Where the project stands
Nooki has been live since October 19, a little over three months after the first line of code. Next, I’m sticking to the order of the spec: the other mini-games (soccer, racing, hide-and-seek, quiz), the shops that are still closed, mounts, then screen sharing and trading between players.