This is the story of how I ended up writing real-time video calling on raw WebRTC — but it doesn't start with WebRTC. It starts with an app nobody asked me to make that good.
The app nobody asked me to make that good
A company handed me an assignment: build a dream tracker. Log a dream, tag the emotion, done. I've genuinely forgotten their name — and they never spoke to me again, so it doesn't matter much.
But I can't stop at 5 when I know I can get to 100. So I followed the thread: dream → night → the sky. I built the entire app on a single living background — a starry-sky engine in React Native and Expo, some stars bright, some faint, the whole thing quietly breathing (the look was inspired by Grok's background). Moods became phases of the moon. The analytics came out genuinely beautiful.
Use ← / → to browse Swipe to browse
I sent them the codebase and the APK. Then — nothing. No reply. Ghosted. I wouldn't be shocked to find it living in their app somewhere. I was proud of it anyway.
(For the record, I've shipped plenty of duds too — a travel-ticket app that never quite worked, a couple of wallpaper apps that went nowhere. This one just happened to land.)
The Reddit post that changed the next year
So I did the only thing that made sense: I posted it on Reddit.
It blew up. More than two thousand people came to my site to see it. Some appreciated it, some consoled me about the ghosting, and a few told me to stop over-building — that going above and beyond isn't worth it. (That's just not who I am; I read it, kept the tab open, moved on.) Some asked for help designing their own apps. And a few reached out to actually work with me. One of them had seen something else on my site: Kabootar.
Kabootar, and why Zoom gets to charge
Kabootar (silly name, I know) was a side project from when I was learning React Native: login-less video calling, join by a PIN. It came from a stubborn question — WhatsApp and Zoom have video calling, WebRTC is an open protocol, and Google's STUN server is free. So why is Zoom charging me?
I spent three or four nights finding out. The whole thing ran on Firebase: both people type the same PIN, the offer, the answer, and the ICE candidates all get written under that PIN's document in Firestore, each device watches the document, and when both sides are present they exchange and connect. I trickled the ICE candidates through Firestore as they were discovered, instead of waiting for the full set.
It worked — but only about 80% of the time, and almost always on my own SIM. On some Wi-Fi it just wouldn't connect. I hadn't thought about backgrounding, reconnection, or dropped calls at all yet. It took me a while to even understand the first reason it failed:
That was the first time I felt the shape of this: the demo is the easy 20%. (Kabootar has its own page now — how it works, and the bugs I never fully fixed: Building Kabootar.)
The real job — making a call actually connect
The person who reached out needed exactly this. They'd seen Kabootar, and they'd seen the detail on Dream Space — the micro-animations, the eye for feel. It became a three-month remote role, building for an OTT actor (someone who's shared the screen with Pankaj Tripathi — that's as much as I can say). Everyone else was in-office; they let me work from anywhere.
The job was to make real-time video calling actually work, on WebRTC — and I walked in with exactly the 20% I'd learned on Kabootar: a call that connects, most of the time. The other 80% — the part that makes it feel seamless — I didn't have yet.
Getting a call to connect once, in a demo, is easy. Getting it to survive is not: backgrounding, reconnection, dropped calls, acks, the renegotiation when the network shifts under you mid-call. And the text chat that runs alongside the call taught me something I hadn't expected — you can't spin up a fresh connection for every little thing, so those messages ride the WebRTC data channel that's already open between the two peers. The socket does the signaling to get the connection up; once it's up, the data channel carries the rest. None of that shows up when you're just proving it works, and all of it decides whether a real person can hold a conversation. I learned it the way I learn everything: jump in the water, start swimming.
Giving interviews around the same time hammered the same lesson from the other side: theory and practice are two different things. You can explain ICE on a whiteboard and still watch a call die on a hotel Wi-Fi.
I also made it feel like a real product. I leaned on Apple's Human Interface Guidelines and WCAG for accessibility, and the app got maybe 10× better in feel. I added camera filters (a React Native library, predefined LUTs). For a separate interview that wanted proof I could handle sockets, I built a real-time chat between my Mac and my phone — my first proper WebSocket work, which fed straight back into this app's signaling. And partway through, we moved the client to LiveKit — but that's a post of its own.
What the surface was hiding
Every one of those gaps turned out to have a real answer I only picked up later. A call that has to ring you when the app is closed needs the operating system's help: on iOS an incoming call arrives as a VoIP push (PushKit) that wakes the app just long enough to present the call. The reason a WhatsApp or Instagram call looks and behaves like a normal phone call — full screen, on the lock screen, in your Recents — isn't the network; it's CallKit, the framework that hands VoIP apps the native call UI. Doing real work while backgrounded needs specific permissions and background modes, because iOS aggressively suspends apps to save battery. And the connection itself is never promised a straight line — ICE negotiates the best route it can find over whatever network you're on (Wi-Fi or cellular), and relays through TURN when it must.
I didn't know any of that while I was building. I learned it by working next to people who did, and by failing enough interviews that the gaps finally had names.
Walking away from the good thing
They were happy with the work. They wanted me long-term, remote, from anywhere — the kind of offer you're supposed to grab with both hands.
I let it go. Not because it wasn't good — because I could feel that most of what I still needed to learn comes from sitting next to people who are better than me. Remote and alone, I'd plateau. It was time to go back into a system, learn, and come back stronger.
You can only connect the dots looking backward
Steve Jobs said you can't connect the dots looking forward, only backward. I feel that now. If I hadn't thrown myself at random, ambitious, over-built things — the dream app nobody asked for, the calling app I had no business attempting — I'd be an average developer.
I'm still at an average job by the logos on the wall. Not MAANG. But on the inside, I don't budge when it gets hard. If there's one thing to take from this: take the challenge even when you know you'll probably fail. Do it anyway. That's the only way the dots show up later.




