Skip to content

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.