| .github | |
| src | |
| .gitignore | |
| CLAUDE.md | |
| index.html | |
| package-lock.json | |
| package.json | |
| README.md | |
| tsconfig.json | |
| tsconfig.tsbuildinfo | |
| vite.config.ts |
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.setParameterscaps 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.