WORK//03
Incogni.tv
Anonymous one-to-one video chat with matching, skip and report.
- Type
- Real-time web app
- Platform
- Web · React + Node.js
- Role
- Full-stack: React client, WebRTC and the Socket.IO signalling server
- Year
- 2026
Overview
Incogni.tv pairs two strangers for a one-to-one video chat, in the style of Omegle or OmeTV. You confirm your camera and microphone, join a queue, get matched, and talk peer-to-peer, with text chat, skip and report alongside the video.
The interesting work is everything around the video itself: matching people fairly, setting up a direct connection between two browsers, and handling the many ways that connection can fail.
The problem
Browsers can stream video to each other directly, but they can't find each other on their own. They need a signalling server to exchange offers, answers and network candidates. They also need a fallback when a strict NAT or mobile network blocks a direct path.
What I built
- 18+ confirmation and a camera and microphone permission check before entering
- Anonymous queue with live position and random partner matching
- Peer-to-peer video and audio over WebRTC, with text chat capped at 500 characters on the server
- Skip to leave a chat instantly, and report to flag a user and be rematched
- Live stats for people online, waiting and in active sessions, plus a /health endpoint
Architecture & key decisions
A Socket.IO signalling server
An Express and Socket.IO server keeps a waiting queue and a session map. When two people are free it puts them in a room and tells exactly one of them to create the WebRTC offer, which avoids both sides offering at once.
Queued ICE candidates
Network candidates can arrive before the remote description is set. The client buffers them and flushes the queue once the offer or answer lands, which removes a whole class of “connected but no video” bugs.
STUN by default, TURN when configured
Five public STUN servers handle most networks. A TURN server can be added through environment variables for users behind strict NATs, where a direct connection is impossible.
Split hosting
The static React client deploys to Vercel. The long-running WebSocket server runs on Render, because serverless functions can't hold WebSocket connections open.
Challenges
Matched, but no video
On some mobile networks both sides connect to the signalling server but the media never flows. STUN isn't enough there, so the app supports a TURN relay, and the README documents why it's needed for a real launch.
Safety for a public launch
Reports are stored in memory today. The README lists what a real launch would need: persistent moderation storage, rate limits, bans and automated content detection.
Outcome
A working real-time product that taught me WebRTC from the ground up: signalling, ICE, STUN/TURN and connection lifecycles, plus how to deploy a stateful WebSocket server.