Skip to content

SakshamSinghal20/Verve

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

124 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

╔══════════════════════════════════════════════════════════════════╗
║                                                                  ║
║    ██╗   ██╗███████╗██████╗ ██╗   ██╗███████╗                    ║
║    ██║   ██║██╔════╝██╔══██╗██║   ██║██╔════╝                    ║
║    ██║   ██║█████╗  ██████╔╝██║   ██║█████╗                      ║
║    ╚██╗ ██╔╝██╔══╝  ██╔══██╗╚██╗ ██╔╝██╔══╝                      ║
║     ╚████╔╝ ███████╗██║  ██║ ╚████╔╝ ███████╗                    ║
║      ╚═══╝  ╚══════╝╚═╝  ╚═╝  ╚═══╝  ╚══════╝                    ║
║                                                                  ║
║     Video calls that don't suck. An SDK that finally works.      ║
║                                                                  ║
╚══════════════════════════════════════════════════════════════════╝

Node.js React Mediasoup Socket.io MongoDB Vite


Crystal-clear video calls · Multi-tenant embeds · Developer SDK

Built with ❤️ by Saksham Singhal


🤔 What is Verve?

Verve started as a private, self-hosted video calling platform — like Zoom met WebRTC at a hackathon and had a baby that actually works.

It has since evolved into a hybrid communication SDK platform. You can embed Verve directly into any website via an <iframe>, or integrate it fully into any application using the Verve JavaScript SDK and prebuilt React components. All while keeping Mediasoup/WebRTC internals completely hidden from consumers.

Verve uses a Selective Forwarding Unit (SFU) architecture via Mediasoup — streams are intelligently routed through the server instead of P2P spaghetti. Translation: it scales, it's fast, and it doesn't melt your CPU.


✨ Feature Buffet

Pick everything. It's all-you-can-eat.

Core Meeting Features

Feature What it does
📹 Video & Audio HD video (720p) + crystal-clear audio with echo cancellation & noise suppression
🏠 Room System Create or join rooms with unique IDs. Share the code, start the party
💬 In-Call Chat Real-time messaging with unread badges, auto-scroll, and chat history (last 200 msgs)
🖥️ Screen Sharing One-click screen share. Auto-stops when you hit the browser's native stop button
👥 Participants Panel See who's in the room at a glance
Raise Hand Toggle raise hand with animated badge. Auto-lowers after 30s
🎙️ Media Controls Mute/unmute mic, toggle camera on/off, leave meeting gracefully
📐 Adaptive Grid Smart video layout that adapts for 1, 2, 4, or many participants
🌙 Dark Theme Sleek dark UI. Your eyes will thank you at 2 AM
🔐 Authentication JWT-based auth with bcrypt password hashing. Register, login, stay secure
🎭 Reactions Send floating emoji reactions during calls
⏱️ Focus Timer Host-controlled countdown timer visible to all participants
📌 Pin to Focus Pin any participant's stream to full-screen focus view
📊 Speaking Stats Track how long each participant has spoken

Phase 1 — Embeddable White-Label System

Feature What it does
🏢 Multi-Tenant Isolated rooms per tenant — guests from one tenant can never access another's rooms
🎨 Dynamic Branding Tenants configure logo & primary color; embed view applies them automatically via CSS vars
🔑 Embed Tokens Versioned, scoped JWTs (v1) bind a guest to a specific tenant + room
🔒 Origin Protection allowedOrigins enforcement — only whitelisted domains can embed your rooms
♻️ Auto Room Expiry MongoDB TTL index auto-deletes rooms after their configured lifetime
🪟 iframe Support Embed any tenant room with a single <iframe> tag

Phase 2 — Hybrid Communication SDK

Feature What it does
📦 npm-first SDK Verve.init() → clean session object. No Mediasoup knowledge required
🤝 State Machine Connection lifecycle: idle → connecting → connected → reconnecting → disconnected → destroyed
🎯 Stable Events Namespaced event contract: participant.joined, chat.message, room.ended, etc.
🧩 Prebuilt Components VerveRoom, VerveControls, VerveChat, VerveParticipants — ready to drop in
🎨 CSS Variable Theming Override --verve-primary, --verve-bg, --verve-text etc. on .verve-root
⚙️ Room Config Disable chat, camera, or screen share per session via config object
🔒 Framework Agnostic Core VerveSession has zero React imports — usable in Vue, Svelte, vanilla JS, or anywhere

