A serverless, Zoom-like video conferencing web app,
built with React, TypeScript, MUI, and PeerJS on top of WebRTC.
Deutsch · English · Español · Français · 日本語 · 한국어 · Português · Русский · 中文
👉 Try it online: https://www.predatorray.me/rendezvous/
There is no application server — the host of each meeting acts as a relay hub for chat messages and media streams, so each participant only maintains connections to the host instead of every other participant. The PeerJS public broker is used only for the initial WebRTC signaling.
Rendezvous is named after the Rendezvous Lodge atop Blackcomb Mountain in Whistler Village — the spot where the author meets up with skier friends.
- Pick a name, host a meeting, or join an existing one by code or link
- 6-letter human-readable meeting codes (~300M combinations)
- Tile-based video grid with auto-layout
- Tile shows the participant’s initials when their camera is off
- Mute / unmute audio, start / stop video (mute icon shown on the tile)
- Collapsible right-side chat drawer with timestamps and join/leave notices
- Chat history is preserved by the host so late joiners see prior messages
- Sharable invite link and copy-able meeting code
- Host leaving ends the meeting for everyone
- No accounts, no passcodes, fully static-site deployable
- React 19 + TypeScript (Create React App)
- MUI v7 (dark, minimalist Zoom-inspired theme)
- React Router v7 (
HashRouterfor static hosting) - PeerJS for signaling and WebRTC orchestration
gh-pagesfor GitHub Pages deployment
npm install
npm startOpen http://localhost:3000. To test multi-party meetings open additional incognito windows and use the same meeting code.
npm run buildOutputs a static bundle in build/ ready to be served from any CDN. The
app uses HashRouter, so it works on hosts that don’t support
client-side SPA rewrites (e.g. GitHub Pages).
-
Add a
homepagefield topackage.jsonpointing at your Pages URL:"homepage": "https://YOUR_USER.github.io/rendezvous"
-
Push to GitHub, then run:
npm run deploy
This builds and pushes the
build/directory to thegh-pagesbranch usinggh-pages. Enable Pages from thegh-pagesbranch in repo Settings → Pages.
src/peer/MeetingClient.ts— owns the PeerJSPeerand implements both host (relay) and client behaviors.src/peer/useMeeting.ts— React hook that adapts the meeting client to component state.src/types.ts— shared types and the wire protocol carried over PeerJSDataConnections.src/pages/— Home and Meeting pages.src/components/—VideoGrid,VideoTile,ChatDrawer,Controls,ShareDialog.
Messages exchanged over the data connection between a client and the host:
| Type | Direction | Purpose |
|---|---|---|
hello |
client → host | Sent on connect with the participant’s name |
welcome |
host → client | Returns assigned id, roster, and timeline |
roster |
host → all | Updated member list (joins, leaves, state) |
chat-send |
client → host | New chat message draft |
timeline |
host → all | Authoritative chat or system event |
state |
client → host | Participant changed audio/video |
end |
host → all | Host is leaving — meeting is over |
Each participant places exactly one outbound media call to the host carrying their own stream. The host accepts and:
- Calls every other connected client with that incoming stream,
tagged with
metadata.peerIdso the receiver knows which participant it represents. - Pushes its own stream and every existing remote stream to a new client when they join.
This gives every client a constant number of signalling sessions with the host (one data connection + N media connections), avoiding the classic O(N²) mesh.
flowchart LR
A[Client A] -- stream A --> H((Host))
B[Client B] -- stream B --> H
C[Client C] -- stream C --> H
H -- streams B, C --> A
H -- streams A, C --> B
H -- streams A, B --> C
- The host’s upstream bandwidth bounds meeting size (relay is on a consumer-grade browser tab).
- Forwarding remote tracks through the host re-encodes them; quality is
limited to what
getUserMediaand the browser’s WebRTC stack negotiate. - Default PeerJS broker is used; for production you can host your own
PeerServer and pass it to the
Peerconstructor. - The "serverless" property only holds when every participant can establish a direct peer-to-peer connection (host candidates, or server-reflexive candidates obtained via STUN for endpoints behind cone NATs). If any participant sits behind a symmetric NAT, ICE cannot negotiate a direct path, and media/data are relayed through a TURN server — meaning that traffic is proxied by a third-party server rather than flowing directly between peers.

