Updated to @xtr-dev/rondevu-client@0.9.2 which fixes critical timing issue where ICE candidates were generated before handlers were attached. Now includes detailed logging for service pool ICE candidate generation with candidate types (host/srflx/relay) for debugging TURN issues. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Rondevu Demo
🎯 Interactive WebRTC peer discovery and connection demo
Experience topic-based peer discovery and WebRTC connections using the Rondevu signaling platform.
Related repositories:
- @xtr-dev/rondevu-client - TypeScript client library (npm)
- @xtr-dev/rondevu-server - HTTP signaling server (npm, live)
- @xtr-dev/rondevu-demo - Interactive demo (live)
Overview
This demo showcases the complete Rondevu workflow:
- Register - Get peer credentials (automatically saved)
- Create Offers - Advertise your WebRTC connection on topics
- Discover Peers - Find other peers by topic
- Connect - Establish direct P2P WebRTC connections via
RondevuPeer - Chat - Send messages over WebRTC data channels
Key Features
- Topic-Based Discovery - Find peers by shared topics (like torrent infohashes)
- Real P2P Connections - Actual WebRTC data channels (not simulated)
- State-Based Peer Management - Uses
RondevuPeerwith clean state machine (idle → creating-offer → waiting-for-answer → exchanging-ice → connected) - Trickle ICE - Fast connection establishment by sending ICE candidates as they're discovered
- Persistent Credentials - Saves authentication to localStorage
- Topics Browser - Browse all active topics and peer counts
- Multiple Connections - Support multiple simultaneous peer connections
- Real-time Chat - Direct peer-to-peer messaging
Quick Start
Installation
npm install
Development
npm run dev
This starts the Vite dev server at http://localhost:5173
Build for Production
npm run build
The built files will be in the dist/ directory.
Preview Production Build
npm run preview
How to Use
Step 1: Register (One-time)
The demo automatically registers you when you first visit. Your credentials are saved in localStorage for future visits.
Step 2: Create an Offer
- Go to the "Create Offer" tab
- Enter one or more topics (comma-separated), e.g.,
demo-room, testing - Click "Create Offer"
- Your offer is now advertised on those topics
Share the topic name with peers you want to connect with!
Step 3: Discover and Connect (Other Peer)
- Go to the "Discover Offers" tab
- Enter the same topic (e.g.,
demo-room) - Click "Discover Offers"
- See available peers and their offers
- Click "Answer Offer" to connect
Step 4: Chat
- Once connected, go to the "Chat" tab
- Select a connection from the dropdown
- Type messages and hit Enter or click Send
- Messages are sent directly peer-to-peer via WebRTC
Browse Topics
Click the "Topics" tab to:
- See all active topics
- View peer counts for each topic
- Quick-discover by clicking a topic
Testing Locally
The easiest way to test:
- Open the demo in two browser windows (or tabs)
- Window 1: Create an offer with topic
test-room - Window 2: Discover offers in
test-roomand answer - Switch to Chat tab in both windows
- Start chatting peer-to-peer!
Technical Implementation
RondevuPeer State Machine
This demo uses the RondevuPeer class which implements a clean state-based connection lifecycle:
import { Rondevu } from '@xtr-dev/rondevu-client';
// Create peer
const peer = client.createPeer();
// Set up event listeners
peer.on('state', (state) => {
console.log('Peer state:', state);
// Offerer: idle → creating-offer → waiting-for-answer → exchanging-ice → connected
// Answerer: idle → answering → exchanging-ice → connected
});
peer.on('connected', () => {
console.log('✅ P2P connection established!');
});
peer.on('datachannel', (channel) => {
channel.addEventListener('message', (event) => {
console.log('📥 Message:', event.data);
});
channel.addEventListener('open', () => {
// Channel is ready, can send messages
channel.send('Hello!');
});
});
peer.on('failed', (error) => {
console.error('❌ Connection failed:', error);
});
// Create offer (offerer)
await peer.createOffer({
topics: ['demo-room'],
ttl: 300000
});
// Or answer an offer (answerer)
await peer.answer(offerId, offerSdp, {
topics: ['demo-room']
});
Connection States
Offerer Flow:
- idle - Initial state
- creating-offer - Creating WebRTC offer and sending to server
- waiting-for-answer - Polling for answer from peer (every 2 seconds)
- exchanging-ice - Exchanging ICE candidates (polling every 1 second)
- connected - Successfully connected!
- failed/closed - Connection failed or was closed
Answerer Flow:
- idle - Initial state
- answering - Creating WebRTC answer and sending to server
- exchanging-ice - Exchanging ICE candidates (polling every 1 second)
- connected - Successfully connected!
- failed/closed - Connection failed or was closed
What Happens Under the Hood
-
Offerer calls
peer.createOffer():- State →
creating-offer - Creates RTCPeerConnection and data channel
- Generates SDP offer
- Sets up ICE candidate handler (before gathering starts)
- Sets local description → ICE gathering begins
- Posts offer to Rondevu server
- State →
waiting-for-answer - Polls for answers every 2 seconds
- When answer received → State →
exchanging-ice
- State →
-
Answerer calls
peer.answer():- State →
answering - Creates RTCPeerConnection
- Sets remote description (offer SDP)
- Generates SDP answer
- Sends answer to server (registers as answerer)
- Sets up ICE candidate handler (before gathering starts)
- Sets local description → ICE gathering begins
- State →
exchanging-ice
- State →
-
ICE Exchange (Trickle ICE):
- Both peers generate ICE candidates as they're discovered
- Candidates are automatically sent to server immediately
- Peers poll and receive remote candidates (every 1 second)
- ICE establishes the direct P2P path
- State →
connected
-
Connection Established:
- Data channel opens
- Chat messages flow directly between peers
- No server relay (true P2P!)
Key Features of Implementation
- Trickle ICE: Candidates sent immediately as discovered (no waiting)
- Proper Authorization: Answer sent to server before ICE gathering to authorize candidate posting
- Event Cleanup: All event listeners properly removed with
removeEventListener - State Management: Clean state machine with well-defined transitions
- Error Handling: Graceful failure states with error events
Architecture
- Frontend: React + Vite
- Signaling: Rondevu server (Cloudflare Workers + D1)
- Client: @xtr-dev/rondevu-client (TypeScript library)
- WebRTC: RTCPeerConnection with STUN/TURN servers
- Connection Management: RondevuPeer class with state machine
Server Configuration
This demo connects to: https://api.ronde.vu
To use a different server, modify API_URL in src/App.jsx:
const API_URL = 'https://your-server.com';
Deployment
Deploy to Cloudflare Pages
Quick Deploy via Wrangler:
npm run build
npx wrangler pages deploy dist --project-name=rondevu-demo
Or via Git Integration:
- Push to GitHub/GitLab
- Connect to Cloudflare Pages
- Set build command:
npm run build - Set output directory:
dist - Deploy automatically on every push!
Development Notes
- Credentials are stored in localStorage and persist across sessions
- Offers expire after 5 minutes by default
- The peer automatically polls for answers and ICE candidates
- Multiple simultaneous connections are supported
- WebRTC uses Google's public STUN servers + custom TURN server for NAT traversal
- Data channel messages are unreliable but fast (perfect for chat)
- Connection cleanup is automatic when peers disconnect
Connection Timeouts
The demo uses these default timeouts:
- ICE Gathering: 10 seconds (not used with trickle ICE)
- Waiting for Answer: 30 seconds
- Creating Answer: 10 seconds
- ICE Connection: 30 seconds
These can be customized in the PeerOptions:
await peer.createOffer({
topics: ['my-topic'],
timeouts: {
waitingForAnswer: 60000, // 1 minute
iceConnection: 45000 // 45 seconds
}
});
Technologies
- React - UI framework
- Vite - Build tool and dev server
- @xtr-dev/rondevu-client - Rondevu client library with
RondevuPeer - RTCPeerConnection - WebRTC connections
- RTCDataChannel - P2P messaging
- QRCode - QR code generation for easy topic sharing
License
MIT