Peer-to-peer web video chat app
- System Overview
- High-Level Architecture
- Core Components
- Data Flow
- Database Design
- Key Design Decisions
live & usage:
you can access this project via a custom domain: https://waves.cam
the .cam domain is used since the .com variant is already occupied.
- allow camera and microphone permissions when prompted
- ensure your browser supports modern WebRTC features
- use a modern browser (Chrome, Safari, Firefox, etc.) for best experience
features:
A real-time communication platform supporting:
- User authentication (email/password + Google OAuth2)
- Friend management (search, request, accept, reject, unfriend)
- Persistent 1:1 messaging via WebSocket + DB
- P2P video/audio calls via WebRTC
- Screen sharing and media recording
Handles all stateless operations:
- Auth: signup, login, Google OAuth2 callback, JWT issuance
- Friend management: search users, send/accept/reject/unfriend
- Conversations: fetch message history, persist new messages
Maintains a live registry of authenticated connections:
Map<UserId, WebSocket>
Used for:
- Real-time message delivery
- WebRTC call signaling (offer / answer / ICE candidates)
- Call control events (ring, accept, reject, drop)
Handles all media directly between peers:
- RTCPeerConnection for video/audio
- Separate RTCPeerConnection for screen sharing (if concurrent)
- RTCDataChannel for in-call chat (no DB writes)
- Signaling is relayed through the WebSocket server
Persistent store for users, relationships, and message history. Video calls produce no DB writes.
Third-party identity provider. Backend validates the token returned by Google and issues its own JWT.
Key relationships:
- Accepting a friend request atomically creates both a
friendsrow and aconversationsrow (transactional) - Each conversation is strictly 1:1 (two users)
- Messages reference conversation, not individual users directly
- Video call data is never persisted — all in-memory, session-scoped
| Decision | Choice | Reason |
|---|---|---|
| Auth mechanism | JWT in HTTP cookie | Stateless, works across REST and WS handshake |
| Real-time transport | WebSocket | Persistent, low-overhead, bidirectional |
| Video/audio | WebRTC P2P | No media relay server needed; lower latency |
| Call signaling | WebSocket server | Already established; no extra infrastructure |
| In-call chat | RTCDataChannel | No DB writes; ephemeral, zero latency |
| Friend accept | DB transaction | Atomically creates friends + conversations rows |
| Offline messages | DB fallback | Message persisted to DB; receiver fetches on reconnect |
| Screen share | Separate RTCPeerConnection | Keeps media tracks isolated and independently controllable |
Install bun (https://bun.sh/), then run:
bun install
Create a .env.dev file in the root of the project:
PORT=3000
NODE_ENV=dev
ACCESS_TYPE=offline
DATABASE_URL=./whatever.db
APP_BASE_URL=http://localhost:3000
GOOGLE_CLIENT_ID=your_google_oauth2.0_client_id
GOOGLE_CLIENT_SECRET=your_google__oauth2.0_client_secret
JWT_SECRET=your_jwt_secret
bun run migrate # create the database
bun run build:browser:dev # build the browser bundle
bun run dev # start the dev server
Visit http://localhost:3000
tech stack:






