How I built a Multiplayer App in 3 days
I thought it was time to create a follow up of my previous post Get your idea deployed to prod already. In the post I detail how you can quickly bootstrap an app and get it deployed and ready in no-time. This is a loose retelling of how I followed my own advice and got an idea to prod.
The idea
Just before I went to sleep last Sunday I thought it would be fun to create an app for my friends and I where we can rate the Eurovision Song Contest participants live together. The idea was to have everyones votes being tallied up live as we make adjustments to the ratings. This led me into looking at Replicache as the solution. I've been interested in trying it out for a while now and this was the perfect project for it. I quickly threw together a sketch on Excalidraw so I would have something to go on for tomorrow.
Building
As the Monday began I started reading documentation and looking into the tech I wanted to use. For most of the stack I went with things I know. But for the multiplayer part I had to read up and understand the core concepts behind Replicache. While reading I came across their hosted service Reflect. This was a perfect fit as time was of the essence and I would not have to build my own Replicache backend! It's completely free for 1000 user hours every month. As this project is supposed to be used during the 5 hours of Eurovision every year we can calculate the maximum amount of users we can have online for the full duration by simple division.
$$\frac{1000hu}{5h} = 200u$$
200 users? We're totally good.
Bootstrapping the project
For the project bootstrapping I chose to base my project on Reflect's own React template. This decision came from the idea that I wanted to play around with it a bit first and get a proper understanding of it before I try to implement my idea. While playing around with the template I made new mutators and subscribers and got a feeling of how the service works. Great start!
Skeleton structure
The next step was setting up basic React and HTML elements to be the skeleton of my idea. I got all the fields I wanted up and a general structure of the main feature. Key here is not to put too much time into styling. I mostly used the css styles already included in the scaffolded project and added just a little bit for the rounded borders and layout to be possible.
Implementing the main feature
Now with the basic ui elements in place we can start implementing the features we care about. For me it was getting live updates when someone changes a rating. At first I built it without taking into consideration what song was being rated just to get something up. and running. And when I got that working I updated the data structure to handle the mutations based on id's and boom the main part of the application was working! I could now switch between different songs and add individual ratings to them while also seeing the updates in real time from another browser!
Adding the other bare necessities
Now that the main feature had been implemented I had two things left to do in regards to the bare necessities. I wanted some sort of "login" page and a scoreboard.
"Login" page
The login page would serve as a way for the user to identify themselves with a name and then join a specific room ID. I purposefully made this super simple, letting users choose whatever room ID they want and Reflect would handle creating the room gracefully with the users not noticing anything at all. For another user to join the same room they simply had to type the same room ID. This is possible since Reflect is using CloudFlares Durable Objects in the background, serverlessly handling the client states for me and I don't have to be worried about any cost as I'm within the limits of Reflect's free plan.
Scoreboard
The scoreboard should show the aggregate total of all the scores from all the users, ranked after either order in the show or the max average. This could surely be built in a more sophisticated way with a table where you could sort based on any attribute but I kept it simple. I render the list based on how many contributions have been rated and calculate the average for each then sort it.
Styling
All the functionality I intended is in place now! Time to make it look at least a little better. As I am not the best css:er nor very imaginative I gave a lot of the stylistic choices to ChatGPT. In essence I gave it the layout of the page with the current styles I had in place and then asked it to "pimp my ride"-it in the style of Eurovision. Color wise that seems to mean "add gradients". This works well enough for me!
Deploying
I was happy with this! Everything seems to work well while developing and it seems ready enough. First I created a production environment for my Reflect service by running npx reflect publish --app lagom-euro
and added the generated url to my .env. Then I ran sst init
to add sst ion to the project. I added a StaticSite resource and ran a deployment to my prod AWS account. sst deploy --stage production
. A production environment was now up and running!
Testing and fixing
The site was up and available via CloudFront. I started to show people and tested it with a few devices. Found some unintended behaviors and things that could be clarified. I added som help text and a button to show and hide the help.
Domain
Finally I bought a domain for it. I continue to name my projects after my favorite Swedish word "Lagom" which translates to "Just right". I attached the domain to my sst resource and boom the site was now available from lagomeurovision.com!
Observability
This is a bit late in the game I admit, but better late than never! I figured it could be cool to know how many visitors and rooms I have on this small little project. I went ahead and created an environment for this project on my Baselime account. Baselime is a serverless observability service, recently acquired by CloudFlare! I've used Baselime in a few of my smaller projects now and it's super nice to use. For this project I installed their React SDK and added a custom event that emits when a room is joined.
I then set up a dashboard where I'd have an overview of the page stats. During the night I had 8 unique rooms and people joined a room 29 times. Very reasonably the site was mostly used on phones with the iPhone hogging the spotlight at 21 counts and Android following on 9. Stats are cool.
Final take-aways
These are my personal key take-aways from this project:
Bootstrap your project to get up and running quickly
- In my case it meant using a Reflect scaffold to have the project structured from the start
Read the documentation
- It's way easier to build things if you understand how the technology works
Start with the main feature
Functionality first, style later
Ask for help if you have a question or get stuck
- I asked for help in both the Replicache Discord and Baselime Slack servers. In the Replicache server I got help from a community member. In the Baselime server I got help from one of the developers (Thanks Thomas!)
Leverage ChatGPT for things you don't necessarily care for (my case, a lot of css)
I like building for my friends
Thanks!
Thanks for taking the time to read or at least scroll through all of this. This post is both meant as an example of me eating my own dog food regarding my previous post but also as an exercise in writing. As a newer content creator/tech blogger/aws influencer I've yet to really find my voice. I think I enjoy this type of content quite a bit. I know I like reading about people's own little side projects or how someone built something very cool. If there is something you liked/disagreed/have a question about please write to me! A comment on the post or a direct message on LinkedIn or X are both welcome.
Here's the repo if you want to check it out.
If you enjoyed this post you could follow me on ๐ at @Paliago. I mostly engage with the serverless community and post pictures of my pets.
Elva is a serverless-first consulting company that can help you transform or begin your AWS journey for the future
Subscribe to my newsletter
Read articles from Alvin Johansson directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by