
When most programmers think of a browser game, typically it would be rendered using the HTML canvas or if you’re a gray beard; flash. Either way, this unholy masterpiece I have created should send any experienced web dev into a coma.
What is HTMX?
HTMX is a javascript library for dynamic web content but unlike frameworks like react or vue, HTMX aims to be an anti-framework. Typically in a large javascript framework, you’d request some JSON blob and then build the UI using the data in that blob. In HTMX, it requests an HTML fragment with all data pre “hydrated”. This reduces the amount of compute overhead on the client side significantly.
How does it work?
The basic idea of how this game works is pretty simple. When the client initializes, it gets an HTML document containing all the triggers, the background, the pipes, and the player. Every ~30ms the client requests a new frame containing css styles with left and top styles to tell each object where to be on the screen.
There’s also some client logic to multiplex each user session to their game state. The physics are also handled server side so each time a session is initialized, it creates a new game loop for that session. This game loop handles collision events, physics for the player, the movement of the pipes and background.
Challenges of creating real time server side rendered games
One of the first issues I dealt with when creating this game was thread safety. When you’re running multiple game loops handling multiple requests coming in at inconsistent times, you have to account for the chance that a data structure may be accessed or modified concurrently. Personally, while working in dynamic languages, and “copy everything” languages for so many years, I didn’t have to deal with such things.
When creating this, I had to use concurrent data structures to both multiplex requests and handle multiple game state updates. I ended up using the go sync.Map to handle the multiple clients and a mutex for each game state struct. I also minimized the use of struct-allocated data and in favor of dynamically allocated struct fields i.e. pointers. This made it so that the likelihood of the same address being accessed at the same time is much lower.
Challenge number two: Network delay. For real-time gameplay in this way to be playable(30fps), it’s not exactly possible to buffer multiple frames in advance. Technically frame tweening could be possible but tuning in the correct animation delay was pretty difficult. There were issues with ghost colliders initially when I was prematurely optimizing, as you do. With the current implementation, it’s essentially required that you are at least within the same region as the server for some semblance of playability.
The baseline frame rate settles around 30 fps due to the default 33ms but with some extra network delay such as California to Virginia(where I’m at) it drops as low as 10-15 fps and this is on a pretty high spec PC. The way that this could be solved would be a regional deployment of the app which I did achieve privately on AWS. Terraform scripts in the GitHub.
Challenge number three: Deployment. When deploying a regular web app you can kick a client off when they have made too many requests within a certain timeframe. When the main functionality of the web app relies on making as many requests as possible, you can see why there would be a problem preventing abuse. Uncompressed each frame is about 2KB consuming ~4MB/minute/user and compressed ~1.6MB/minute/user. At a minimum, ~172GB uncompressed and 69GB per month compressed, assuming only one user on at a time, coming to $0.69/month/concurrent user.
This isn’t even considering the potential abuse vectors such as DOS attacks, one user could request ~3,000 frames a second in its current state coming out to ~518GB a day. Costing me $5.18 per day or $155.52 per month in just egress data. Call me paranoid but that’s insane.
In the future, I should have a good solution to fix this issue. Frame throttling, limiting playtime, or just implementing a max fps are my first thoughts. Let me know what you think in the comments.
WHY WOULD YOU DO THIS?
I did this project to explore a different way of thinking. As someone who currently isn’t a professional developer but wants to be, I’m always looking for ways to expose myself to more enterprise tech. Combined, the services I have in production get roughly ~1000-1500 unique users per month, definitely not enough to worry about auto-scaling compute instances and being buried by Bezos himself. I’d encourage anybody looking to gain experience in software to do something ridiculous every once in a while. When you do ridiculous things, you get ridiculous problems that you wouldn’t have had to think about otherwise.
Star the repo on Git Hub! Thanks!!!

Leave a comment