Eight, the Portuguese dating app,is raising a €3M seed round
Back to Tutorials
techTutorialintermediate

Eight, the Portuguese dating app,is raising a €3M seed round

April 9, 20264 views6 min read

Learn to build a video-first dating app backend using WebRTC and Socket.IO, replicating the core technology behind Eight's real-time video communication system.

Introduction

In this tutorial, we'll build a core component of a video-first dating app similar to Eight's concept. We'll create a system that handles 30-second video intros and instant live calls using WebRTC technology. This implementation focuses on the technical foundation that powers real-time video communication in modern dating applications.

Prerequisites

  • Basic understanding of JavaScript and Node.js
  • Node.js installed (version 14+ recommended)
  • Basic knowledge of WebRTC concepts
  • npm or yarn package manager
  • Basic understanding of WebSocket communication

Step-by-step instructions

Step 1: Set up the project structure

We'll start by creating our project directory and initializing the Node.js application. This setup will include all necessary dependencies for our video dating app backend.

1.1 Create project directory

mkdir eight-video-app
 cd eight-video-app
 npm init -y

Why: This creates our working directory and initializes a package.json file to manage our dependencies.

1.2 Install required dependencies

npm install express socket.io webrtc-adapter

Why: Express provides our web server framework, Socket.IO handles real-time communication, and webrtc-adapter ensures cross-browser compatibility for WebRTC.

Step 2: Create the main server application

Now we'll build our core server that will handle user connections, signaling, and room management for video calls.

2.1 Create server.js file

const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const path = require('path');

const app = express();
const server = http.createServer(app);
const io = socketIo(server);

// Serve static files
app.use(express.static(path.join(__dirname, 'public')));

// In-memory storage for rooms and users
const rooms = new Map();
const users = new Map();