🏗️ Architecture

                    ╭──────────────────────────────────╮
                    │          THE INTERNET            │
                    ╰──────────┬───────────┬───────────╯
                               │           │
                    ┌──────────▼──┐   ┌────▼──────────┐   ┌──────────────────┐
                    │  Browser A  │   │   Browser B   │   │  3rd-party App   │
                    │  (React)    │   │   (React)     │   │  (SDK / iframe)  │
                    └──────┬──────┘   └──────┬────────┘   └────────┬─────────┘
                           │                 │                     │
                      produce &          produce &           Verve SDK / iframe
                      consume            consume                   │
                           │                 │                     │
                    ┌──────▼─────────────────▼─────────────────────▼──────┐
                    │                                                       │
                    │             🚀 Verve Server (Node.js)               │
                    │                                                       │
                    │   ┌─────────────────┐   ┌───────────────────────┐    │
                    │   │  Mediasoup SFU  │   │  Tenant API           │    │
                    │   │  (WebRTC/RTP)   │   │  POST /rooms          │    │
                    │   └─────────────────┘   │  POST /rooms/:id/token│    │
                    │                         │  GET  /branding/:id   │    │
                    │   ┌─────────────────┐   └───────────────────────┘    │
                    │   │  Socket.io      │                                 │
                    │   │  Signaling +    │   ┌───────────────────────┐    │
                    │   │  Chat + Raise   │   │  JWT Auth Middleware  │    │
                    │   │  Hand + Embed   │   │  (DB users + guests)  │    │
                    │   └─────────────────┘   └───────────────────────┘    │
                    └───────────────────────────────┬───────────────────────┘
                                                    │
                                           ┌────────▼────────┐
                                           │  MongoDB Atlas  │
                                           │  Users, Tenants │
                                           │  Rooms (TTL)    │
                                           └─────────────────┘

SDK Integration Layers

Developer Code
     │
     ├── Path 1: iframe embed (Phase 1)
     │     <iframe src="/embed/ROOM?token=JWT" />
     │
     ├── Path 2: SDK headless (Phase 2)
     │     const session = await Verve.init({ apiKey, roomId, user })
     │     await session.joinRoom()
     │     session.on("participant.joined", handler)
     │
     └── Path 3: SDK prebuilt React (Phase 2)
           <VerveProvider session={session}>
             <VerveRoom />
           </VerveProvider>

All three paths use the same backend, same socket events, same Mediasoup stack.

Vanilla SDK Core (framework-agnostic)
    ↓
React Bindings / Prebuilt Components
    ↓
Existing RTC Core (untouched)
🧠 How does the SFU magic work?

Instead of every participant sending their video to every other participant (which is O(n²) chaos), Verve uses a Selective Forwarding Unit:

  1. You produce → Your camera/mic stream goes to the server (just once!)
  2. Server routes → Mediasoup intelligently forwards your stream to everyone else
  3. They consume → Other peers receive your stream from the server
