Too loud?

Keyboard shortcuts

Anywhere on the site, while you are not typing.

M
Sounds on or off
E
Copy my email
T
Day or night
S
Change the season
D
Developer mode
?
These shortcuts
Esc
Close what is open

  • take-home
  • 2025
  • WebRTC
  • Solo

Kernel was the take-home for the role I have now. The brief was anonymous, room-based chat; I added peer-to-peer voice calls and delivered it twelve hours before the deadline, built, styled and deployed.

The server's job is to introduce two browsers, then get out of the way. One Socket.IO server handles chat and signalling; for a call it only relays offers, answers and network candidates between two people, and never sees the audio.

The Kernel home page: join a private chat by code, or start a new one.
anonymous rooms
A room with three messages and the online users panel, with a call button beside the guest.
call anyone in the room
Two people in a peer to peer voice call, with mute and hang up controls.
in a call, peer to peer
Type
take-home
Role
Solo
When
Two days, Sep 2025
Stack
React, Socket.IO, WebRTC, MongoDB
Links
kernelchat.vercel.appCode

What I built

  • Anonymous rooms

    Room-based chat with the history kept per room, and no accounts at all.

  • Peer-to-peer voice

    Calls I added beyond the brief, with the audio going straight from browser to browser.

    How it works
  • A signalling server

    One Socket.IO server relays offers, answers and candidates, and never touches the audio.

    How it works
  • A handshake that can't race

    Candidates that arrive early wait until the other side's description lands.

    How it works
  • Delivered early

    Built, styled and deployed in two days, twelve hours before the deadline.

How it fits together

Callerbrowser ASocket.IOchat + signalsCalleebrowser Bofferanswerice candidates, queued until the answer landsaudio, peer to peerthe server is not in the pathTURN relays where networks block direct links
The server introduces two browsers and gets out of the way: it relays the handshake, and the audio never passes through it.

Only the handshake

For a call the server does three things, and each is a relay: the offer goes to the callee, the answer comes back, and network candidates pass between them.

server/src/index.js

socket.on("call-user", (payload) => {  const { to, from, offer } = payload;  io.to(to).emit("call-made", { offer, from });});socket.on("make-answer", (data) => {  const { to, answer } = data;  activeCalls[socket.roomId] = true;  io.in(socket.roomId).emit('call_in_progress');  io.to(to).emit("answer-made", { answer });});socket.on("ice-candidate", (data) => {  const { to, candidate } = data;  socket.to(to).emit("ice-candidate", { candidate });});
Nothing here reads a stream. Once the peers connect, the server is out of the call.

Candidates that arrive early

A network candidate can arrive before the other side's description is set, and adding it then fails. So early ones wait in a queue and are applied once the answer lands, and a fast network never races the handshake.

client/src/pages/ChatRoom.jsx

const iceCandidateQueue = [];const iceCandidateListener = (data) => {  if (peerConnectionRef.current) {    if (      peerConnectionRef.current.remoteDescription &&      peerConnectionRef.current.remoteDescription.type    ) {      peerConnectionRef.current.addIceCandidate(        new RTCIceCandidate(data.candidate)      );    } else {      iceCandidateQueue.push(data.candidate);    }  }};// Process queued ICE candidates after setting the// remote descriptionconst processIceCandidateQueue = () => {  while (iceCandidateQueue.length > 0) {    const candidate = iceCandidateQueue.shift();    peerConnectionRef.current.addIceCandidate(      new RTCIceCandidate(candidate)    );  }};
Queued, then drained in order the moment the remote description is set.
signalingsdp only
peer acaller
peer bcallee

handshake via the server

The handshake goes through the server. The audio does not.

What I'd change. It's one-to-one by design. At IABTM I later moved group audio to an SFU, for exactly the reason a mesh doesn't scale.