// Handle socket connections
io.on('connection', (socket) => {
  console.log('User connected:', socket.id);

  // Handle user joining a room
  socket.on('join-room', (data) => {
    const { roomId, userId, userName } = data;
    
    // Join the room
    socket.join(roomId);
    
    // Store user information
    users.set(socket.id, { userId, userName, roomId });
    
    // Store room information
    if (!rooms.has(roomId)) {
      rooms.set(roomId, { users: new Set(), createdAt: Date.now() });
    }
    
    rooms.get(roomId).users.add(socket.id);
    
    // Notify others in the room
    socket.to(roomId).emit('user-joined', { userId, userName });
    
    // Send room information to the user
    socket.emit('room-joined', {
      roomId,
      users: Array.from(rooms.get(roomId).users).map(id => users.get(id))
    });
  });

  // Handle signaling messages
  socket.on('signal', (data) => {
    const { to, signal } = data;
    socket.to(to).emit('signal', signal);
  });

  // Handle user disconnection
  socket.on('disconnect', () => {
    const user = users.get(socket.id);
    if (user) {
      const { roomId } = user;
      rooms.get(roomId)?.users.delete(socket.id);
      socket.to(roomId).emit('user-left', { userId: user.userId });
      users.delete(socket.id);
    }
    console.log('User disconnected:', socket.id);
  });
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Why: This creates our core server that handles room management, user connections, and signaling for WebRTC communication between users.

Step 3: Create the frontend interface

Next, we'll build the client-side interface that users will interact with for video intros and calls.

3.1 Create public/index.html

<!DOCTYPE html>
<html>
<head>
  <title>Eight Video Dating</title>
  <meta charset="UTF-8">
  <style>
    body { font-family: Arial, sans-serif; margin: 20px; }
    #video-container { display: flex; justify-content: space-around; }
    video { width: 400px; height: 300px; border: 1px solid #ccc; }
    button { padding: 10px 20px; margin: 5px; }
    #room-id { padding: 5px; width: 200px; }
  </style>
</head>
<body>
  <h1>Eight Video Dating</h1>
  
  <div id="join-section">
    <input type="text" id="user-name" placeholder="Enter your name">
    <input type="text" id="room-id" placeholder="Enter room ID">
    <button onclick="joinRoom()">Join Room</button>
  </div>
  
  <div id="video-section" style="display: none;">
    <div id="video-container">
      <div>
        <h3>Your Video</h3>
        <video id="local-video" autoplay muted></video>
      </div>
      <div>
        <h3>Partner Video</h3>
        <video id="remote-video" autoplay></video>
      </div>
    </div>
    <button onclick="startCall()">Start Call</button>
    <button onclick="endCall()">End Call</button>
  </div>
  
  <script src="/socket.io/socket.io.js"></script>
  <script src="app.js"></script>
</body>
</html>

Why: This HTML file provides the user interface for joining rooms and managing video calls, matching the real-time communication requirements of Eight's concept.

3.2 Create public/app.js

const socket = io();
let localVideo = document.getElementById('local-video');
let remoteVideo = document.getElementById('remote-video');
let localStream;
let remoteStream;
let peerConnection;
let roomId;
let userName;

// Get user media
async function getUserMedia() {
  try {
    localStream = await navigator.mediaDevices.getUserMedia({
      video: true,
      audio: true
    });
    localVideo.srcObject = localStream;
  } catch (error) {
    console.error('Error accessing media devices:', error);
  }
}

// Join a room
function joinRoom() {
  userName = document.getElementById('user-name').value;
  roomId = document.getElementById('room-id').value;
  
  if (!userName || !roomId) {
    alert('Please enter both name and room ID');
    return;
  }
  
  socket.emit('join-room', { roomId, userId: socket.id, userName });
}

// Handle room join success
socket.on('room-joined', (data) => {
  console.log('Joined room:', data.roomId);
  document.getElementById('join-section').style.display = 'none';
  document.getElementById('video-section').style.display = 'block';
  
  // Get user media after joining
  getUserMedia();
});

// Handle user joining
socket.on('user-joined', (data) => {
  console.log('User joined:', data.userName);
});

// Start a call
function startCall() {
  // Create peer connection
  const configuration = {
    iceServers: [
      { urls: 'stun:stun.l.google.com:19302' },
      { urls: 'stun:stun1.l.google.com:19302' }
    ]
  };
  
  peerConnection = new RTCPeerConnection(configuration);
  
  // Add local stream to peer connection
  localStream.getTracks().forEach(track => {
    peerConnection.addTrack(track, localStream);
  });
  
  // Handle remote stream
  peerConnection.ontrack = (event) => {
    remoteStream = event.streams[0];
    remoteVideo.srcObject = remoteStream;
  };
  
  // Handle ICE candidates
  peerConnection.onicecandidate = (event) => {
    if (event.candidate) {
      socket.emit('signal', {
        to: socket.id, // In a real app, you'd identify the specific user
        signal: {
          type: 'candidate',
          candidate: event.candidate
        }
      });
    }
  };
  
  // Create offer
  peerConnection.createOffer()
    .then(offer => peerConnection.setLocalDescription(offer))
    .then(() => {
      socket.emit('signal', {
        to: socket.id,
        signal: {
          type: 'offer',
          sdp: peerConnection.localDescription
        }
      });
    });
}

// End the call
function endCall() {
  if (peerConnection) {
    peerConnection.close();
    peerConnection = null;
  }
  
  if (remoteVideo.srcObject) {
    remoteVideo.srcObject.getTracks().forEach(track => track.stop());
    remoteVideo.srcObject = null;
  }
}

Why: This JavaScript file handles the client-side WebRTC implementation, allowing users to join rooms and make video calls with each other.

Step 4: Run and test the application

4.1 Start the server

node server.js

Why: This starts our Node.js server that will handle user connections and real-time communication.

4.2 Test the application

Open your browser and navigate to http://localhost:3000. You can open multiple browser tabs to simulate multiple users in the same room. Enter names and room IDs to test the video calling functionality.

Summary

This tutorial demonstrates the core technology behind video-first dating apps like Eight. We've built a system that handles room-based communication, user connections, and real-time video calling using WebRTC and Socket.IO. The implementation includes essential features like user joining, signaling, and peer-to-peer video streaming that form the foundation of modern video dating applications. While this is a simplified version, it shows how the technology works at a fundamental level, providing a solid foundation for building more complex features like 30-second intros, user verification, and advanced matching algorithms.

Source: TNW Neural

Related Articles