Elysia Tools

Browse tools, examples, visual explainers, and topic guides from one clear workspace.

Top Categories

Hubs

Language
ZHESPTRUFRDEEN

Elysia Tools

Navigation

Web Technologies

WebRTC Real-Time Communication Samples

Comprehensive WebRTC samples for peer-to-peer audio/video communication, data channels, screen sharing, and signaling server implementation

Samples

Entries inside this sample collection

CodeIntermediate

Peer-to-Peer Video Call Implementation

Complete WebRTC video call setup with camera access, audio streams, and connection establishment

Difficulty

7/10

Estimated time

30 min

Tags

webrtc, p2p, video, audio, peer-connection, media-stream

plaintext
// WebRTC Peer-to-Peer Video Call Implementation
// Complete setup for video and audio communication

class WebRTCPeerConnection {
  constructor(config = {}) {
    this.localVideo = document.getElementById('localVideo');
    this.remoteVideo = document.getElementById('remoteVideo');

    this.pc = new RTCPeerConnection({
      iceServers: [
        { urls: 'stun:stun.l.google.com:19302' },
        {
          urls: 'turn:turn.example.com:3478',
          username: 'user',
          credential: 'password'
        }
      ],
      ...config
    });

    this.setupPeerConnection();
    this.setupMediaStreams();
  }

  async setupMediaStreams() {
    try {
      // Get local media stream with video and audio
      const stream = await navigator.mediaDevices.getUserMedia({
        video: {
          width: { ideal: 1280 },
          height: { ideal: 720 },
          facingMode: 'user'
        },
        audio: {
          echoCancellation: true,
          noiseSuppression: true,
          autoGainControl: true
        }
      });

      this.localVideo.srcObject = stream;

      // Add tracks to peer connection
      stream.getTracks().forEach(track => {
        this.pc.addTrack(track, stream);
      });

    } catch (error) {
      console.error('Error accessing media devices:', error);
    }
  }

  setupPeerConnection() {
    // Handle incoming tracks
    this.pc.ontrack = (event) => {
      if (event.streams && event.streams[0]) {
        this.remoteVideo.srcObject = event.streams[0];
      }
    };

    // Handle ICE candidates
    this.pc.onicecandidate = (event) => {
      if (event.candidate) {
        // Send candidate to remote peer via signaling server
        this.sendSignalingMessage({
          type: 'ice-candidate',
          candidate: event.candidate
        });
      }
    };

    // Handle connection state changes
    this.pc.onconnectionstatechange = () => {
      console.log('Connection state:', this.pc.connectionState);

      if (this.pc.connectionState === 'connected') {
        console.log('WebRTC connection established!');
      }
    };

    // Handle ICE connection state changes
    this.pc.oniceconnectionstatechange = () => {
      console.log('ICE connection state:', this.pc.iceConnectionState);
    };
  }

  async createOffer() {
    try {
      const offer = await this.pc.createOffer({
        offerToReceiveAudio: true,
        offerToReceiveVideo: true
      });

      await this.pc.setLocalDescription(offer);

      // Send offer to remote peer
      this.sendSignalingMessage({
        type: 'offer',
        offer: offer
      });

    } catch (error) {
      console.error('Error creating offer:', error);
    }
  }

  async handleOffer(offer) {
    try {
      await this.pc.setRemoteDescription(offer);

      const answer = await this.pc.createAnswer();
      await this.pc.setLocalDescription(answer);

      // Send answer back
      this.sendSignalingMessage({
        type: 'answer',
        answer: answer
      });

    } catch (error) {
      console.error('Error handling offer:', error);
    }
  }

  async handleAnswer(answer) {
    try {
      await this.pc.setRemoteDescription(answer);
    } catch (error) {
      console.error('Error handling answer:', error);
    }
  }

  async handleIceCandidate(candidate) {
    try {
      await this.pc.addIceCandidate(candidate);
    } catch (error) {
      console.error('Error adding ICE candidate:', error);
    }
  }

  sendSignalingMessage(message) {
    // Implement WebSocket or signaling server communication
    console.log('Sending signaling message:', message);
  }

  // Media controls
  toggleAudio() {
    const audioTrack = this.localVideo.srcObject.getAudioTracks()[0];
    if (audioTrack) {
      audioTrack.enabled = !audioTrack.enabled;
    }
  }

  toggleVideo() {
    const videoTrack = this.localVideo.srcObject.getVideoTracks()[0];
    if (videoTrack) {
      videoTrack.enabled = !videoTrack.enabled;
    }
  }

  // Hang up the call
  hangup() {
    if (this.pc) {
      this.pc.close();
      this.pc = null;
    }

    // Stop local media
    if (this.localVideo.srcObject) {
      this.localVideo.srcObject.getTracks().forEach(track => track.stop());
      this.localVideo.srcObject = null;
    }

    this.remoteVideo.srcObject = null;
  }
}

// Usage example
const webrtcConnection = new WebRTCPeerConnection();

// Start the call
webrtcConnection.createOffer();
format
JavaScript
apis
["RTCPeerConnection","MediaDevices","MediaStream"]
features
["ice-handling","media-stream","connection-management"]
browsers
["Chrome","Firefox","Safari","Edge"]
CodeIntermediate

WebRTC Data Channel Communication

Implementation of reliable and unreliable data channels for real-time text, file transfer, and game communication

Difficulty

6/10

Estimated time

25 min

Tags

data-channel, real-time, messaging, file-transfer, p2p

plaintext
// WebRTC Data Channel Implementation
// Real-time data communication between peers

class WebRTCDataChannel {
  constructor(peerConnection) {
    this.pc = peerConnection;
    this.dataChannel = null;
    this.messageCallbacks = new Map();

    this.setupDataChannel();
  }

  setupDataChannel() {
    // Create data channel for reliable messaging
    this.dataChannel = this.pc.createDataChannel('chat', {
      ordered: true,
      maxRetransmits: 3,
      protocol: 'json'
    });

    this.setupDataChannelHandlers();
  }

