Waves is a modern, real-time chat application that offers global, network-based, and custom private chat rooms. Built with React, Node.js, and Socket.IO, it features a beautiful, responsive UI and seamless real-time communication.
- Global Room: Connect with users worldwide in a public chat
- Network Room: Auto-assigned rooms based on IP subnet for local connections
- Custom Rooms: Private rooms with unique 6-character codes for secure sharing
- Anonymous login with auto-generated usernames and colors
- Custom account creation with persistent identities
- Room-specific color palettes for visual distinction
- Peer-to-Peer messaging via WebRTC with automatic server fallback
- Real-time communication with message deduplication
- Message reactions and timestamps
- Mobile-optimized interface with touch-friendly controls
- Native sharing with clipboard fallback
- Auto-focus input fields and rate limiting
- Node.js (v14 or higher)
- MongoDB
- npm or yarn
- Clone the repository
git clone https://github.com/wavey-waves/waves.git
cd waves- Install dependencies for both frontend and backend
# Install backend dependencies
cd backend
npm install
# Install frontend dependencies
cd ../frontend
npm install- Create environment files
Backend (.env):
PORT=3000
MONGODB_URI=your_mongodb_uri
JWT_SECRET=your_jwt_secret
NODE_ENV=development- Start the development servers
Backend:
cd backend
npm run devFrontend:
cd frontend
npm run dev- React (Vite)
- Socket.IO Client
- Axios
- TailwindCSS
- React Router
- react-toastify
- Node.js
- Express
- Socket.IO
- MongoDB with Mongoose
- JWT Authentication
Waves is a real-time chat application featuring Peer-to-Peer (P2P) messaging with WebRTC Data Channels, automatic server fallback, and three room types: Global, Network-based, and Custom private rooms.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β WAVES CHAT PLATFORM β
β Real-time P2P Messaging System β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β USER JOURNEY β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββ΄ββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββββββ βββββββββββββββββββββββ
β ANONYMOUS USER β β REGISTERED USER β
β (localStorage) β β (JWT Cookies) β
βββββββββββββββββββββββ βββββββββββββββββββββββ
β β
βββββββββββββββββ¬ββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ROOM SELECTION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββΌββββββββββββββββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββββββ ββββββββββββββββββ ββββββββββββββββββββββββ
β GLOBAL ROOM β β NETWORK β β CUSTOM ROOM β
β (/chat/global) β β ROOM β β (/chat/custom/XXXXXX)|
β β β (/chat/network)| β β
βββββββββββββββββββββββ ββββββββββββββββββ ββββββββββββββββββββββββ
β β β
βββββββββββββββββββββββΌβββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β AUTHENTICATION FLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
1. User visits / (Home)
2. Clicks room type (Global/Network/Custom)
3. JoinRoom component loads
4. Check localStorage.getItem('anonymousUser')
βββ If exists & valid (not expired):
β βββ Parse JSON: {name, color, expiry}
β βββ expiry = Date.now() + 7 days
βββ If missing/invalid/expired:
βββ Generate new random name & color
βββ Name: unique-names-generator (adjective-color-animal)
βββ Color: Room-specific color palette
βββ Store in localStorage as JSON
5. User clicks "Join as Anonymous"
6. POST /api/auth/login with {userName: randomName}
βββ If user exists: Return user data
βββ If user doesn't exist: Auto-create via signup
βββ POST /api/auth/signup with {userName, color, isAnonymous: true}
7. Server generates JWT token (7 days expiry)
8. Cookie: jwt=token; httpOnly=true; secure=production; path="/"
9. Return: {_id, userName, color, isAnonymous: true}
1. User visits / (Home)
2. Clicks room type β JoinRoom component
3. Selects "Create Account" tab
4. Enters username, password, selects color
5. POST /api/auth/signup with {userName, password, color, isAnonymous: false}
βββ Validate: username unique, password β₯6 chars
βββ Hash password: bcrypt.genSalt(10) + bcrypt.hash()
βββ Store: {userName, hashedPassword, color, isAnonymous: false}
βββ Generate JWT token (7 days)
6. Cookie: jwt=token (same settings as anonymous)
7. Return: {_id, userName, color, isAnonymous: false}
- JWT Cookie: 7 days expiry, httpOnly, secure in production
- Anonymous localStorage: 7 days expiry, auto-regenerates
- Color Assignment: Room-specific palettes (15 colors each)
βββ Global: Purple/Violet/Blue theme
βββ Network: Emerald/Cyan/Teal theme
βββ Custom: Rose/Pink theme
- Accessible to all authenticated users
- Room name: "global-room"
- No special assignment logic
- All users join same Socket.IO room: "global-room"
1. User authenticates successfully
2. POST /api/rooms/assign (protected route)
3. Extract client IP using request-ip package
4. Create subnet: IP.split('.').slice(0,3).join('.')
5. Generate roomName: `network-${subnet}`
6. Find/create room in MongoDB
βββ If exists: Add user to members array
βββ If new: Create room with user as first member
7. Return: {roomId, roomName, memberCount, members[]}
8. Socket.IO join: socket.join(roomName)
Creation Flow:
1. User clicks "Custom Room" on home
2. CustomRoom component loads
3. User clicks "Create Room"
4. POST /api/rooms/create
5. Generate unique 6-char code:
βββ Loop until unique: Math.random().toString(36).substring(2,8).toUpperCase()
βββ Check MongoDB: Room.findOne({code})
6. Create room: {roomName: `custom-${code}`, code, isCustomRoom: true}
7. Return: {roomId, roomName, code, memberCount: 0}
Joining Flow:
1. User enters URL: /chat/custom/ABC123
2. ChatRoute component loads
3. POST /api/rooms/join with {code: "ABC123"}
4. Find room by code (case-insensitive)
5. Return room info (without member details for privacy)
6. Show JoinRoom component for authentication
7. After auth: Join Socket.IO room by roomName
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MESSAGE SENDING FLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββ
β User Types Message β
βββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββ
β Input Validation β
β - Not empty β
β - β€1000 chars β
β - Rate limit (1s) β
βββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββ
β P2P Attempt First β
β (WebRTC DataChannel)β
βββββββββββββββββββββββββ
β
βββββββββββββΌββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββ βββββββ βββββββββββββββ
β P2P Success β βP2P β β P2P Fails β
β Direct Send β βFailsβ β Server β
βββββββββββββββββββ βββββββ β Relay β
βββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββ
β Server Processing β
β POST /api/messages/send/:roomName|
ββββββββββββββββββββββββββββββββββββββ
Connection Establishment:
1. User joins Socket.IO room
2. Server emits "existing-room-users" with other users
3. For each existing user:
βββ Create RTCPeerConnection with ICE servers
βββ Create DataChannel named "chat"
βββ Create offer: pc.createOffer()
βββ Set local description
βββ Send offer via Socket.IO: "webrtc-offer"
4. Receiving user:
βββ Create RTCPeerConnection
βββ Set remote description (offer)
βββ Create answer: pc.createAnswer()
βββ Send answer via Socket.IO: "webrtc-answer"
5. ICE candidate exchange:
βββ Both sides: pc.onicecandidate β emit "webrtc-ice-candidate"
βββ Add received candidates to peer connections
6. DataChannel setup:
βββ dc.onopen: Connection ready for P2P messaging
βββ dc.onmessage: Receive P2P messages
βββ dc.onclose: Cleanup connection
Fallback Logic:
- If P2P fails after 10 seconds: Close connection, use server
- Network changes: Automatically attempt P2P reconnection
- Firewall/NAT issues: Seamless server relay fallback
Message Schema:
{
senderId: ObjectId (ref: Users),
room: String (default: "global-room"),
text: String,
reactions: [{
userId: ObjectId,
emoji: String,
createdAt: Date
}],
expiresAt: Date (31 days for messages, 40 days for rooms)
}
Cleanup System:
- MongoDB TTL indexes auto-delete expired messages/rooms
- Manual cleanup: DELETE /api/messages/cleanup
βββ Keep only latest 1000 messages per room
βββ Delete older messages in batches
- Reaction limits: One reaction per user per message
Room-Based Color Palettes:
Global Room Colors (15 colors):
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β #8b5cf6 #a855f7 #6366f1 #3b82f6 #0ea5e9 #60a5fa β
β #d946ef #ec4899 #f43f5e #f97316 #f59e0b #fbbf24 β
β #eab308 #84cc16 #22c55e β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Theme: Purple β Violet β Blue β Indigo
Network Room Colors (15 colors):
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β #10b981 #14b8a6 #06b6d4 #34d399 #22c55e #84cc16 β
β #0ea5e9 #60a5fa #3b82f6 #6366f1 #8b5cf6 #a855f7 β
β #d946ef #ec4899 #f43f5e β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Theme: Emerald β Teal β Cyan β Green
Custom Room Colors (15 colors):
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β #f43f5e #ec4899 #d946ef #e11d48 #f97316 #fbbf24 β
β #f59e0b #84cc16 #22c55e #10b981 #06b6d4 #3b82f6 β
β #6366f1 #8b5cf6 #a855f7 β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Theme: Rose β Pink β Fuchsia
Mobile Optimizations:
- Viewport height: CSS custom property --vh
- Info button: Shows user details & room code on mobile
- Header layout: Logo acts as back button on mobile
- Touch-friendly: Larger buttons, swipe gestures
- Auto-focus: Input field focuses automatically on load
Desktop Features:
- Hover effects: Scale transforms, glow effects
- Gradient animations: CSS keyframe animations
- Modal dialogs: Documentation, room creation
- Native sharing: Web Share API with clipboard fallback
Server Events (io.on):
βββ "connection": New client connects
βββ "join": Client joins room
βββ "leave": Client leaves room
βββ "disconnect": Client disconnects
βββ "webrtc-offer": WebRTC offer received
βββ "webrtc-answer": WebRTC answer received
βββ "webrtc-ice-candidate": ICE candidate received
Client Events (socket.emit):
βββ "join": Join room
βββ "leave": Leave room
βββ "webrtc-offer": Send WebRTC offer
βββ "webrtc-answer": Send WebRTC answer
βββ "webrtc-ice-candidate": Send ICE candidate
Broadcast Events (io.to(room).emit):
βββ "userJoined": New user joined room
βββ "userLeft": User left/disconnected
βββ "chatMessage": New message received
βββ "message-reacted": Message reaction updated
βββ "existing-room-users": Send existing users to new joiner
Processed Message IDs:
- Set<string> processedMessageIds (in-memory)
- Tracks both _id and tempId for each message
- Prevents duplicate rendering from P2P + Server paths
- Cleanup: Automatic garbage collection on component unmount
Info Button (Mobile Only):
- Position: Fixed top-right corner
- Content: Username, Room type/code, Member count
- Trigger: Click to show modal overlay
- Close: Click outside or X button
Header Layout:
- Desktop: Logo + Room info + Share + Info
- Mobile: Logo (back) + Room name + Info button
- Responsive breakpoints: Tailwind CSS classes
Input System:
- Auto-focus: textarea.focus() on component mount
- Auto-resize: Dynamic height based on content
- Character limit: 1000 chars with warning at 900
- Rate limiting: 1000ms throttle between sends
if (navigator.share) {
// Web Share API (Mobile browsers)
navigator.share({
title: \`Join \${roomName}\`,
text: \`Join my chat room: \${roomCode}\`,
url: window.location.href
});
} else {
// Clipboard fallback (Desktop/some mobile)
navigator.clipboard.writeText(window.location.href);
toast.success("Room link copied to clipboard!");
}
/ β Home (room selection)
/chat/global β Global room
/chat/network β Network room (IP-based)
/chat/custom/:code β Custom room (code-based)
jwt: JWT token (7 days expiry)
βββ httpOnly: true (prevents XSS access)
βββ secure: true (HTTPS only in production)
βββ sameSite: "lax" (CSRF protection)
βββ path: "/" (available site-wide)
anonymousUser: JSON string
βββ {name, color, expiry}
βββ expiry: Date.now() + 7 days
βββ Auto-regenerates when expired
Legacy keys (auto-migrated):
βββ anonymousUsername: string
βββ userColor: string
Users:
βββ _id, userName, password?, color, isAnonymous
βββ expiresAt (TTL: 7 days anonymous, 1 year registered)
Messages:
βββ senderId, room, text, reactions[], expiresAt
βββ TTL: 31 days
Rooms:
βββ roomName, code?, members[], createdByIp, isCustomRoom
βββ TTL: 40 days
Backend (.env):
βββ MONGODB_URI: MongoDB connection string
βββ PORT: Server port (default: 3000)
βββ JWT_SECRET: JWT signing key
βββ NODE_ENV: development/production
Frontend (.env.*):
βββ VITE_BACKEND_URL: API endpoint
βββ Development: http://localhost:8000
βββ Production: https://waves-c53a.onrender.com
Root package.json scripts:
βββ build: Install deps + build frontend + install backend
βββ start: Start backend (serves built frontend)
Frontend (Vite):
βββ dev: Development server with HMR
βββ build: Production build to dist/
βββ preview: Preview production build
Backend (Node.js):
βββ dev: nodemon with auto-restart
βββ start: Production server
WebRTC P2P Fallback:
βββ Timeout: 10 seconds for P2P connection
βββ Auto-fallback: Server relay if P2P fails
βββ Reconnection: Automatic P2P reattempts
Network Issues:
βββ Socket.IO auto-reconnection
βββ Message queuing during disconnects
βββ Offline detection and user feedback
Authentication Errors:
βββ Token expiry: Redirect to join room
βββ Invalid token: Clear cookies, re-authenticate
βββ Network errors: Retry with exponential backoff
Automatic Cleanup:
βββ Keep latest 1000 messages per room
βββ Delete older messages in batches
βββ MongoDB TTL indexes for auto-expiry
Memory Management:
βββ Processed message ID deduplication
βββ Peer connection cleanup on disconnect
βββ Component unmount cleanup
Rate Limiting:
βββ 1000ms throttle between message sends
βββ Character limits (1000 max)
βββ Input validation before sending
1. Landing Page
βββ Animated "Waves" title with glow effects
βββ Three room type cards with hover animations
βββ Documentation button (top-right)
βββ Gradient background with subtle patterns
2. Room Selection
βββ Global: Purple theme, instant access
βββ Network: Green theme, IP-based auto-assignment
βββ Custom: Pink theme, code generation/sharing
3. Authentication
βββ Anonymous: Auto-generated name/color (localStorage)
βββ Registered: Manual username/password creation
βββ Color selection from room-specific palettes
4. Chat Interface
βββ Real-time P2P messaging with server fallback
βββ Mobile-responsive header with info modal
βββ Auto-focus input field
βββ Message reactions (one per user)
βββ Share functionality with native API
5. Message Features
βββ P2P priority with seamless server fallback
βββ User color coding for message distinction
βββ Timestamp display
βββ Auto-scroll to latest messages
βββ Character counter with warnings
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request