Skip to main content

Command Palette

Search for a command to run...

Building a Real-Time Watch-Party Application with Node.js and Socket.IO

Published
4 min read
Building a Real-Time Watch-Party Application with Node.js and Socket.IO
M

I'm a Student, Developer, Writer and a Tech Geek

Watch-party applications have gained popularity for enabling synchronized viewing experiences among users separated by distance. Leveraging Node.js for server-side logic and Socket.IO for real-time communication, we can construct a robust platform for hosting virtual movie nights with seamless synchronization and interactive features. In this technical guide, we'll walk through the process of creating a watch-party application from scratch, complete with code snippets for implementation.

Setting Up the Development Environment

Ensure you have Node.js and npm installed on your machine. Initialize a new Node.js project and install the required dependencies:

mkdir watch-party-app
cd watch-party-app
npm init -y
npm install express socket.io

Server-Side Implementation (server.js)

First, let's set up our Node.js server using Express.js and Socket.IO:

const express = require("express");
const http = require("http");
const socketIO = require("socket.io");

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

app.use(express.static("public"));

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/public/index.html");
});

const users = {};
let videoState = { playing: false, currentTime: 0 };

io.on("connection", (socket) => {
  // User joins the room
  socket.on("join", (username) => {
    users[socket.id] = username;
    io.emit("updateUsers", Object.values(users));
    socket.broadcast.emit("message", `${username} has joined the room`);
    socket.emit("videoSync", videoState);
  });

  // User sends a chat message
  socket.on("chatMessage", (message) => {
    io.emit("message", `${users[socket.id]}: ${message}`);
  });

   // User plays, pauses, or seeks the video
  socket.on('videoControl', (data) => {
    videoState = data;
    io.emit('videoControl', data);
  });

  // User leaves the room
  socket.on("disconnect", () => {
    io.emit("message", `${users[socket.id]} has left the room`);
    delete users[socket.id];
    io.emit("updateUsers", Object.values(users));
  });
});

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

Client-Side Implementation (index.html)

Now, let's create the client-side interface for our watch-party application. Create a new folder and name it "public". In the public folder create a new file and name it "index.html":

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Watch Party App</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      margin: 0;
      padding: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      height: auto;
      background-color: #f0f0f0;
    }

    #container {
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 20px;
      background-color: #fff;
      border-radius: 8px;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    }

    #chat {
      display: flex;
      flex-direction: column;
      align-items: center;
      margin-bottom: 20px;
    }

    #messages {
      list-style-type: none;
      padding: 0;
      margin: 0;
      max-height: 300px;
      overflow-y: auto;
      border: 1px solid #ddd;
      border-radius: 4px;
      padding: 10px;
      background-color: #f9f9f9;
    }

    input {
      margin-top: 10px;
      padding: 8px;
      border: 1px solid #ddd;
      border-radius: 4px;
      overflow-y: auto;
    }

    button {
      margin-top: 10px;
      padding: 8px;
      background-color: #4caf50;
      color: #fff;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    }

    #users {
      font-style: italic;
      color: #555;
    }
  </style>
</head>
<body>
  <div id="container">
    <h1>Watch Party</h1>

    <div>

        <video id="videoPlayer" width="640" height="360" controls>
        <source src="./vtest1.mp4" type="video/mp4">
        Your browser does not support the video tag.
      </video>
    </div>

    <div id="chat">
      <ul id="messages"></ul>
      <input id="m" placeholder="Type your message..." autocomplete="off"/>
      <button onclick="sendMessage()" >Send</button>
    </div>

    <div id="users">Users: </div>
  </div>

  <script src="/socket.io/socket.io.js"></script>
  <script src="script.js"></script>
</body>
</html>

Connection between Client-Server (script.js)

Now, let's create a new file in the public folder and name it "script.js".

document.addEventListener("DOMContentLoaded", () => {
  const socket = io();
  const video = document.getElementById("videoPlayer");

  // Prompt for username and join the room
  const username = prompt("Enter your username:");
  socket.emit("join", username);

  // Update users list
  socket.on("updateUsers", (users) => {
    document.getElementById("users").innerHTML = `Users: ${users.join(", ")}`;
  });

  // Receive and display chat messages
  socket.on("message", (data) => {
    const messages = document.getElementById("messages");
    const item = document.createElement("li");
    // item.textContent = `${data.username}: ${data.message}`;
    item.textContent = data;
    messages.appendChild(item);
  });

  // Receive and handle video control events
  socket.on("videoControl", (data) => {
    if (data.playing) {
      video.play();
    } else {
      video.pause();
    }
    video.currentTime = data.currentTime;
  });

  // Send chat message
  window.sendMessage = () => {
    const input = document.getElementById("m");
    const message = input.value.trim();
    if (message !== "") {
      socket.emit("chatMessage", message);
      input.value = "";
    }
  };

  // Update video state when playing, pausing, or seeking
  video.addEventListener("play", () => {
    const currentTime = video.currentTime;
    socket.emit("videoControl", { playing: true, currentTime });
  });

  video.addEventListener("pause", () => {
    const currentTime = video.currentTime;
    socket.emit("videoControl", { playing: false, currentTime });
  });

  // Handle file select and emit the video to the server
  window.handleFileSelect = () => {
    const file = videoInput.files[0];
    if (file) {
      const reader = new FileReader();
      reader.onload = (event) => {
        const videoData = {
          name: file.name,
          type: file.type,
          data: event.target.result,
        };
        socket.emit("uploadVideo", videoData);
      };
      reader.readAsDataURL(file);
    }
  };


});

Deploying the Application

Once you've completed development and testing locally, deploy your application to a server or cloud platform for public access. Utilize platforms like Render or AWS for hosting your Node.js application. I would recommend render.com which has free plans.

Conclusion

By following this guide and implementing the provided code snippets, you've created a real-time watch-party application using Node.js and Socket.IO. Users can now enjoy synchronized video playback and engage in real-time chat during movie nights, regardless of their physical location. Experiment with additional features and enhancements to tailor the application to your specific requirements. Happy coding!

GitHub Link: https://github.com/manoj-narasimha/watch-party-app
Follow me for more articles like this ☝️