  setupDataChannelHandlers() {
    this.dataChannel.onopen = () => {
      console.log('Data channel opened');
      this.onChannelOpen();
    };

    this.dataChannel.onmessage = (event) => {
      this.handleMessage(event.data);
    };

    this.dataChannel.onclose = () => {
      console.log('Data channel closed');
      this.onChannelClose();
    };

    this.dataChannel.onerror = (error) => {
      console.error('Data channel error:', error);
      this.onChannelError(error);
    };
  }

  handleMessage(data) {
    try {
      const message = JSON.parse(data);

      switch (message.type) {
        case 'chat':
          this.onChatMessage(message);
          break;
        case 'file':
          this.onFileTransfer(message);
          break;
        case 'typing':
          this.onTypingIndicator(message);
          break;
        case 'cursor':
          this.onCursorUpdate(message);
          break;
        default:
          console.log('Unknown message type:', message.type);
      }

      // Call custom callbacks
      if (this.messageCallbacks.has(message.type)) {
        this.messageCallbacks.get(message.type)(message);
      }

    } catch (error) {
      console.error('Error parsing message:', error);
    }
  }

  // Send different types of messages
  sendChatMessage(text, sender = 'Anonymous') {
    const message = {
      type: 'chat',
      text: text,
      sender: sender,
      timestamp: Date.now()
    };

    this.sendMessage(message);
  }

  sendFile(file, onProgress) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();

      reader.onload = (event) => {
        const message = {
          type: 'file',
          name: file.name,
          size: file.size,
          mimeType: file.type,
          data: event.target.result
        };

        this.sendMessage(message);
        resolve(message);
      };

      reader.onerror = (error) => reject(error);
      reader.readAsArrayBuffer(file);
    });
  }

  sendTypingIndicator(isTyping) {
    const message = {
      type: 'typing',
      isTyping: isTyping,
      timestamp: Date.now()
    };

    this.sendMessage(message);
  }

  sendCursorUpdate(x, y) {
    const message = {
      type: 'cursor',
      x: x,
      y: y,
      timestamp: Date.now()
    };

    this.sendMessage(message);
  }

  sendCustomMessage(type, data) {
    const message = {
      type: type,
      data: data,
      timestamp: Date.now()
    };

    this.sendMessage(message);
  }

  sendMessage(message) {
    if (this.dataChannel && this.dataChannel.readyState === 'open') {
      this.dataChannel.send(JSON.stringify(message));
    } else {
      console.error('Data channel is not open');
    }
  }

  // Event handlers (can be overridden)
  onChannelOpen() {
    console.log('Data channel ready for communication');
  }

  onChannelClose() {
    console.log('Data channel communication ended');
  }

  onChannelError(error) {
    console.error('Data channel communication error:', error);
  }

  onChatMessage(message) {
    console.log('Chat message received:', message);
  }

  onFileTransfer(message) {
    console.log('File transfer received:', message);
  }

  onTypingIndicator(message) {
    console.log('Typing indicator:', message);
  }

  onCursorUpdate(message) {
    console.log('Cursor update:', message);
  }

  // Register custom message handlers
  onMessageType(type, callback) {
    this.messageCallbacks.set(type, callback);
  }

  // Data channel status
  isOpen() {
    return this.dataChannel && this.dataChannel.readyState === 'open';
  }

  close() {
    if (this.dataChannel) {
      this.dataChannel.close();
      this.dataChannel = null;
    }
  }
}

// File transfer helper class
class FileTransferHandler {
  constructor(dataChannel) {
    this.dataChannel = dataChannel;
    this.receivingFiles = new Map();
  }

  async receiveFile(fileInfo, onProgress) {
    const chunks = [];
    let receivedBytes = 0;

    return new Promise((resolve, reject) => {
      const handleChunk = (chunk) => {
        chunks.push(chunk);
        receivedBytes += chunk.byteLength;

        if (onProgress) {
          onProgress(receivedBytes, fileInfo.size);
        }

        if (receivedBytes >= fileInfo.size) {
          const blob = new Blob(chunks, { type: fileInfo.type });
          const file = new File([blob], fileInfo.name, { type: fileInfo.type });
          resolve(file);
        }
      };

      this.dataChannel.onMessageType('file-chunk', (message) => {
        if (message.fileId === fileInfo.id) {
          handleChunk(message.data);
        }
      });
    });
  }

  async sendFile(file, onProgress) {
    const chunkSize = 16384; // 16KB chunks
    const fileId = Date.now().toString();

    // Send file metadata
    this.dataChannel.sendCustomMessage('file-info', {
      id: fileId,
      name: file.name,
      size: file.size,
      type: file.type
    });

    // Send file in chunks
    const reader = new FileReader();
    let offset = 0;

    const sendNextChunk = () => {
      const chunk = file.slice(offset, offset + chunkSize);
      reader.readAsArrayBuffer(chunk);
    };

    reader.onload = (event) => {
      this.dataChannel.sendCustomMessage('file-chunk', {
        fileId: fileId,
        data: event.target.result
      });

      offset += chunkSize;

      if (onProgress) {
        onProgress(offset, file.size);
      }

      if (offset < file.size) {
        setTimeout(sendNextChunk, 10); // Small delay to prevent blocking
      }
    };

    sendNextChunk();
  }
}

// Usage example
const webrtcDataChannel = new WebRTCDataChannel(peerConnection);

// Set up message handlers
webrtcDataChannel.onMessageType('game-state', (message) => {
  updateGameDisplay(message.data);
});

// Send messages
webrtcDataChannel.sendChatMessage('Hello from peer!');
webrtcDataChannel.sendTypingIndicator(true);
format
JavaScript
apis
["RTCDataChannel","FileReader","Blob"]
features
["reliable-messaging","file-transfer","custom-protocols"]
protocols
["JSON","Binary"]
CodeIntermediate