Without SFU (Mesh — yikes):          With SFU (Verve — chef's kiss):

   A ←→ B                                A → Server → B
   A ←→ C                                A → Server → C

   Each client: N uploads!!! 🔥💀       Each client: 1 upload, N downloads
                                          Server handles the routing 🎯

🛠️ Tech Stack

Layer Tech Why?
🎨 Frontend React 19 + Vite 7 Blazing fast HMR, modern React with hooks
🎭 Styling Vanilla CSS + CSS Variables Pure hand-crafted CSS, SDK theming via custom properties
⚙️ Backend Node.js + Express 5 Battle-tested, async-first, runs everywhere
📡 Real-time Socket.io 4 WebSocket with fallbacks, rooms, and ack support
📹 Media Mediasoup 3 (SFU) Enterprise-grade WebRTC SFU. Handles the hard stuff
🗄️ Database MongoDB Atlas Cloud-native document store; TTL indexes for room cleanup
🔐 Auth JWT + bcrypt Stateless tokens (versioned v1) + salted password hashing
🧭 Routing React Router 7 Client-side navigation with /room, /embed, and dynamic routes
📦 SDK Vanilla JS (framework-agnostic) Works in React, Vue, Svelte, or plain JS apps

🚀 Quick Start

Time to first call: ~3 minutes ⏱️

Prerequisites

1️⃣ Clone & Install

git clone https://github.com/SakshamSinghal20/Verve.git
cd Verve

# Backend
cd Backend && npm install

# Frontend
cd ../Frontend/Live-calling && npm install

2️⃣ Environment Setup

📋 Backend (.env) — click to expand
cd Backend
cp .env.example .env

Edit Backend/.env:

Variable Default What it does
PORT 5000 Server port
FRONTEND_URL https://verve-live.vercel.app CORS origin
SERVER_IP 127.0.0.1 Mediasoup announced IP
MONGO_URI MongoDB connection string
JWT_SECRET verve-dev-secret-change-me Signs all JWTs (user & embed)
📋 Frontend (.env) — click to expand
cd Frontend/Live-calling
cp .env.example .env

Edit Frontend/Live-calling/.env:

Variable Default What it does
VITE_BACKEND_URL http://3.108.133.255:5000 Backend URL

3️⃣ Launch! 🚀

# Terminal 1 — Backend
cd Backend && node server.js

# Terminal 2 — Frontend
cd Frontend/Live-calling && npm run dev

4️⃣ Use it

  1. Open https://verve-live.vercel.app in your browser
  2. Register an account, then click ✦ Create Meeting
  3. Share the Room ID — they paste it in Join and you're live 🎉

💡 Open two browser tabs to test locally.


📁 Project Structure

Verve/
│
├── 📂 Backend/
│   ├── 🚀 server.js                ← SFU + Socket.io + Tenant isolation
│   ├── 📂 middleware/
│   │   └── 🔐 auth.js              ← JWT verification (DB users + embed guests)
│   ├── 📂 models/
│   │   ├── 👤 User.js              ← MongoDB user schema
│   │   ├── 🏢 Tenant.js            ← Multi-tenant schema + API key generator
│   │   └── 🚪 Room.js              ← Room schema with tenantId + TTL expiry
│   ├── 📂 routes/
│   │   ├── 🛣️ auth.js              ← Register & login endpoints
│   │   ├── 🚪 rooms.js             ← Room API: instant, custom, join
│   │   └── 🏢 tenant.js            ← Tenant API: rooms, tokens, branding
│   └── 📋 .env.example
│
├── 📂 Frontend/
│   └── 📂 Live-calling/
│       └── 📂 src/
│           ├── 🏠 App.jsx           ← Landing page
│           ├── ⚡ main.jsx          ← Router: /room, /embed, /login, /register
│           ├── 🔌 socket.js         ← Socket factory (supports embed tokens)
│           ├── 📂 context/
│           │   ├── AuthContext.jsx   ← User session context
│           │   └── EmbedContext.jsx  ← Embed mode + tenant context
│           ├── 📂 hooks/
│           │   └── 🎣 useRoomSocket.js ← All Mediasoup + socket logic
│           ├── 📂 pages/
│           │   ├── 📹 Room.jsx       ← Standard meeting room
│           │   ├── 🪟 EmbedRoom.jsx  ← Branded iframe-friendly room
│           │   ├── 🎨 EmbedRoom.css  ← Embed-specific styles
│           │   ├── 🔑 Login.jsx
│           │   └── 📝 Register.jsx
│           ├── 📂 components/
│           │   ├── VideoGrid.jsx     ← Adaptive video layout
│           │   ├── ControlBar.jsx    ← Mic, cam, screen, chat controls
│           │   ├── ChatPanel.jsx     ← Real-time chat panel
│           │   ├── ParticipantsPanel.jsx
│           │   ├── SpeakingStatsPanel.jsx
│           │   ├── TimerBar.jsx
│           │   ├── ReactionOverlay.jsx
│           │   └── Icons.jsx
│           ├── 📂 sdk/               ← 📦 Phase 2: Verve Communication SDK
│           │   ├── index.js          ← Barrel export (single import path)
│           │   ├── VerveSDK.js       ← Verve.init() entry point
│           │   ├── VerveSession.js   ← State machine + event translation
│           │   ├── VerveEventEmitter.js ← Namespaced event system
│           │   ├── VerveProvider.jsx ← React context + theme application
│           │   ├── VerveTheme.css    ← CSS variable theming layer
│           │   ├── VerveRoom.jsx     ← Full prebuilt room component
│           │   ├── VerveControls.jsx ← Standalone control bar
│           │   ├── VerveChat.jsx     ← Standalone chat panel
│           │   └── VerveParticipants.jsx ← Standalone participants panel
│           └── 📂 utils/
│               └── decodeJwt.js      ← Client-side JWT decoder (UI only)
│
└── 📖 README.md

🏢 Multi-Tenant Embed API (Phase 1)

The Tenant API lets any third-party service embed Verve rooms with full branding isolation. All RTC logic is reused — no parallel engine.

Register a Tenant

POST /api/tenant/register
Content-Type: application/json

{
  "name": "Acme Corp",
  "primaryColor": "#ff6b00",
  "allowedOrigins": ["https://acme.com"]
}

Response:

{
  "tenantId": "...",
  "apiKey": "vk_...",
  "primaryColor": "#ff6b00"
}

Create a Room

POST /api/tenant/rooms
x-api-key: vk_...

{ "ttlHours": 2 }

Response:

{
  "roomId": "abc123def456",
  "embedUrl": "https://verve-live.vercel.app/embed/abc123def456",
  "expiresAt": "2024-01-01T14:00:00.000Z"
}

Generate an Embed Token

POST /api/tenant/rooms/:roomId/token
x-api-key: vk_...

{ "guestName": "Alice", "role": "participant" }

Response:

{
  "token": "eyJ...",
  "embedUrl": "https://verve-live.vercel.app/embed/ROOM?token=eyJ...",
  "guestId": "guest_abc123"
}

Embed via iframe

<iframe
  src="https://verve-live.vercel.app/embed/ROOM_ID?token=JWT_TOKEN"
  allow="camera; microphone; display-capture; fullscreen; clipboard-write"
  width="100%"
  height="640px"
/>

Parent Page Communication (postMessage)

The embedded iframe communicates status changes back to the host window using window.parent.postMessage. The host page can listen for these events to track the meeting session state:

window.addEventListener("message", (event) => {
  // Ensure the message is from your Verve deployment origin
  if (event.origin !== "https://verve-live.vercel.app") return;

  const data = event.data;
  if (data?.type === "verve:status") {
    const { status, roomId, tenantId, role, guestId, guestName, roomEnded } = data;
    console.log(`Room Status changed to: ${status} (Ended: ${roomEnded})`);
  }
});

📦 Verve SDK (Phase 2)

Outsourcing WebRTC & Media Complexity

Verve handles all selective forwarding unit (SFU) routing, WebRTC peer connection transport, DTLS handshakes, and multi-tenant database isolation. Implementing raw WebRTC or Mediasoup requires managing complex connection states, SDP signaling, and media streams; Verve wraps all this logic inside a simple, unified interface.

The SDK is organized into three architectural layers:

  1. Vanilla SDK Core (Framework Agnostic): VerveSDK (entry point), VerveSession (connection state machine), and VerveEventEmitter (stable events). Has zero dependencies on UI frameworks and can be used in React, Vue, Svelte, or vanilla JS.
  2. React Bindings: VerveProvider (injects theme CSS variables and provides context) and useVerveSession (hook).
  3. Prebuilt UI Components: Ready-made, highly functional React components (VerveRoom, VerveControls, VerveChat, VerveParticipants) that plug directly into the session.

White-label embed helper

For partner platforms that already receive a server-generated embed token, mount Verve without React:

import Verve from "./sdk";

const embed = Verve.embed({
  container: "#support-call",
  roomId: "abc123def456",
  token: "eyJ...",
  frontendUrl: "https://your-verve-host.com",
  style: { minHeight: "640px", borderRadius: "12px" },
});

// Later:
embed.destroy();

You can also pass the full embedUrl returned by POST /api/tenant/rooms/:roomId/token. Keep tenant API keys on your server; the browser embed only needs the short-lived token.

The SDK provides a clean, high-level API for code-based integration. Mediasoup/WebRTC internals stay completely hidden.

Installation

# Import directly from the src/sdk path (monorepo setup)
import Verve from "./sdk";

Initialize & Join

const session = await Verve.init({
  apiKey: "vk_...",
  roomId: "abc123def456",
  user: { name: "Alice", role: "participant" },
  config: {
    chatEnabled: true,
    screenShareEnabled: true,
    cameraEnabled: true,
  },
  theme: {
    primary: "#7c3aed",
    bg: "#0a0a0f",
    text: "#ffffff",
  },
});

await session.joinRoom();

Actions

session.toggleMic();          // mute / unmute
session.toggleCamera();       // camera on / off
session.startScreenShare();   // start sharing
session.stopScreenShare();    // stop sharing
session.sendMessage("hello"); // send chat message
session.toggleRaiseHand();    // raise / lower hand
session.leaveRoom();          // disconnect
session.destroy();            // full cleanup

Events

session.on("room.connected",        (data) => { /* ... */ });
session.on("room.disconnected",     (data) => { /* ... */ });
session.on("room.ended",            (data) => { /* ... */ });
session.on("participant.joined",    (data) => { /* id, name */ });
session.on("participant.left",      (data) => { /* id, name */ });
session.on("chat.message",          (msg)  => { /* ... */ });
session.on("mic.changed",           (data) => { /* ... */ });
session.on("camera.changed",        (data) => { /* ... */ });
session.on("screenShare.started",   (data) => { /* ... */ });
session.on("screenShare.stopped",   (data) => { /* ... */ });
session.on("room.stateChanged",     ({ from, to }) => { /* state machine */ });

Connection State Machine

idle → connecting → connected → reconnecting → disconnected → destroyed
console.log(session.connectionState); // "idle" | "connecting" | "connected" | ...
console.log(session.connected);       // boolean shorthand

Prebuilt React Components

import { VerveProvider, VerveRoom } from "./sdk";

function App() {
  return (
    <VerveProvider session={session}>
      <VerveRoom />
    </VerveProvider>
  );
}

CSS Theming

Override CSS variables on .verve-root or pass theme to Verve.init():

.verve-root {
  --verve-primary: #7c3aed;
  --verve-bg:      #0a0a0f;
  --verve-text:    #ffffff;
  --verve-border:  rgba(255, 255, 255, 0.08);
  --verve-font:    'Inter', sans-serif;
  --verve-radius:  12px;
}

🔌 Socket Events Reference

📡 Click to see all Socket.io events

Client → Server

Event Payload Response/Callback Description
join-room rawRoomId (string) { rtpCapabilities, isCreator, myUserId, myName, timerState } or { error } Normalizes Room ID, validates tokens (regular or embed), verifies expiration, and returns room details.
create-send-transport { id, iceParameters, iceCandidates, dtlsParameters } or { error } Initiates worker send transport on the server.
create-recv-transport { id, iceParameters, iceCandidates, dtlsParameters } or { error } Initiates worker receive transport on the server.
connect-transport { transportId, dtlsParameters } { connected: true } or { error } Completes DTLS handshakes for the transport.
produce { transportId, kind, rtpParameters, appData } { id } or { error } Starts producing video, audio, or screen media.
consume { producerId, rtpCapabilities } { id, producerId, kind, rtpParameters } or { error } Creates a paused stream consumer for a participant.
resume-consumer { consumerId } { resumed: true } or { error } Resumes the paused stream consumer.
get-producers { producers: [] } (list of active producers) or { error } Returns all active stream producers in the room.
send-message { message } { sent: true } or { error } Sends a message to the room chat channel.
get-chat-history { messages: [] } or { error } Returns the recent in-memory chat message history.
raise-hand { raised } Toggles hand-raised status; broadcasts state to the room.
send-reaction { type } Sends floating reactions ("confetti", "clap", "laugh", "heart", "fire", "thumbsup"). Rate-limited to 1 reaction per second.
speaking-update { durationMs } Reports active speaking duration for dynamic statistics tracking.
start-timer { durationMs } Starts the shared focus timer (host only).
stop-timer Stops the active focus timer (host only).

💡 SDK Client Actions: The vanilla SDK wrapper (VerveSession.js) also exposes methods that emit helper events like toggle-mute, toggle-camera, start-screen-share, stop-screen-share, toggle-raise-hand, and chat-message to assist with headless custom integrations.

Server → Client

Event Payload Trigger
new-producer { producerId, peerId, userId, name, kind, appData } Broadcasted when a participant begins producing media.
new-peer { peerId, userId, name } Broadcasted when a new user successfully joins the room.
peer-left { peerId, userId } Broadcasted when a participant leaves or disconnects.
producer-closed { producerId } Broadcasted when a producer shuts down their media track.
peers-list { peers: [{ peerId, userId, name }] } Emitted to update the local participants roster.
new-message { peerId, userId, name, message, timestamp } Dispatched to deliver a new chat message to all room members.
hand-raised { peerId, userId, name, raised } Dispatched when a participant raises or lowers their hand.
reaction { type, peerId, userId, name, timestamp } Dispatched when a participant triggers an emoji reaction.
speaking-stats { stats: { [userId]: { ms, name } } } Periodic broadcast tracking cumulative speaking durations.
timer-sync { durationMs, startedAt } Dispatched to sync focus timer configuration across all peers.
timer-ended Dispatched when the active focus timer duration elapses.
room-closed { reason } Sent when the room host closes the meeting or disconnects.

🧪 API Endpoints

Auth API

Method Endpoint Body Auth?
POST /api/auth/register { name, email, password }
POST /api/auth/login { email, password }
GET /api/auth/me ✅ Bearer Token

Room API

Method Endpoint Body Auth?
POST /api/rooms/instant ✅ Bearer Token
POST /api/rooms { roomId } ✅ Bearer Token
POST /api/rooms/join { roomId } ✅ Bearer Token

Tenant API

Method Endpoint Body Auth?
POST /api/tenant/register { name, primaryColor, allowedOrigins }
POST /api/tenant/rooms { ttlHours } x-api-key
POST /api/tenant/rooms/:roomId/token { guestName, role } x-api-key
GET /api/tenant/branding/:tenantId ❌ (public)
PUT /api/tenant/branding { name, logo, primaryColor, allowedOrigins } x-api-key

🎯 Roadmap

  • Core video & audio calling with SFU
  • Room system with unique IDs
  • In-call chat with history
  • Screen sharing
  • Raise hand feature
  • Adaptive grid layout
  • Dark theme UI
  • User authentication (JWT + MongoDB)
  • Speaking stats panel
  • Floating emoji reactions
  • Focus timer (host-controlled)
  • Pin-to-focus participant view
  • Phase 1: Multi-tenant embed system with branded iframes
  • Phase 1: Scoped JWT embed tokens with TTL room expiry
  • Phase 1: Origin-validated allowedOrigins for iframe security
  • Phase 2: Hybrid Communication SDK (Verve.init)
  • Phase 2: Connection state machine
  • Phase 2: Namespaced event system
  • Phase 2: Prebuilt React components (VerveRoom, VerveChat, etc.)
  • Phase 2: CSS variable theming layer
  • Recording support
  • Virtual backgrounds
  • Breakout rooms
  • Mobile-responsive design
  • E2E encryption

🤝 Contributing

  1. Fork the repo
  2. Create your feature branch (git checkout -b feature/amazing-thing)
  3. Commit your changes (git commit -m 'Add amazing thing')
  4. Push to the branch (git push origin feature/amazing-thing)
  5. Open a Pull Request

📜 License

This project is available under the MIT License.


Built with 🔥 by

Saksham Singhal and Shivam Mishra


If Verve made your calls better, give it a ⭐ — it fuels the late-night coding sessions!

About

A low-latency video conferencing app with instant room generation, peer-to-peer media streams, real-time chat, and mute/camera controls powered by WebRTC, Socket.io, React, and Node.js.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors