Real-time Video Communication Platform with AI-Powered Sign Language Translation
Universal Link is a peer-to-peer video calling application designed to bridge communication gaps between sign language users and speakers. Built with WebRTC for low-latency video streaming and featuring AI-powered sign language recognition, this platform enables seamless real-time translation and inclusive communication.
- Peer-to-peer connectivity with sub-200ms latency
- Multi-participant support via room-based architecture
- STUN server integration for NAT traversal using Google's public STUN servers
- Real-time hand tracking powered by MediaPipe/TensorFlow
- AI-powered gesture recognition for American Sign Language (ASL)
- Sentence building with gesture-to-text conversion
- Live translation overlay for seamless communication
- Text messaging alongside video communication
- Sign-to-text messages with special indicators
- Real-time message synchronization across all participants
- Direct P2P connections - video never routed through servers
- End-to-end encryption via WebRTC's built-in DTLS-SRTP
- No data storage - ephemeral room-based sessions
- Responsive design with TailwindCSS v4
- Dark mode support with theme customization
- Accessible interface following WCAG guidelines
- Real-time participant indicators and connection status
- Framework: React 19 with TypeScript
- Build Tool: Vite 7
- Styling: TailwindCSS v4 with Radix UI components
- State Management: React Hooks (useState, useEffect, useRef, useCallback)
- Routing: React Router DOM v7
- Real-time Communication: Socket.IO Client v4.8
- Icons: Lucide React
- Runtime: Node.js with Express v5
- WebSocket Server: Socket.IO v4.8
- CORS Handling: CORS middleware
- Environment Config: dotenv
- Video Protocol: WebRTC (RTCPeerConnection)
- ICE Servers: Google STUN servers (stun.l.google.com)
- Media API: getUserMedia for camera/microphone access
- Hand Tracking: MediaPipe Hands
- Sign Recognition: TensorFlow.js with custom ASL model
π The signaling server is ONLY used for initial connection setup. Once the WebRTC peer connection is established, all video/audio streams flow directly P2P between clients - the server never handles media data.
graph TB
subgraph ClientA["π₯οΈ Client Browser A"]
ReactA["React App"]
MediaA["πΉ MediaStream<br/>(Camera/Mic)"]
end
subgraph Server["π Signaling Server<br/>(Node.js + Socket.IO)"]
RoomMgmt["β’ Room Management<br/>β’ SDP Offer/Answer Exchange<br/>β’ ICE Candidate Relay<br/>β’ User Join/Leave Events"]
end
subgraph ClientB["π₯οΈ Client Browser B"]
ReactB["React App"]
MediaB["πΉ MediaStream<br/>(Camera/Mic)"]
end
ReactA -->|"Socket.IO<br/>(Signaling Only)"| Server
Server -->|"Socket.IO<br/>(Signaling Only)"| ReactB
MediaA <-.->|"β‘ WebRTC P2P Direct<br/>(Video/Audio)<br/>π No Server!"| MediaB
style Server fill:#2563eb,stroke:#1e40af,color:#fff
style ClientA fill:#10b981,stroke:#059669,color:#fff
style ClientB fill:#10b981,stroke:#059669,color:#fff
style MediaA fill:#f59e0b,stroke:#d97706,color:#000
style MediaB fill:#f59e0b,stroke:#d97706,color:#000
Phase 1: Signaling (via Server)
- β Clients connect to signaling server via Socket.IO
- β Users join rooms with unique Room IDs
- β
Signaling server coordinates WebRTC handshake:
- Exchange SDP offers/answers (session descriptions)
- Relay ICE candidates (network path discovery)
- Notify peers about joins/leaves
Phase 2: Direct P2P (No Server) 4. β Once WebRTC connection is established, clients communicate directly 5. β All video/audio streams flow peer-to-peer without server involvement 6. β Signaling server remains connected only for control messages (new peers joining, etc.)
Result: Ultra-low latency (<200ms) with complete privacy - your video never touches the server!
Before you begin, ensure you have the following installed:
- Node.js (v18.x or higher)
- npm (v9.x or higher) or pnpm
- Modern browser with WebRTC support (Chrome, Firefox, Edge, Safari 11+)
- HTTPS/localhost (WebRTC requires secure context for camera/mic access)
git clone https://github.com/RunTime-Terrors-NITC/Universal-Link
cd Universal-LinkNavigate to the backend directory and install dependencies:
cd backend
npm installCreate a .env file based on .env.example:
cp .env.example .envEdit .env with your configuration:
PORT=4000
FRONTEND_URL=http://localhost:3000,http://127.0.0.1:3000Start the backend server:
# Development mode with auto-reload
npm run dev
# Production mode
npm startThe signaling server will be running on http://localhost:4000
Navigate to the frontend directory and install dependencies:
cd ../frontend
npm installCreate a .env file based on .env.example:
cp .env.example .envEdit .env with your configuration:
VITE_PORT=3000
VITE_BACKEND_URL=http://localhost:4000Start the development server:
npm run devThe application will be available at http://localhost:3000
Frontend:
cd frontend
npm run buildProduction build will be in frontend/dist/
Backend:
cd backend
npm start- Open the application at
http://localhost:3000 - Enter your Display Name
- Click "Create Room" to generate a new session
- Share the generated Room ID with participants
- Open the application
- Enter your Display Name
- Enter the Room ID provided by the host
- Click "Join Room"
Controls:
- π€ Toggle Microphone - Mute/unmute audio
- πΉ Toggle Camera - Turn video on/off
- π End Call - Leave the room and return to home
- π¬ Chat - Open text chat panel
- βοΈ Options - Access sign mode and sound settings
For Sign Language Users:
- Enable "Sign Mode" from the options menu
- Use hand gestures in front of the camera
- Detected signs will appear in the overlay
- Sentences build automatically and can be sent to other participants
The signaling server uses Socket.IO with the following events:
| Event | Payload | Description |
|---|---|---|
join-room |
roomId: string |
Join a specific room |
leave-room |
roomId: string |
Leave the current room |
offer |
{ offer: RTCSessionDescription, to: string } |
Send WebRTC offer to peer |
answer |
{ answer: RTCSessionDescription, to: string } |
Send WebRTC answer to peer |
ice-candidate |
{ candidate: RTCIceCandidate, to: string } |
Send ICE candidate to peer |
| Event | Payload | Description |
|---|---|---|
joined-room |
{ roomId: string, userId: string } |
Confirmation of room join |
user-joined |
{ userId: string } |
Notify when a new user joins |
user-left |
{ userId: string } |
Notify when a user leaves |
offer |
{ offer: RTCSessionDescription, from: string } |
Receive WebRTC offer from peer |
answer |
{ answer: RTCSessionDescription, from: string } |
Receive WebRTC answer from peer |
ice-candidate |
{ candidate: RTCIceCandidate, from: string } |
Receive ICE candidate from peer |
Universal-Link/
βββ backend/
β βββ .env.example # Environment template
β βββ package.json # Backend dependencies
β βββ server.js # Express + Socket.IO server
β
βββ frontend/
β βββ public/ # Static assets
β βββ src/
β β βββ components/
β β β βββ ui/ # Reusable UI components (shadcn/ui)
β β β βββ ChatPanel.tsx # Chat interface component
β β β βββ VideoGrid.tsx # Video participant grid
β β βββ hooks/
β β β βββ useWebRTC.ts # WebRTC connection logic
β β βββ pages/
β β β βββ Home.tsx # Landing page with room creation
β β β βββ Room.tsx # Video call interface
β β β βββ NotFound.tsx # 404 page
β β βββ router/
β β β βββ index.tsx # React Router configuration
β β βββ lib/
β β β βββ utils.ts # Utility functions
β β βββ App.tsx # Root component
β β βββ main.tsx # React entry point
β β βββ index.css # Global styles
β βββ .env.example # Environment template
β βββ package.json # Frontend dependencies
β βββ vite.config.ts # Vite configuration
β βββ tsconfig.json # TypeScript configuration
β
βββ README.md # This file
- WebRTC for enabling real-time peer-to-peer communication
- Socket.IO for reliable signaling
- MediaPipe for hand tracking technology
- Radix UI and shadcn/ui for accessible component primitives
- TailwindCSS for utility-first styling
- The open-source community for inspiration and tools
Made with β€οΈ for accessible communication