Screen Sharing Implementation

Complete screen sharing and desktop capture setup with tab sharing and audio support

Difficulty

6/10

Estimated time

20 min

Tags

screen-share, desktop-capture, tab-sharing, picture-in-picture, recording

plaintext
// WebRTC Screen Sharing Implementation
// Screen capture, tab sharing, and audio support

class ScreenShareManager {
  constructor(peerConnection) {
    this.pc = peerConnection;
    this.screenStream = null;
    this.localVideo = document.getElementById('localScreen');
    this.isSharing = false;
  }

  async startScreenShare(options = {}) {
    try {
      // Get screen share stream
      this.screenStream = await navigator.mediaDevices.getDisplayMedia({
        video: {
          cursor: options.cursor || 'always',
          displaySurface: options.displaySurface || 'monitor'
        },
        audio: options.audio || false,
        preferCurrentTab: options.preferCurrentTab || false
      });

      // Replace local video with screen share
      this.localVideo.srcObject = this.screenStream;

      // Add or replace video tracks in peer connection
      const videoTracks = this.screenStream.getVideoTracks();
      const sender = this.pc.getSenders().find(s =>
        s.track && s.track.kind === 'video'
      );

      if (sender && videoTracks.length > 0) {
        // Replace existing video track
        await sender.replaceTrack(videoTracks[0]);
      } else if (videoTracks.length > 0) {
        // Add new video track
        this.pc.addTrack(videoTracks[0], this.screenStream);
      }

      // Add audio tracks if available
      const audioTracks = this.screenStream.getAudioTracks();
      audioTracks.forEach(track => {
        const audioSender = this.pc.getSenders().find(s =>
          s.track && s.track.kind === 'audio'
        );

        if (audioSender) {
          audioSender.replaceTrack(track);
        } else {
          this.pc.addTrack(track, this.screenStream);
        }
      });

      this.isSharing = true;

      // Listen for stream end
      this.screenStream.getVideoTracks()[0].onended = () => {
        this.stopScreenShare();
      };

      return this.screenStream;

    } catch (error) {
      console.error('Error starting screen share:', error);
      throw error;
    }
  }

  async startTabShare() {
    try {
      // Use Chrome extension API for tab sharing (if available)
      if (chrome && chrome.desktopCapture) {
        return new Promise((resolve, reject) => {
          chrome.desktopCapture.chooseDesktopMedia(
            ['screen', 'window', 'tab'],
            (streamId) => {
              if (streamId) {
                navigator.mediaDevices.getUserMedia({
                  video: {
                    mandatory: {
                      chromeMediaSource: 'desktop',
                      chromeMediaSourceId: streamId
                    }
                  }
                }).then(resolve).catch(reject);
              } else {
                reject(new Error('No stream selected'));
              }
            }
          );
        });
      } else {
        // Fallback to standard screen capture with tab preference
        return this.startScreenShare({
          preferCurrentTab: true,
          displaySurface: 'browser'
        });
      }
    } catch (error) {
      console.error('Error starting tab share:', error);
      throw error;
    }
  }

  async stopScreenShare() {
    try {
      // Stop all tracks in screen stream
      if (this.screenStream) {
        this.screenStream.getTracks().forEach(track => track.stop());
        this.screenStream = null;
      }

      // Clear local video
      this.localVideo.srcObject = null;

      // Try to restore camera stream if available
      await this.restoreCameraStream();

      this.isSharing = false;

    } catch (error) {
      console.error('Error stopping screen share:', error);
    }
  }

  async restoreCameraStream() {
    try {
      const cameraStream = await navigator.mediaDevices.getUserMedia({
        video: true,
        audio: true
      });

      this.localVideo.srcObject = cameraStream;

      // Replace tracks in peer connection
      const videoTracks = cameraStream.getVideoTracks();
      const sender = this.pc.getSenders().find(s =>
        s.track && s.track.kind === 'video'
      );

      if (sender && videoTracks.length > 0) {
        await sender.replaceTrack(videoTracks[0]);
      }

      // Add audio tracks
      const audioTracks = cameraStream.getAudioTracks();
      audioTracks.forEach(track => {
        const audioSender = this.pc.getSenders().find(s =>
          s.track && s.track.kind === 'audio'
        );

        if (audioSender) {
          audioSender.replaceTrack(track);
        }
      });

    } catch (error) {
      console.error('Error restoring camera stream:', error);
    }
  }

  async toggleScreenShare() {
    if (this.isSharing) {
      await this.stopScreenShare();
    } else {
      await this.startScreenShare();
    }
  }

  // Get available screen sources
  async getScreenSources() {
    try {
      if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
        const devices = await navigator.mediaDevices.enumerateDevices();
        return devices.filter(device => device.kind === 'videoinput');
      }
      return [];
    } catch (error) {
      console.error('Error getting screen sources:', error);
      return [];
    }
  }

  // Check screen sharing capabilities
  getCapabilities() {
    return {
      screenCapture: !!(navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia),
      tabCapture: !!(chrome && chrome.desktopCapture),
      audioCapture: !!(navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia),
      cursorSupport: true,
      multipleSources: true
    };
  }

  // Add picture-in-picture support
  async enablePictureInPicture() {
    try {
      if (this.localVideo && 'requestPictureInPicture' in this.localVideo) {
        await this.localVideo.requestPictureInPicture();
      }
    } catch (error) {
      console.error('Error enabling picture-in-picture:', error);
    }
  }

  disablePictureInPicture() {
    if (this.localVideo && 'exitPictureInPicture' in this.localVideo) {
      this.localVideo.exitPictureInPicture();
    }
  }

  // Recording functionality
  startRecording() {
    if (!this.screenStream) return null;

    const chunks = [];
    const mediaRecorder = new MediaRecorder(this.screenStream, {
      mimeType: MediaRecorder.isTypeSupported('video/webm;codecs=vp9')
        ? 'video/webm;codecs=vp9'
        : 'video/webm'
    });

    mediaRecorder.ondataavailable = (event) => {
      if (event.data.size > 0) {
        chunks.push(event.data);
      }
    };

    mediaRecorder.onstop = () => {
      const blob = new Blob(chunks, { type: 'video/webm' });
      this.onRecordingComplete(blob);
    };

    mediaRecorder.start();
    return mediaRecorder;
  }

  onRecordingComplete(blob) {
    // Handle recording completion
    console.log('Screen share recording completed:', blob);

    // Create download link
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `screen-share-${Date.now()}.webm`;
    a.click();
    URL.revokeObjectURL(url);
  }

  // Get current status
  getStatus() {
    return {
      isSharing: this.isSharing,
      hasVideo: !!(this.screenStream && this.screenStream.getVideoTracks().length),
      hasAudio: !!(this.screenStream && this.screenStream.getAudioTracks().length),
      trackCount: this.screenStream ? this.screenStream.getTracks().length : 0
    };
  }
}

