A few weeks ago I had a weird dream.

It was weird because it was 1.) a work dream (usually not my fave) and 2.) goofy as hell.
In it, I was celebrating with my team in a cozy holiday app. I existed as a cursor, a retro/pixelated avatar version of myself, leaving notes in a virtual guestbook. When I woke up, I first decided I needed to spend less time on the internet.
But shortly after that I decided it was kind of a cozy dream, and my mind kept drifting to wanting to build a similar experience IRL.
This year has been special for my team. We got acquired. There's been chaos and uncertainty, but also growth and camaraderie. We typically do a sort of "Year in Review" powerpoint thing, recapping highlights and all that.
Between my desire to not write some slides and the nerd-sniping of my dream, I set to work trying to make something more fun than our usual powerpoint.
Play #
"If you want creative workers, give them enough time to play." — John Cleese
Lately I've been obsessed with this cute language, gleam. I like it for a lot of reasons, in particular the small surface area (you can learn it in an afternoon), excellent language server, and the fact that it compiles to Erlang (and JS lol).
I didn't have any experience with the BEAM, and this felt like a good project to test things out.
The architecture split naturally: SvelteKit on the front (auth, SSR, the timeline), Gleam for the "alive" stuff (realtime presence, cursor positions, reactions). SvelteKit I know well; it gets out of the way and lets me move fast. Gleam was the adventure.
Why Gleam/Erlang for a side project? The BEAM (Erlang's virtual machine) was built for telecom—millions of tiny conversations happening simultaneously. That's exactly what realtime multiplayer feels like: cursors gossiping across screens, presence pinging every 50ms, reactions bubbling up etc. The actor model handles this elegantly (supposedly, again I'm new here hi).
I didn't spend time stress testing it, but I was able to easily spin up 20-30 sessions happening at once locally on an M1 without my CPU breaking a sweat. Sure, 20-30 sessions is nothing, but it's cool that it "just worked" without me having to think about what happens when any of those sessions failed or whatever.
On the frontend, Svelte 5 made the interactive bits easy to build. If you haven't spent much time in Svelte, I recommend giving it an honest try. There's lots of stuff I could talk about, but wanted to highlight the cursor physics:
// springs made easy a la Svelte
const springX = Spring.of(() => x, { stiffness: 0.12, damping: 0.7 });
const springY = Spring.of(() => y, { stiffness: 0.12, damping: 0.7 });
Those two lines smooth out the 50ms network updates into fluid motion. When you see someone else's cursor glide across the screen, that's spring physics interpolating between heartbeats.
I sprinkled in features: Konami code triggers confetti, there's a cookie clicker for "liking" events (why not 🤷♀️), floating emoji reactions that drift upward and fade. Each one exists purely because it made me smile while building it.
Ship Your Silly Thing #
Was it overengineered? Absolutely. It was also super fun.
Watching my teammates discover their pixelated avatars and trigger the Konami code made the whole thing worth it. After a year of hard work, it feels good to make something off-beat and mostly throwaway.
Highly recommend.