/ concept-collection / commonroom
concept-collection / commonroom
commonroom
Go to fileHistoryFork
.githubServerless group video calls: rooms, WebRTC mesh, shared settings
srcAdd spotlight view, room chat, and clearer media-failure messages
.gitignoreServerless group video calls: rooms, WebRTC mesh, shared settings
CLAUDE.mdAdd spotlight view, room chat, and clearer media-failure messages
index.htmlServerless group video calls: rooms, WebRTC mesh, shared settings
package-lock.jsonServerless group video calls: rooms, WebRTC mesh, shared settings
package.jsonServerless group video calls: rooms, WebRTC mesh, shared settings
README.mdAdd spotlight view, room chat, and clearer media-failure messages
tsconfig.jsonServerless group video calls: rooms, WebRTC mesh, shared settings
tsconfig.tsbuildinfoServerless group video calls: rooms, WebRTC mesh, shared settings
vite.config.tsServerless group video calls: rooms, WebRTC mesh, shared settings

commonroom#

Serverless group video calls in the browser.

Live page: https://concept-collection.github.io/commonroom/

Enter your name and a room name — any string you like (no spaces) — and you're in. Share the room URL with anyone; everyone who joins the same room is connected to everyone else over a full WebRTC mesh, up to 8 people. You enter with your microphone and camera off and turn them on when you're ready. Click any tile to enlarge it (the rest shrink to a filmstrip); click it again or press Esc to return to the gallery.

The room has shared settings that anyone can change and that apply to everyone — currently the video quality (low / medium / high / auto, medium by default). You can also share your screen in place of your camera, and there's a room chat (with join/left notices and clickable links) that is as ephemeral as the call itself: you only see what's said while you're in the room, and nothing is stored anywhere.

How it works#

There is no backend and no room registry. The techniques come from the sibling projects commonview (the auto-connecting mesh) and commoncall (WebRTC media, quality presets, screen share):

  • Identity — each browser generates a secp256k1 (BIP340 schnorr) keypair, persisted in localStorage. The x-only public key is the peer ID, and every nostr event is signed with it, so peers can't be impersonated.
  • Rooms — the room name is hashed into a nostr topic; knowing the name IS the key. Everyone in the room announces {peerId, name} on that topic every few seconds via ephemeral events on public relays; entries expire when announcements stop.
  • Mesh — being in the room is the consent: every participant automatically brings up a WebRTC connection with every other participant (deterministic initiator = smaller peer ID; offer/answer/ICE ride per-peer nostr topics). Audio/video flows directly between browsers, with public STUN servers and a free TURN relay as fallback. Rooms are softly capped at 8 — peers already at capacity turn newcomers away.
  • Muted by default — camera/mic are requested on entry so unmuting is instant, but tracks start disabled. If you deny access you still join, sending silent/black placeholder tracks; unmuting retries the device and upgrades the track in place (replaceTrack, no renegotiation).
  • Shared settings — one settings object for the whole room, synced over the per-peer control data channels with per-key last-writer-wins (revision counters; ties resolved by the setter's peer ID). The video-quality presets map to RTCRtpSender.setParameters caps that each participant applies to its own outgoing senders.

Development#

npm install
npm run dev

Identity is per-browser-profile (localStorage), so two tabs in the same profile are the same peer — to try a room with multiple participants, use a second browser or a private window.

npm run build type-checks and bundles to dist/. Pushes to main deploy to GitHub Pages via .github/workflows/deploy.yml.