// UI Integration Example
class ScreenShareUI {
  constructor(screenShareManager) {
    this.screenShare = screenShareManager;
    this.setupUI();
  }

  setupUI() {
    // Create control buttons
    this.createShareButton();
    this.createTabShareButton();
    this.createStopButton();
    this.createRecordButton();

    // Create status display
    this.createStatusDisplay();
  }

  createShareButton() {
    const button = document.createElement('button');
    button.textContent = 'Share Screen';
    button.onclick = async () => {
      try {
        await this.screenShare.startScreenShare();
        this.updateStatus();
      } catch (error) {
        this.showError('Failed to start screen share: ' + error.message);
      }
    };
    document.body.appendChild(button);
  }

  createTabShareButton() {
    const button = document.createElement('button');
    button.textContent = 'Share Tab';
    button.onclick = async () => {
      try {
        await this.screenShare.startTabShare();
        this.updateStatus();
      } catch (error) {
        this.showError('Failed to start tab share: ' + error.message);
      }
    };
    document.body.appendChild(button);
  }

  createStopButton() {
    const button = document.createElement('button');
    button.textContent = 'Stop Sharing';
    button.onclick = async () => {
      await this.screenShare.stopScreenShare();
      this.updateStatus();
    };
    document.body.appendChild(button);
  }

  createRecordButton() {
    const button = document.createElement('button');
    button.textContent = 'Start Recording';

    button.onclick = () => {
      if (!this.recording) {
        this.recording = this.screenShare.startRecording();
        button.textContent = 'Stop Recording';
      } else {
        this.recording.stop();
        this.recording = null;
        button.textContent = 'Start Recording';
      }
    };

    document.body.appendChild(button);
  }

  createStatusDisplay() {
    this.statusDiv = document.createElement('div');
    this.statusDiv.style.marginTop = '10px';
    this.updateStatus();
    document.body.appendChild(this.statusDiv);
  }

  updateStatus() {
    const status = this.screenShare.getStatus();
    this.statusDiv.innerHTML = `
      <div>Sharing: ${status.isSharing ? 'Yes' : 'No'}</div>
      <div>Video: ${status.hasVideo ? 'Yes' : 'No'}</div>
      <div>Audio: ${status.hasAudio ? 'Yes' : 'No'}</div>
      <div>Tracks: ${status.trackCount}</div>
    `;
  }

  showError(message) {
    alert(message);
  }
}

// Usage example
const screenShareManager = new ScreenShareManager(peerConnection);
const screenShareUI = new ScreenShareUI(screenShareManager);
format
JavaScript
apis
["getDisplayMedia","MediaRecorder","PictureInPicture"]
features
["screen-capture","tab-capture","audio-capture","recording"]
browsers
["Chrome","Firefox","Edge"]
CodeComplex

WebRTC Signaling Server

WebSocket-based signaling server for WebRTC peer connection establishment and media negotiation

Difficulty

8/10

Estimated time

35 min

Tags

signaling, websocket, peer-connection, room-management, server

plaintext
// WebRTC Signaling Server Implementation
// Node.js WebSocket server for peer connection coordination

const WebSocket = require('ws');
const http = require('http');
const express = require('express');
const cors = require('cors');

class WebRTCSignalingServer {
  constructor(port = 8080) {
    this.port = port;
    this.app = express();
    this.server = http.createServer(this.app);
    this.wss = new WebSocket.Server({ server: this.server });

    this.rooms = new Map(); // room_id -> Set of peers
    this.peers = new Map(); // peer_id -> { ws, room_id, metadata }

    this.setupExpress();
    this.setupWebSocket();
  }

  setupExpress() {
    this.app.use(cors());
    this.app.use(express.json());

    // Static files for client
    this.app.use(express.static('public'));

    // API endpoints
    this.app.get('/api/rooms', (req, res) => {
      const rooms = Array.from(this.rooms.keys()).map(roomId => ({
        id: roomId,
        peerCount: this.rooms.get(roomId).size
      }));
      res.json(rooms);
    });

    this.app.post('/api/rooms', (req, res) => {
      const roomId = this.generateRoomId();
      this.rooms.set(roomId, new Set());
      res.json({ roomId });
    });

    this.app.get('/api/rooms/:roomId/peers', (req, res) => {
      const roomId = req.params.roomId;
      const roomPeers = this.rooms.get(roomId);

      if (!roomPeers) {
        return res.status(404).json({ error: 'Room not found' });
      }

      const peers = Array.from(roomPeers).map(peerId => ({
        id: peerId,
        metadata: this.peers.get(peerId)?.metadata
      }));

      res.json(peers);
    });
  }

