blog

one lightbulb shared across the whole globe

ProjectGoSvelteKit

An easy project it seems? I made it into an overkill production grade application to create the truly global lightbulb.

one lightbulb shared across the whole globe
Read the article below

How great would it be if we had a global lightbulb in this world, nobody said ever. Well actually I said it (maybe not out loud), and decided to make it happen.

And now we have it: a truly global lightbulb. You can turn the lightbulb on in Amsterdam while someone from Hong Kong is watching it turn on in real time.

But how did I exactly make it, and did I go too far?

Idea

Let’s start from the basics. I wanted to have a website where people could turn on a lightbulb, no matter where they were in the world, and see it turn on in real time.

The implications: zero The possibilities: endless

It is the perfect project to practice WebSockets, because WebSockets are goated.

Plan

The plan was simple: 1 atomic shared boolean living on a backend. Everyone on the website has an active WebSocket connection and can make a request to toggle the lightbulb. To prevent bots from creating an epileptic seizure machine, there would be a cooldown of 10 seconds between toggles.

After toggling the lightbulb, you could input why exactly you wanted to toggle it, or just write “hi from [place on earth]” I guess.

Tech Stack

For this project, I wanted to work with my favorite tech stack: Go & SvelteKit. These are 2 technologies I have the most experience with, and that I enjoy using. They are also very performant, and if you know me, you know I like performance.

I picked gin-gonic as my API framework with gorilla/websocket, as I’ve found these nice to work with in the past. For the database layer, I tried something new. For a previous project I used GORM, but this isn’t always the best choice. So I decided to use sqlc, where you write raw SQL queries & migrations and it compiles them into type-safe Go code. This way you know your Go code is valid because it is generated by a battle-tested SQL-to-Go generator.

Furthermore, I decided to check out Lefthook and use my default linting & formatting tools alongside GitHub Workflows (thanks for the free workflow runs, Microsoft).

Let’s Program

I started by creating the folder structure & necessary files. Right from the beginning, I added the GitHub Workflow files (copied from another project) and worked on improving them.

I created the program in multiple small steps (very agile).

1. Database

I put my hands to sqlc, trying so hard to remember anything from my Data Essentials classes. Because it was a new tool, I did some tinkering on how to add migrations & correctly structure the codebase.

Then you just run one command and sqlc generates all the Go boilerplate code for you, like magic. And I don’t have to write unit tests for it, because it is compiler-proven code ;)

It also doesn’t require a C compiler because it uses a pure Go SQLite driver. Perfect for cross compilation!

2. Basic API

The goal was to keep it basic: no history yet, just the toggling & WebSocket implementation. Also no frontend yet; everything was validated by unit tests & integration tests. This way I could almost guarantee that it would work perfectly.

One of the important parts is that people can only flick the lamp once every 10 seconds. I implemented this by keeping track of a mix between the IP address and an anonymous device ID in the user’s cookies. I used hashing with a sprinkle of salt on the IP address + cookie to make sure that no PIIs are stored on my application. This means the lamp isn’t strictly IP-locked, so you can still toggle the lamp even if your brother has already toggled it.

3. Frontend

Time for some frontend programming in my favorite framework. I customized a lightbulb SVG I found on the internet so it could have different states and animate smoothly between them using CSS transitions and glow filters. I made this into a reusable Svelte component.

4. History

Next up, I added the history page. I wanted it to have lazy loading (infinite scrolling), something I hadn’t made before. The concept is really simple: you keep a cursor/pointer to wherever you are and request the next batch of toggles.

5. Reasons

A nice gimmick is that people could leave a reason for why they flicked the lamp. This of course comes with some caveats. People could curse, try to hack the database, or just write absolute nonsense. I can’t stop the nonsense part… The DB is easy—who even writes SQL injections in the big 26? Not me (I hope). For the cursing, I found a small Go library (go-away) that strips out profanity and handles leetspeak like “sh!t” & ”@$$hole”.

6. Viewer Counter

A final gimmick I didn’t plan for earlier was a real-time viewer counter. This way, visitors can see how many people are actually watching the bulb in real time. I also added a funny rotating text status to keep it playful & interesting.

Testing & Deploying

Testing is handled via GitHub Workflows. For every feature or fix, I create a pull request to let all the workflows run and verify code quality. Currently I test in prod, but in the future I’ll add a staging website.

The deployment story is pretty neat. Go has a built-in feature (embed.FS) to embed static files into the compiled binary. This means I can build the SvelteKit frontend as static files and package them directly into the Go binary. This allows me to run just one single binary (or Docker container) on my server. The container build is super lightweight—pure Go running on an Alpine base image—sitting at just 20MB in production! You wouldn’t even notice it running on your server.

Host it Yourself

Yes, you are free to self-host the project. You can find the complete source code on GitHub.

Future

There are still some fun plans for the project, so stay tuned ;)

ennl