Siddharth Ramakrishnan

Writing

Learning from First Principles

July 16, 2025

Sometimes learning from first principles thinker isn't all what it's cracked up to be.

I was working on 2 projects that I thought were going to be fun, but turned out to be a mix of both pain and fun (maybe that's what all projects end up being?). I was building a real-time, collaborative sudoku game, and a real-time, collaborative minesweeper game (hey, I really wanted to play some of my favorite games with my friends).

I figured I'd start out with a Firebase instance since for everything I've used it for, it's really fast, supports auth out of the box, and could run without me managing my own instance. Seemed like a win-win-win.

I started with the sudoku game (Sudokrew). The idea was simple: a black and white sudoku app where you could create a new game and share the link with friends to play. Each person who joined got a random color assigned to them, and when they filled in a tile, the tile took on that color and there was a score counter at the bottom which showed who had filled in the most squares.

I got everything set up, polished the UI, had all the backend functions to create and update the game hooked up, and things seemed like they worked! Well, almost worked. When I played alone, everything seemed smooth, but when you played with other people there were a few edge cases where things just were off.

If you and a friend filled in the same tile at the same time, there were multiple weird edge cases:

The whole game here was running through Firebase directly, and while I thought the realtime database would be fast enough, it proved to still be too slow for a real, real-time game. Everything happened in under 1 second (data getting sent to Firebase, Firebase updating, Firebase kicking off a callback to update the clients), but for a gaming experience that was sometimes too slow.

I tried the same thing with the minesweeper game (Bombsquad, yes I am good with naming things), and again the same thing happened. Users would clear the same area and there would be conflicts on how the area was colored, what updated, or who got the points.

Eventually after like a week of just tweaking things and praying it would resolve itself, I just scrapped everything and decided to take the "basic" route. I changed the code to run in memory instead of hitting Firebase, spun up an AWS instance, and then just deployed the game (with some minor annoyances around nginx proxying and HTTPS... but that's a story for another time).

This time, everything worked seamlessly. Since the board was just in memory, updates happened in real time. Each player was hooked up via sockets to the machine directly, so they saw as little latency as possible (< 50ms on top of their internet latency). I also chose US-West-1 since everyone I was playing with was on the West Coast. I had optimistic updating on the client side, but any changes were pretty immediately surfaced back to the user from the server if necessary. It felt so nice to be able to play and really feel like all the players were actually in the same room.

A few months passed by, and I thought about this again and ended up looking up how actual online games make everything work with low latency. Rocket League is much more complicated than a sudoku app with 20 total users. That is when I found out that I could have saved weeks of time if I just looked this up at the beginning of my journey. Games have dedicated servers for games where clients connect to. Changes update optimistically locally, but then propagate globally from the server if necessary. The only difference is a UDP connection instead of TCP + sockets.

I successfully reinvented the wheel (kind of), but next time I'm going to do a better job searching before a start a project.