  setupWebSocket() {
    this.wss.on('connection', (ws, req) => {
      const peerId = this.generatePeerId();

      console.log(`Peer connected: ${peerId}`);

      // Store peer connection
      this.peers.set(peerId, {
        ws: ws,
        room_id: null,
        metadata: {
          userAgent: req.headers['user-agent'],
          ip: req.socket.remoteAddress,
          connectedAt: Date.now()
        }
      });

      // Send peer ID to client
      this.sendToPeer(peerId, {
        type: 'peer-id',
        peerId: peerId
      });

      // Handle messages
      ws.on('message', (message) => {
        try {
          const data = JSON.parse(message);
          this.handleMessage(peerId, data);
        } catch (error) {
          console.error(`Invalid message from peer ${peerId}:`, error);
          this.sendError(peerId, 'Invalid message format');
        }
      });

      // Handle disconnection
      ws.on('close', () => {
        console.log(`Peer disconnected: ${peerId}`);
        this.handlePeerDisconnect(peerId);
      });

      // Handle errors
      ws.on('error', (error) => {
        console.error(`WebSocket error for peer ${peerId}:`, error);
      });
    });
  }

  handleMessage(peerId, message) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    switch (message.type) {
      case 'join-room':
        this.handleJoinRoom(peerId, message.roomId);
        break;

      case 'leave-room':
        this.handleLeaveRoom(peerId);
        break;

      case 'offer':
        this.handleRelayMessage(peerId, message, 'offer');
        break;

      case 'answer':
        this.handleRelayMessage(peerId, message, 'answer');
        break;

      case 'ice-candidate':
        this.handleRelayMessage(peerId, message, 'ice-candidate');
        break;

      case 'room-info':
        this.sendRoomInfo(peerId);
        break;

      case 'peer-list':
        this.sendPeerList(peerId);
        break;

      case 'direct-message':
        this.handleDirectMessage(peerId, message);
        break;

      default:
        console.log(`Unknown message type: ${message.type}`);
    }
  }

  handleJoinRoom(peerId, roomId) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    // Leave current room if in one
    if (peer.room_id) {
      this.handleLeaveRoom(peerId);
    }

    // Join new room
    if (!this.rooms.has(roomId)) {
      this.rooms.set(roomId, new Set());
    }

    this.rooms.get(roomId).add(peerId);
    peer.room_id = roomId;

    console.log(`Peer ${peerId} joined room ${roomId}`);

    // Notify room about new peer
    this.broadcastToRoom(roomId, {
      type: 'peer-joined',
      peerId: peerId,
      metadata: peer.metadata
    }, peerId);

    // Send room info to new peer
    this.sendRoomInfo(peerId);
  }

  handleLeaveRoom(peerId) {
    const peer = this.peers.get(peerId);
    if (!peer || !peer.room_id) return;

    const roomId = peer.room_id;
    this.rooms.get(roomId).delete(peerId);

    // Clean up empty rooms
    if (this.rooms.get(roomId).size === 0) {
      this.rooms.delete(roomId);
    }

    console.log(`Peer ${peerId} left room ${roomId}`);

    // Notify room about peer leaving
    this.broadcastToRoom(roomId, {
      type: 'peer-left',
      peerId: peerId
    });

    peer.room_id = null;
  }

  handleRelayMessage(senderId, message, messageType) {
    const peer = this.peers.get(senderId);
    if (!peer || !peer.room_id) return;

    // Add sender info to message
    message.senderId = senderId;

    // Relay to all peers in room except sender
    this.broadcastToRoom(peer.room_id, message, senderId);
  }

  handleDirectMessage(senderId, message) {
    const recipientId = message.recipientId;
    const recipient = this.peers.get(recipientId);

    if (recipient) {
      message.senderId = senderId;
      this.sendToPeer(recipientId, message);
    } else {
      this.sendError(senderId, `Recipient ${recipientId} not found`);
    }
  }

  handlePeerDisconnect(peerId) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    // Leave room if in one
    if (peer.room_id) {
      this.handleLeaveRoom(peerId);
    }

    // Clean up peer data
    this.peers.delete(peerId);
  }

  sendToPeer(peerId, message) {
    const peer = this.peers.get(peerId);
    if (peer && peer.ws.readyState === WebSocket.OPEN) {
      peer.ws.send(JSON.stringify(message));
    }
  }

  sendError(peerId, error) {
    this.sendToPeer(peerId, {
      type: 'error',
      error: error
    });
  }

  broadcastToRoom(roomId, message, excludePeerId = null) {
    const room = this.rooms.get(roomId);
    if (!room) return;

    room.forEach(peerId => {
      if (peerId !== excludePeerId) {
        this.sendToPeer(peerId, message);
      }
    });
  }

  sendRoomInfo(peerId) {
    const peer = this.peers.get(peerId);
    if (!peer || !peer.room_id) return;

    const room = this.rooms.get(peer.room_id);
    const peers = Array.from(room || []).filter(id => id !== peerId);

    this.sendToPeer(peerId, {
      type: 'room-info',
      roomId: peer.room_id,
      peers: peers,
      peerCount: peers.length
    });
  }

  sendPeerList(peerId) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    const allPeers = Array.from(this.peers.keys())
      .filter(id => id !== peerId)
      .map(id => ({
        id: id,
        metadata: this.peers.get(id)?.metadata
      }));

    this.sendToPeer(peerId, {
      type: 'peer-list',
      peers: allPeers
    });
  }

  generatePeerId() {
    return `peer_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  generateRoomId() {
    return Math.random().toString(36).substr(2, 8).toUpperCase();
  }

  start() {
    this.server.listen(this.port, () => {
      console.log(`Signaling server running on port ${this.port}`);
    });
  }

  stop() {
    this.wss.close();
    this.server.close();
  }

  // Server statistics
  getStats() {
    return {
      totalPeers: this.peers.size,
      totalRooms: this.rooms.size,
      rooms: Array.from(this.rooms.entries()).map(([id, peers]) => ({
        id,
        peerCount: peers.size
      }))
    };
  }
}

// Room management extension
class RoomManager {
  constructor(signalingServer) {
    this.server = signalingServer;

    // Add room management endpoints
    this.server.app.delete('/api/rooms/:roomId', (req, res) => {
      const roomId = req.params.roomId;
      const room = this.server.rooms.get(roomId);

      if (!room) {
        return res.status(404).json({ error: 'Room not found' });
      }

      // Disconnect all peers in room
      room.forEach(peerId => {
        this.server.sendToPeer(peerId, {
          type: 'room-closed',
          roomId: roomId,
          reason: 'Room deleted by admin'
        });

        const peer = this.server.peers.get(peerId);
        if (peer) {
          peer.room_id = null;
        }
      });

      this.server.rooms.delete(roomId);
      res.json({ message: 'Room deleted' });
    });

    // Add room settings
    this.server.app.patch('/api/rooms/:roomId/settings', (req, res) => {
      const roomId = req.params.roomId;
      const settings = req.body;

      // Store room settings (implement room settings storage)
      console.log(`Room ${roomId} settings updated:`, settings);

      // Broadcast settings update to room
      this.server.broadcastToRoom(roomId, {
        type: 'room-settings-updated',
        roomId: roomId,
        settings: settings
      });

      res.json({ message: 'Settings updated' });
    });
  }
}

// Usage example
const signalingServer = new WebRTCSignalingServer(8080);
const roomManager = new RoomManager(signalingServer);

// Start server
signalingServer.start();

// Graceful shutdown
process.on('SIGINT', () => {
  console.log('Shutting down signaling server...');
  signalingServer.stop();
  process.exit(0);
});

// Health check endpoint
signalingServer.app.get('/health', (req, res) => {
  const stats = signalingServer.getStats();
  res.json({
    status: 'healthy',
    ...stats
  });
});
format
JavaScript
frameworks
["Node.js","WebSocket","Express"]
features
["room-management","peer-discovery","message-relay"]
protocols
["WebSocket","HTTP"]
CodeComplex

SFU Media Server Implementation

Selective Forwarding Unit (SFU) architecture for scalable multi-participant video conferences

Difficulty

9/10

Estimated time

45 min

Tags

sfu, media-server, scalable, video-conference, simulcast, quality-control

plaintext
// WebRTC SFU (Selective Forwarding Unit) Implementation
// Scalable media server for multi-participant conferences

const WebSocket = require('ws');
const { RTCPeerConnection, RTCSessionDescription } = require('wrtc');
const uuid = require('uuid');

class SFUMediaServer {
  constructor() {
    this.rooms = new Map(); // roomId -> Room instance
    this.peers = new Map(); // peerId -> Peer instance
    this.wss = new WebSocket.Server({ port: 3000 });

    this.setupWebSocket();
  }

  setupWebSocket() {
    this.wss.on('connection', (ws, req) => {
      console.log('New SFU client connected');

      ws.on('message', async (data) => {
        try {
          const message = JSON.parse(data);
          await this.handleMessage(ws, message);
        } catch (error) {
          console.error('Error handling message:', error);
        }
      });

      ws.on('close', () => {
        console.log('SFU client disconnected');
        this.handleDisconnect(ws);
      });
    });
  }

  async handleMessage(ws, message) {
    const { type, roomId, peerId, data } = message;

    switch (type) {
      case 'join-room':
        await this.handleJoinRoom(ws, roomId, peerId, data);
        break;

      case 'leave-room':
        this.handleLeaveRoom(peerId);
        break;

      case 'offer':
        await this.handleOffer(peerId, data);
        break;

      case 'ice-candidate':
        await this.handleIceCandidate(peerId, data);
        break;

      case 'request-tracks':
        await this.handleTrackRequest(peerId, data);
        break;

      case 'update-layout':
        this.handleLayoutUpdate(roomId, data);
        break;

      default:
        console.log(`Unknown message type: ${type}`);
    }
  }

  async handleJoinRoom(ws, roomId, peerId, metadata = {}) {
    try {
      // Create room if it doesn't exist
      if (!this.rooms.has(roomId)) {
        this.rooms.set(roomId, new SFURoom(roomId));
      }

      const room = this.rooms.get(roomId);

      // Create peer instance
      const peer = new SFUPeer(peerId, ws, room, metadata);
      this.peers.set(peerId, peer);

      // Add peer to room
      await room.addPeer(peer);

      console.log(`Peer ${peerId} joined room ${roomId}`);

    } catch (error) {
      console.error('Error joining room:', error);
      ws.send(JSON.stringify({
        type: 'error',
        error: error.message
      }));
    }
  }

  handleLeaveRoom(peerId) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    peer.leave();
    this.peers.delete(peerId);
  }

  async handleOffer(peerId, offerData) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    await peer.handleOffer(offerData);
  }

  async handleIceCandidate(peerId, candidateData) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    await peer.handleIceCandidate(candidateData);
  }

  async handleTrackRequest(peerId, request) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    await peer.handleTrackRequest(request);
  }

  handleLayoutUpdate(roomId, layout) {
    const room = this.rooms.get(roomId);
    if (!room) return;

    room.updateLayout(layout);
  }

  handleDisconnect(ws) {
    // Find and disconnect peer
    for (const [peerId, peer] of this.peers.entries()) {
      if (peer.ws === ws) {
        this.handleLeaveRoom(peerId);
        break;
      }
    }
  }
}

class SFURoom {
  constructor(roomId) {
    this.roomId = roomId;
    this.peers = new Map(); // peerId -> Peer instance
    this.tracks = new Map(); // trackId -> Track instance
    this.layout = 'grid'; // grid, speaker, custom
    this.maxVideoQuality = '1080p';
  }

  async addPeer(peer) {
    this.peers.set(peer.id, peer);

    // Send existing tracks to new peer
    for (const [trackId, track] of this.tracks.entries()) {
      await this.sendTrackToPeer(peer, track);
    }

    // Notify other peers about new peer
    this.broadcastToPeers({
      type: 'peer-joined',
      peerId: peer.id,
      metadata: peer.metadata
    }, peer.id);
  }

  removePeer(peerId) {
    const peer = this.peers.get(peerId);
    if (!peer) return;

    // Clean up peer's tracks
    for (const trackId of peer.trackIds) {
      this.tracks.delete(trackId);
    }

    // Remove peer from room
    this.peers.delete(peerId);

    // Notify other peers
    this.broadcastToPeers({
      type: 'peer-left',
      peerId: peerId
    });

    console.log(`Peer ${peerId} left room ${this.roomId}`);
  }

  async sendTrackToPeer(peer, track) {
    try {
      // Create transceiver for the track
      const transceiver = peer.pc.addTransceiver(track.track, {
        direction: 'sendonly'
      });

      // Store track mapping
      peer.trackMappings.set(track.id, {
        transceiver: transceiver,
        trackId: track.id
      });

      console.log(`Sent track ${track.id} to peer ${peer.id}`);

    } catch (error) {
      console.error(`Error sending track to peer ${peer.id}:`, error);
    }
  }

  addTrack(peerId, track) {
    const trackId = uuid.v4();
    const sfuTrack = new SFUTrack(trackId, peerId, track);

    this.tracks.set(trackId, sfuTrack);

    // Send track to all other peers
    this.broadcastToPeers({
      type: 'track-added',
      trackId: trackId,
      peerId: peerId,
      metadata: sfuTrack.metadata
    }, peerId);

    return sfuTrack;
  }

  removeTrack(trackId) {
    const track = this.tracks.get(trackId);
    if (!track) return;

    this.tracks.delete(trackId);

    // Notify all peers
    this.broadcastToPeers({
      type: 'track-removed',
      trackId: trackId
    });
  }

  updateLayout(layout) {
    this.layout = layout;

    // Recalculate track quality and simulcast layers
    this.optimizeTracks();

    // Notify peers about layout change
    this.broadcastToPeers({
      type: 'layout-updated',
      layout: layout
    });
  }

  optimizeTracks() {
    // Implement track optimization based on layout
    // Adjust quality levels, enable/disable simulcast layers
    console.log(`Optimizing tracks for layout: ${this.layout}`);
  }

  broadcastToPeers(message, excludePeerId = null) {
    for (const [peerId, peer] of this.peers.entries()) {
      if (peerId !== excludePeerId) {
        peer.send(message);
      }
    }
  }
}

class SFUPeer {
  constructor(id, ws, room, metadata = {}) {
    this.id = id;
    this.ws = ws;
    this.room = room;
    this.metadata = metadata;

    this.pc = new RTCPeerConnection({
      iceServers: [
        { urls: 'stun:stun.l.google.com:19302' }
      ]
    });

    this.trackIds = new Set();
    this.trackMappings = new Map();
    this.requestedTracks = new Set();

    this.setupPeerConnection();
    this.setupWebSocket();
  }

  setupPeerConnection() {
    // Handle incoming tracks
    this.pc.ontrack = (event) => {
      console.log(`Received track from peer ${this.id}:`, event.track);

      const trackId = uuid.v4();
      this.trackIds.add(trackId);

      // Add track to room
      this.room.addTrack(this.id, event.track);
    };

    // Handle ICE candidates
    this.pc.onicecandidate = (event) => {
      if (event.candidate) {
        this.send({
          type: 'ice-candidate',
          candidate: event.candidate
        });
      }
    };

    // Handle connection state
    this.pc.onconnectionstatechange = () => {
      console.log(`Peer ${this.id} connection state: ${this.pc.connectionState}`);
    };
  }

  setupWebSocket() {
    this.ws.on('close', () => {
      this.leave();
    });

    this.ws.on('error', (error) => {
      console.error(`WebSocket error for peer ${this.id}:`, error);
    });
  }

  async handleOffer(offerData) {
    try {
      const offer = new RTCSessionDescription(offerData);
      await this.pc.setRemoteDescription(offer);

      // Create answer
      const answer = await this.pc.createAnswer();
      await this.pc.setLocalDescription(answer);

      // Send answer back
      this.send({
        type: 'answer',
        answer: answer
      });

    } catch (error) {
      console.error(`Error handling offer for peer ${this.id}:`, error);
    }
  }

  async handleIceCandidate(candidateData) {
    try {
      const candidate = new RTCIceCandidate(candidateData);
      await this.pc.addIceCandidate(candidate);
    } catch (error) {
      console.error(`Error adding ICE candidate for peer ${this.id}:`, error);
    }
  }

  async handleTrackRequest(request) {
    const { trackId, quality } = request;

    if (!this.room.tracks.has(trackId)) {
      console.warn(`Track ${trackId} not found`);
      return;
    }

    // Adjust track quality based on request
    await this.adjustTrackQuality(trackId, quality);

    console.log(`Peer ${this.id} requested track ${trackId} with quality ${quality}`);
  }

  async adjustTrackQuality(trackId, quality) {
    const track = this.room.tracks.get(trackId);
    if (!track) return;

    // Implement quality adjustment logic
    // This could involve simulcast layer selection or bitrate adjustment
    console.log(`Adjusting track ${trackId} quality to ${quality}`);
  }

  send(message) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(message));
    }
  }

  leave() {
    console.log(`Peer ${this.id} leaving`);

    // Clean up peer connection
    if (this.pc) {
      this.pc.close();
    }

    // Remove from room
    this.room.removePeer(this.id);
  }
}

class SFUTrack {
  constructor(id, peerId, track) {
    this.id = id;
    this.peerId = peerId;
    this.track = track;
    this.metadata = {
      kind: track.kind,
      enabled: track.enabled,
      readyState: track.readyState
    };
    this.quality = 'auto'; // auto, high, medium, low
    this.simulcastLayers = [];
  }

  setQuality(quality) {
    this.quality = quality;
    // Implement actual quality adjustment
  }

  enable() {
    this.track.enabled = true;
    this.metadata.enabled = true;
  }

  disable() {
    this.track.enabled = false;
    this.metadata.enabled = false;
  }
}

// Usage example
const sfuServer = new SFUMediaServer();

console.log('SFU Media Server running on ws://localhost:3000');

// Add graceful shutdown
process.on('SIGINT', () => {
  console.log('Shutting down SFU server...');
  process.exit(0);
});

// Monitoring and statistics
class SFUMonitor {
  constructor(sfuServer) {
    this.sfu = sfuServer;

    // Add monitoring endpoint
    setInterval(() => {
      this.logStats();
    }, 30000); // Log stats every 30 seconds
  }

  logStats() {
    const stats = this.getStats();
    console.log('SFU Statistics:', stats);
  }

  getStats() {
    let totalPeers = 0;
    let totalTracks = 0;

    for (const room of this.sfu.rooms.values()) {
      totalPeers += room.peers.size;
      totalTracks += room.tracks.size;
    }

    return {
      rooms: this.sfu.rooms.size,
      peers: totalPeers,
      tracks: totalTracks,
      timestamp: new Date().toISOString()
    };
  }
}

const monitor = new SFUMonitor(sfuServer);
format
JavaScript
frameworks
["Node.js","WebSocket","WebRTC"]
features
["track-forwarding","quality-control","simulcast","room-management"]
protocols
["WebRTC","WebSocket"]

Tools

Tools frequently paired with this sample

Related

Related sample collections

Related

Related workflows

Tool usage guide

Sample-focused context for search and workflow understanding

Title

WebRTC Real-Time Communication Samples

Description

Comprehensive WebRTC samples for peer-to-peer audio/video communication, data channels, screen sharing, and signaling server implementation

Overview

Comprehensive WebRTC samples for peer-to-peer audio/video communication, data channels, screen sharing, and signaling server implementation

When to use

  • Use this sample set when you want reference inputs for WebRTC Real-Time Communication Samples.
  • Use it to preview output formats and compare related sample variants.

How it works

  1. 1Open a sample entry to inspect the prepared example payload or asset.
  2. 2Compare it with related tools to understand how the workflow connects.

Use cases

  • Peer-to-Peer Video Call Implementation
  • WebRTC Data Channel Communication
  • Screen Sharing Implementation
  • WebRTC Signaling Server
  • SFU Media Server Implementation

FAQ

What is included in WebRTC Real-Time Communication Samples?

This page includes curated sample entries, related tools, and neighboring sample collections for WebRTC Real-Time Communication Samples.

Hub

Prepare and Verify Multilingual Web Typography

Check font coverage, compare heading and body pairs with real copy, configure variable axes, and deliver responsive CSS with verified fallback behavior.

Hub

Observability Debugging from Logs, Traces, Webhooks, and JSON Payloads

Parse logs, decode traces, inspect webhook/API evidence, explore JSON payloads, and extract recurring patterns before writing an incident-ready debugging note.

Hub

Date and Time Normalization and Deadline Calculation

Turn ambiguous or mixed-format date inputs into validated, timezone-aware, business-ready timestamps, deadlines, and duration facts.

Hub

Population Ecology, Biodiversity, and Food Web Calculators

Use educational ecology models to explore population growth, carrying capacity, species interactions, sampled biodiversity, and trophic energy transfer.

Home
Tools3201
Samples593
Visualizations465
Viewers103
Hubs233
Tags333
Images, Audio & Video657

Image, audio, and video processing, conversion, and optimization tools

Math, Date & Finance644

Calculators, numerics, date logic, statistics, and finance tools

Design & Color297

Color, layout, graphics, visual styling, and design helper tools

Text & Writing209

Writing, cleanup, formatting, extraction, and text analysis tools

Developer & Web193

Developer utilities, networking, web debugging, and automation helpers

Conversion & Encoding175

Format, file, archive, unit, and encoding conversion tools

FLAC/WAV Source Screening and Evidence Handoff
Self-Hosted Giveaway Widget Review and Embed Handoff
Stripe Promo Code Batch Review and Import Preflight
Batch Photo Culling Review and Editing Shortlist Handoff
UI Screenshot Structure Review and Development Handoff

© 2026 Elysia Tools. All rights reserved.

DocsPricingAboutTerms of ServicePrivacy PolicyCookiesContact

Data Analysis

Time Series Anomaly Detector

Upload CSV or JSON time series data, detect anomalies with Z-Score and IQR methods, and return a chart-backed report

Data Analysis

Time Series Forecast & Seasonality Analyzer

Forecast future periods from CSV or JSON time-series data and inspect trend, seasonal, and residual decomposition in one report

Document Tools

Fitness Workout PDF Generator

Generate a printable workout-plan PDF from JSON or CSV data with exercise tables, progress boxes, and optional QR video links

Data Processing

JSON File Merger

Merge multiple JSON files with various merging strategies (deep merge, overwrite, combine arrays, etc.)

Security

JWT Generator

Generate JWT tokens with custom claims and algorithms

Format Conversion

Text to Excel Converter

Convert various text formats to Excel with customizable parsing and formatting options

Format Conversion

Text to JSON Converter

Convert various text formats to JSON with customizable parsing options

Document Tools

PDF Sales Dashboard

Create a visual sales dashboard PDF with charts, data tables, and conclusions

Media

QR Code Samples

Sample QR code images for testing QR code reading and generation tools

Document

PDF Samples

Generated PDF samples from tools dated 2026-02-01 to 2026-02-10

DevOps

ELK Stack Log Analysis Samples

Comprehensive ELK Stack (Elasticsearch, Logstash, Kibana) samples for log aggregation, processing, and visualization in distributed systems

C++

Windows Serialization C++ Samples

Windows C++ data serialization examples including JSON serialization/deserialization and XML parsing

Media

SVG Samples

Scalable Vector Graphics (SVG) samples demonstrating various SVG features and techniques

Web Technologies

Progressive Web Applications (PWA)

Build modern, reliable, and engaging Progressive Web Applications with offline capabilities