You are reading part one of a two-part series. Please consider reading part two after this.
Just before midnight a couple of days ago, I launched BassDrum, a Chrome extension to watch PrimeVideo in sync with your friends. It's 100% free and doesn't require you to sign up to use it.
BassDrum lets you create secret "rooms" that you can share with friends. When someone in the room changes to a video, plays or pauses it, it is synced to everyone in the "room" instantly. BassDrum was built to help people relive the days where we could still have friends over and have movie nights. (A little bit of context here: I'm writing this in the middle of the COVID-19 pandemic.)
In this post (part one), I'll detail my experience building BassDrum — what worked, what didn't, and the mistakes I made.
In part two, I'll talk about my experience launching it — how it was received, what I could have done better — and the lessons I learnt along the way.
I hope this is helpful to someone out there, who's trying to build something on their own!
Building BassDrum
BassDrum started out as an idea well over a couple of years ago. I have discussed it with multiple groups of friends since then, but we never really got around to building it or researching it further. Time went by, and one platform after another started coming up with "watch party" features. Whenever I thought about this idea, I told myself that I would definitely build this out one day, even if just for that sake of building it out.
The stack
Then one fine day a few weeks ago, I was learning ReactJS, and wanted a fun little project to practice it with. BassDrum (although I didn't call it that back then) seemed like a fitting idea for the task. I pitched this to a few friends, and they thought it was a good idea to build it — off to the races!
Initially, I tried building it as a regular React app (CRA). Turns out, this was not a good idea: the process for building a Chrome extension with React was not straightforward, and Webpack hit me in the face like a ton of bricks. I couldn't make head or tail of it, and couldn't wrap my head around the config files. This was way too many new things to handle at once, and I told myself I needed to tackle one problem at a time.
I pinged a colleague to ask if there was an easier way, and they suggested I use a boilerplate. A quick search revealed a couple of options. I tried both for ten minutes, and picked the one that seemed to make the most sense to me.
The first version
Here's an early prototype in action:
I wanted to do just one thing for the first version, and do it well. So I decided that I would implement the concept of "rooms", and build just the video syncing feature. I built the side-pane, added a few icons to make it look right, and matched the colours with the PrimeVideo theme. It was bloated, but it was alright.
I generated a UUID every time someone clicked "Create Room", and used that as the room's secret name. Anyone could join that room using its secret name (the UUID). Every user had a UUID too, and joining was simply a matter of adding that user's UUID to the relevant room.
I had no idea how to get the video's state, so I called my trusted front-end-ninja and buddy, Viknesh, and he helped me figure it out.
Using Firebase as the backend, I was ready with a working prototype by the end of the day. The functionality that Firebase's realtime database provided was exactly what I needed to sync data between the users in a room.
But, there was a problem.
Off to the races — literally
There was a significant lag in the sync, and it was painfully evident in my slow connection, playing two videos simultaneously (so that I could test the sync functionality). The lag was all the more evident because I was frequently updating the state with the video's current time as it was playing.
At this point, I was just using the React component's state
to keep track of the video the room was playing, and its current time. If someone paused their video, then by the time the change was received by all users, the sender had already received another instruction. The videos would sometimes go back and forth between two states, in an endless loop.
Yup, race condition.
I tried restructuring my data model to work around these race conditions. I asked on Twitter, and tried a few different approaches. I ended up re-writing the extension over six times, but all the variants were either too expensive (lots of data to sync) or didn't solve the problem (there was still a chance of race condition).
What ended up working was using Websockets as the communication channel, and a Starlette application to aggregate this data in the backend.
Mistakes and lessons
I think I spent too much time, too close, on the sync problem. At the peak of trying to solve it, I made little flash cards with all the problems and scenarios, spread them out on my bed, and kept looking at them for hours, trying to think of a solution. It would probably have been healthier to let it rest for a few days, and then take another shot at it. Harish — pushing me on as always, two years in a row now — and Viknesh listened to me rant on about this in true big-brother fashion, though. 😅
I was reluctant to switch tactics when my first shot was not working. Right from the outset, I wanted to focus on React, and that led to me spending a lot of time trying to make it work with Firebase. But hey, it was a good experience!
I'm proud of myself for not registering a domain name as soon as this idea popped into my head again. It was well after the first prototype was ready that I setup social media and bought the domain. Yet another domain saved from the zombie domain graveyard, yay!
It was extremely hard for me to get the sync working as I expected — I think this was partly because I'm self-taught, and I did not study any "hard" CS like algorithms. But I'm glad I kept at it even when it was too hard. The profound joy I experienced when it finally worked, was definitely worth it!
Also, lesson here for me: go back to the basics and learn some hard CS. Although I might not use it directly, I feel like it will give me good mental models to solve well-known hard problems like these.
It was overwhelming to see so much kindness from people cheering me on, even though this was just a side project! You guys know who you are — you are awesome, and I'm super-grateful! There's a small surprise coming your way soon! 😉
That's it for now. See you again in part two!