How to Build SnapDrop WebRTC App with JavaScript?

Learn how to create a SnapDrop-like file-sharing application using WebRTC and JavaScript. Follow our step-by-step guide to build a cross-platform file-sharing tool that works like AirDrop.

Introduction to SnapDrop WebRTC Technology

In today's digital age, sharing files seamlessly across devices is crucial for productivity and convenience. SnapDrop, a web-based file-sharing service, offers a solution reminiscent of Apple's AirDrop, but with broader compatibility across different platforms. Built on WebRTC (Web Real-Time Communication) technology, SnapDrop enables peer-to-peer file sharing without the need for a centralized server.

What is SnapDrop?

SnapDrop is an open-source project that allows users to share files over the same local network. Unlike traditional file-sharing methods that rely on cloud storage or USB drives, SnapDrop leverages the power of WebRTC to establish direct connections between devices. This ensures faster transfer speeds and enhanced privacy, as files do not pass through third-party servers.

Understanding WebRTC

WebRTC is a technology that facilitates real-time communication through peer-to-peer connections. Initially designed for video and audio conferencing, WebRTC has expanded its utility to include data transfer capabilities. By using WebRTC, SnapDrop can efficiently handle file transfers between devices connected to the same network, regardless of the operating system or browser being used.

Benefits of Using SnapDrop

SnapDrop offers several advantages that make it an ideal choice for file sharing:
  • Cross-Platform Compatibility: SnapDrop works on any device with a modern web browser, including Windows, macOS, Linux, iOS, and Android.
  • Ease of Use: No installation or account creation is required. Simply open the SnapDrop website on both devices, and they will recognize each other if connected to the same network.
  • Speed and Efficiency: Direct peer-to-peer connections enable fast file transfers without intermediary steps.
  • Privacy: Files are transferred directly between devices, minimizing the risk of interception by third parties.

Getting Started with the Code!

Create a New SnapDrop App

To get started with creating your own SnapDrop app, you'll need to set up your development environment and install the necessary dependencies. This section will guide you through the initial setup and provide an overview of the project's structure and architecture.

Initial Setup and Requirements

Before diving into the code, ensure you have the following tools installed on your development machine:
  • Node.js and npm: Node.js is a JavaScript runtime, and npm is its package manager. You can download and install them from the

    official Node.js website

    .
  • Git: A version control system for tracking changes in your code. Download and install it from the

    official Git website

    .
Once these tools are installed, you can clone the SnapDrop repository from GitHub:

bash

1git clone https://github.com/RobinLinus/snapdrop.git
2cd snapdrop

Installing Necessary Dependencies

Navigate to the project directory and install the required dependencies using npm:

bash

1npm install
This command will read the package.json file and install all the packages listed under dependencies.

Basic Project Structure

The SnapDrop project is organized into several key directories and files:
  • public/: Contains static files like HTML, CSS, and client-side JavaScript.
  • src/: Contains the main source code for the server-side logic.
  • package.json: Lists the project's dependencies and scripts.
  • server.js: The entry point for the Node.js server.
Understanding the structure will help you navigate the project and make modifications more efficiently.

Overview of the App Architecture

SnapDrop's architecture can be divided into three main components:
  1. Client-Side (Frontend): Handles the user interface and interactions using HTML, CSS, and JavaScript.
  2. Server-Side (Backend): Manages the WebRTC signaling process and establishes connections between peers.
  3. WebRTC: Facilitates peer-to-peer communication directly between devices.

Client-Side (Frontend)

  • HTML and CSS: Define the structure and style of the web interface.
  • JavaScript: Handles user interactions and communicates with the WebRTC API.

Server-Side (Backend)

  • Node.js: Runs the server, handles signaling, and coordinates peer connections.
  • WebSocket: Manages real-time communication between the client and server for signaling purposes.

WebRTC

  • Peer Connections: Establishes direct connections between devices for file transfer.
  • Data Channels: Enables the transfer of files and data through established peer connections.

Step 1: Get Started with the Main File

In this section, we'll set up the main JavaScript file that initializes the SnapDrop application. This file will integrate the WebRTC API and lay the foundation for peer-to-peer file sharing.

Setting Up the Main File

First, let's create and configure the main JavaScript file, which will be responsible for handling the core logic of our SnapDrop app.

[a] Creating the Main JavaScript File

Create a new file named main.js in the public/ directory. This file will contain the primary logic for the SnapDrop application.

[b] Integrating WebRTC API

To establish peer-to-peer connections, we'll use the WebRTC API. The following code snippet demonstrates how to set up a simple WebRTC connection:

JavaScript

1   // main.js
2
3   // Check for WebRTC support
4   if (!window.RTCPeerConnection) {
5       console.error('WebRTC is not supported by your browser.');
6   }
7
8   // Configuration for the peer connection
9   const configuration = {
10       iceServers: [
11           {
12               urls: 'stun:stun.l.google.com:19302' // Google's public STUN server
13           }
14       ]
15   };
16
17   let peerConnection = new RTCPeerConnection(configuration);
18
19   // Handle ICE candidate events
20   peerConnection.onicecandidate = (event) => {
21       if (event.candidate) {
22           console.log('New ICE candidate: ', event.candidate);
23           // Send the candidate to the remote peer
24       }
25   };
26
27   // Handle the connection state change
28   peerConnection.onconnectionstatechange = (event) => {
29       switch (peerConnection.connectionState) {
30           case 'connected':
31               console.log('Peers connected!');
32               break;
33           case 'disconnected':
34           case 'failed':
35               console.error('Peer connection failed.');
36               break;
37           case 'closed':
38               console.log('Peer connection closed.');
39               break;
40       }
41   };
This code sets up a basic WebRTC peer connection with ICE candidates handling and connection state changes.

[c] Initializing the App

Now, let's initialize the SnapDrop application. This involves setting up event listeners and handling user interactions:

JavaScript

1   // Initialize SnapDrop
2   document.addEventListener('DOMContentLoaded', () => {
3       // Handle file input changes
4       document.getElementById('fileInput').addEventListener('change', handleFileSelect);
5
6       // Handle the send button click
7       document.getElementById('sendButton').addEventListener('click', sendFile);
8
9       // Function to handle file selection
10       function handleFileSelect(event) {
11           const file = event.target.files[0];
12           if (file) {
13               console.log('Selected file: ', file.name);
14               // Prepare the file for sending
15           }
16       }
17
18       // Function to send the selected file
19       function sendFile() {
20           const file = document.getElementById('fileInput').files[0];
21           if (file) {
22               console.log('Sending file: ', file.name);
23               // Send the file using WebRTC Data Channels
24           }
25       }
26   });
In this code, we:
  • Wait for the DOM to load before initializing the app.
  • Set up event listeners for file selection and sending.
  • Define functions to handle file selection and sending.
By completing this step, you have set up the foundational code required for SnapDrop to initialize and handle basic user interactions. In the next sections, we will build upon this foundation to add more functionality and refine the user interface.

Step 2: Wireframe All the Components

In this step, we will design the user interface (UI) for the SnapDrop application. We'll create the HTML structure, apply basic CSS for styling, and ensure that the interface is user-friendly and intuitive.

Designing the User Interface

The SnapDrop app needs a clean and simple interface that allows users to select files and connect with other devices easily. We'll create a basic wireframe that includes a file input, a button to send files, and an area to display connected participants.

[a] Planning the Wireframe for the App

Before writing any code, it's helpful to plan the layout of the interface. The main components will include:
  • A header with the app name.
  • A file input for selecting files.
  • A button to initiate the file transfer.
  • A section to display connected devices.

[b] Creating the HTML Structure

Here's the basic HTML structure for our SnapDrop app:

HTML

1   <!-- public/index.html -->
2   <!DOCTYPE html>
3   <html lang="en">
4   <head>
5       <meta charset="UTF-8">
6       <meta name="viewport" content="width=device-width, initial-scale=1.0">
7       <title>SnapDrop - Share Files Like AirDrop</title>
8       <link rel="stylesheet" href="styles.css">
9   </head>
10   <body>
11       <header>
12           <h1>SnapDrop</h1>
13           <p>Share files like AirDrop with WebRTC</p>
14       </header>
15       <main>
16           <section class="file-input-section">
17               <input type="file" id="fileInput" accept="*/*">
18               <button id="sendButton">Send File</button>
19           </section>
20           <section class="participants-section">
21               <h2>Connected Devices</h2>
22               <div id="participantsList"></div>
23           </section>
24       </main>
25       <script src="main.js"></script>
26   </body>
27   </html>
This HTML file includes:
  • A header with the app's title and description.
  • A main section with a file input and send button.
  • A section to display connected devices.

[c] Applying CSS for Basic Styling

Now, let's add some basic styling to make the interface visually appealing. Create a styles.css file in the public/ directory:

CSS

1   /* public/styles.css */
2
3   body {
4       font-family: Arial, sans-serif;
5       background-color: #f0f0f0;
6       margin: 0;
7       padding: 0;
8       display: flex;
9       flex-direction: column;
10       align-items: center;
11   }
12
13   header {
14       background-color: #4CAF50;
15       color: white;
16       padding: 1rem;
17       text-align: center;
18       width: 100%;
19   }
20
21   main {
22       display: flex;
23       flex-direction: column;
24       align-items: center;
25       padding: 2rem;
26       width: 100%;
27   }
28
29   .file-input-section {
30       margin-bottom: 2rem;
31   }
32
33   #fileInput {
34       margin-right: 1rem;
35   }
36
37   button {
38       background-color: #4CAF50;
39       color: white;
40       border: none;
41       padding: 0.5rem 1rem;
42       cursor: pointer;
43   }
44
45   button:hover {
46       background-color: #45a049;
47   }
48
49   .participants-section {
50       background-color: white;
51       padding: 1rem;
52       border-radius: 5px;
53       box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
54       width: 100%;
55       max-width: 600px;
56   }
57
58   #participantsList {
59       display: flex;
60       flex-direction: column;
61       align-items: center;
62   }
This CSS file includes styles for:
  • The body, to set a uniform background and center the content.
  • The header, to give it a distinct background color and center the text.
  • The main content area, to align items in a column.
  • The file input section and participants section, to provide spacing and basic styling.
By completing this step, you have created a basic but functional user interface for the SnapDrop app. This interface will allow users to select files, send them, and view connected devices. In the next sections, we'll implement the functionality to connect devices and transfer files using WebRTC.

Step 3: Implement Join Screen

In this step, we'll create the join screen for SnapDrop, which allows users to connect with other devices on the same local network. This involves developing the user interface for the join screen, handling user inputs, and connecting to the WebRTC network.

Creating the Join Screen

The join screen is a crucial part of the SnapDrop application, as it facilitates the initial connection between devices. We'll build a simple interface for users to join and manage their connections.

[a] Developing the User Interface for the Join Screen

We'll add elements to the existing HTML structure to support the join screen functionality. Update the index.html file to include a section for the join screen:

HTML

1   <!-- public/index.html -->
2   <!DOCTYPE html>
3   <html lang="en">
4   <head>
5       <meta charset="UTF-8">
6       <meta name="viewport" content="width=device-width, initial-scale=1.0">
7       <title>SnapDrop - Share Files Like AirDrop</title>
8       <link rel="stylesheet" href="styles.css">
9   </head>
10   <body>
11       <header>
12           <h1>SnapDrop</h1>
13           <p>Share files like AirDrop with WebRTC</p>
14       </header>
15       <main>
16           <section class="join-screen">
17               <input type="text" id="usernameInput" placeholder="Enter your username">
18               <button id="joinButton">Join</button>
19           </section>
20           <section class="file-input-section">
21               <input type="file" id="fileInput" accept="*/*">
22               <button id="sendButton">Send File</button>
23           </section>
24           <section class="participants-section">
25               <h2>Connected Devices</h2>
26               <div id="participantsList"></div>
27           </section>
28       </main>
29       <script src="main.js"></script>
30   </body>
31   </html>
This updated HTML includes:
  • A new section with an input field for the username and a join button.

[b] Handling User Inputs

Next, we'll add JavaScript to handle user inputs and join the WebRTC network. Update the main.js file to include the necessary event listeners and functions:

JavaScript

1   // main.js
2
3   document.addEventListener('DOMContentLoaded', () => {
4       // Existing event listeners
5       document.getElementById('fileInput').addEventListener('change', handleFileSelect);
6       document.getElementById('sendButton').addEventListener('click', sendFile);
7
8       // New event listener for join button
9       document.getElementById('joinButton').addEventListener('click', joinNetwork);
10
11       // Function to handle file selection
12       function handleFileSelect(event) {
13           const file = event.target.files[0];
14           if (file) {
15               console.log('Selected file: ', file.name);
16               // Prepare the file for sending
17           }
18       }
19
20       // Function to send the selected file
21       function sendFile() {
22           const file = document.getElementById('fileInput').files[0];
23           if (file) {
24               console.log('Sending file: ', file.name);
25               // Send the file using WebRTC Data Channels
26           }
27       }
28
29       // Function to join the WebRTC network
30       function joinNetwork() {
31           const username = document.getElementById('usernameInput').value;
32           if (username) {
33               console.log('Joining network as: ', username);
34               // Initialize WebRTC connection
35               initializeConnection(username);
36           } else {
37               alert('Please enter a username.');
38           }
39       }
40
41       // Function to initialize WebRTC connection
42       function initializeConnection(username) {
43           // Configuration for the peer connection
44           const configuration = {
45               iceServers: [
46                   {
47                       urls: 'stun:stun.l.google.com:19302' // Google's public STUN server
48                   }
49               ]
50           };
51
52           let peerConnection = new RTCPeerConnection(configuration);
53
54           // Handle ICE candidate events
55           peerConnection.onicecandidate = (event) => {
56               if (event.candidate) {
57                   console.log('New ICE candidate: ', event.candidate);
58                   // Send the candidate to the remote peer
59               }
60           };
61
62           // Handle the connection state change
63           peerConnection.onconnectionstatechange = (event) => {
64               switch (peerConnection.connectionState) {
65                   case 'connected':
66                       console.log('Peers connected!');
67                       break;
68                   case 'disconnected':
69                   case 'failed':
70                       console.error('Peer connection failed.');
71                       break;
72                   case 'closed':
73                       console.log('Peer connection closed.');
74                       break;
75               }
76           };
77
78           // Further WebRTC initialization steps (e.g., setting up data channels) will go here
79       }
80   });
This JavaScript code:
  • Adds an event listener for the join button to capture the username input.
  • Defines the joinNetwork function to handle joining the WebRTC network.
  • Includes the initializeConnection function to set up the WebRTC peer connection.

[c] Connecting to the WebRTC Network

The final step is to complete the WebRTC connection setup, allowing devices to discover and connect to each other. This involves setting up data channels and handling signaling between peers.
Update the initializeConnection function to include data channel setup:

JavaScript

1   // Function to initialize WebRTC connection
2   function initializeConnection(username) {
3       // Configuration for the peer connection
4       const configuration = {
5           iceServers: [
6               {
7                   urls: 'stun:stun.l.google.com:19302' // Google's public STUN server
8               }
9           ]
10       };
11
12       let peerConnection = new RTCPeerConnection(configuration);
13
14       // Handle ICE candidate events
15       peerConnection.onicecandidate = (event) => {
16           if (event.candidate) {
17               console.log('New ICE candidate: ', event.candidate);
18               // Send the candidate to the remote peer
19           }
20       };
21
22       // Handle the connection state change
23       peerConnection.onconnectionstatechange = (event) => {
24           switch (peerConnection.connectionState) {
25               case 'connected':
26                   console.log('Peers connected!');
27                   break;
28               case 'disconnected':
29               case 'failed':
30                   console.error('Peer connection failed.');
31                   break;
32               case 'closed':
33                   console.log('Peer connection closed.');
34                   break;
35           }
36       };
37
38       // Set up data channel
39       const dataChannel = peerConnection.createDataChannel('fileTransfer');
40
41       dataChannel.onopen = () => {
42           console.log('Data channel is open');
43       };
44
45       dataChannel.onclose = () => {
46           console.log('Data channel is closed');
47       };
48
49       // Handle incoming messages
50       dataChannel.onmessage = (event) => {
51           console.log('Received message: ', event.data);
52           // Handle the received file or data
53       };
54
55       // Further WebRTC initialization steps (e.g., creating offers/answers, signaling) will go here
56   }
By following these steps, you have successfully implemented the join screen for the SnapDrop application. Users can now enter their username and join the WebRTC network, setting the stage for peer-to-peer file sharing. In the next sections, we will build on this foundation to implement file transfer and participant management functionalities.

Step 4: Implement Controls

In this step, we will build the control features for the SnapDrop application. These controls include selecting files, sending them to connected devices, and managing user interactions. We'll add the necessary HTML elements and JavaScript functions to handle these actions.

Building Control Features

[a] Implementing File Selection and Sharing Controls

We'll enhance the file input and send button functionalities to handle file selection and initiate file transfer using WebRTC data channels.

[b] Adding Buttons for Sharing and Receiving Files

Update the index.html file to include the necessary buttons and controls:

HTML

1   <!-- public/index.html -->
2   <!DOCTYPE html>
3   <html lang="en">
4   <head>
5       <meta charset="UTF-8">
6       <meta name="viewport" content="width=device-width, initial-scale=1.0">
7       <title>SnapDrop - Share Files Like AirDrop</title>
8       <link rel="stylesheet" href="styles.css">
9   </head>
10   <body>
11       <header>
12           <h1>SnapDrop</h1>
13           <p>Share files like AirDrop with WebRTC</p>
14       </header>
15       <main>
16           <section class="join-screen">
17               <input type="text" id="usernameInput" placeholder="Enter your username">
18               <button id="joinButton">Join</button>
19           </section>
20           <section class="file-input-section">
21               <input type="file" id="fileInput" accept="*/*">
22               <button id="sendButton">Send File</button>
23           </section>
24           <section class="participants-section">
25               <h2>Connected Devices</h2>
26               <div id="participantsList"></div>
27           </section>
28       </main>
29       <script src="main.js"></script>
30   </body>
31   </html>
This HTML structure includes:
  • A file input for selecting files.
  • A send button to initiate the file transfer.

[c] Handling User Interactions with JavaScript

Update the main.js file to handle file selection, manage the data channel for file transfer, and interact with the UI elements.

JavaScript

1   // main.js
2
3   document.addEventListener('DOMContentLoaded', () => {
4       // Existing event listeners
5       document.getElementById('fileInput').addEventListener('change', handleFileSelect);
6       document.getElementById('sendButton').addEventListener('click', sendFile);
7       document.getElementById('joinButton').addEventListener('click', joinNetwork);
8
9       let selectedFile;
10       let dataChannel;
11
12       // Function to handle file selection
13       function handleFileSelect(event) {
14           selectedFile = event.target.files[0];
15           if (selectedFile) {
16               console.log('Selected file: ', selectedFile.name);
17               // Prepare the file for sending
18           }
19       }
20
21       // Function to send the selected file
22       function sendFile() {
23           if (selectedFile && dataChannel) {
24               const reader = new FileReader();
25               reader.onload = (event) => {
26                   const arrayBuffer = event.target.result;
27                   dataChannel.send(arrayBuffer);
28                   console.log('Sending file: ', selectedFile.name);
29               };
30               reader.readAsArrayBuffer(selectedFile);
31           } else {
32               console.log('No file selected or data channel is not open.');
33           }
34       }
35
36       // Function to join the WebRTC network
37       function joinNetwork() {
38           const username = document.getElementById('usernameInput').value;
39           if (username) {
40               console.log('Joining network as: ', username);
41               // Initialize WebRTC connection
42               initializeConnection(username);
43           } else {
44               alert('Please enter a username.');
45           }
46       }
47
48       // Function to initialize WebRTC connection
49       function initializeConnection(username) {
50           // Configuration for the peer connection
51           const configuration = {
52               iceServers: [
53                   {
54                       urls: 'stun:stun.l.google.com:19302' // Google's public STUN server
55                   }
56               ]
57           };
58
59           let peerConnection = new RTCPeerConnection(configuration);
60
61           // Handle ICE candidate events
62           peerConnection.onicecandidate = (event) => {
63               if (event.candidate) {
64                   console.log('New ICE candidate: ', event.candidate);
65                   // Send the candidate to the remote peer
66               }
67           };
68
69           // Handle the connection state change
70           peerConnection.onconnectionstatechange = (event) => {
71               switch (peerConnection.connectionState) {
72                   case 'connected':
73                       console.log('Peers connected!');
74                       break;
75                   case 'disconnected':
76                   case 'failed':
77                       console.error('Peer connection failed.');
78                       break;
79                   case 'closed':
80                       console.log('Peer connection closed.');
81                       break;
82               }
83           };
84
85           // Set up data channel
86           dataChannel = peerConnection.createDataChannel('fileTransfer');
87
88           dataChannel.onopen = () => {
89               console.log('Data channel is open');
90           };
91
92           dataChannel.onclose = () => {
93               console.log('Data channel is closed');
94           };
95
96           // Handle incoming messages
97           dataChannel.onmessage = (event) => {
98               console.log('Received message: ', event.data);
99               // Handle the received file or data
100           };
101
102           // Further WebRTC initialization steps (e.g., creating offers/answers, signaling) will go here
103       }
104   });
This JavaScript code:
  • Adds logic to handle file selection and read the file as an ArrayBuffer.
  • Sends the selected file through the WebRTC data channel when the send button is clicked.
  • Includes functions to join the WebRTC network and initialize the data channel for file transfer.
By implementing these controls, users can now select files, send them to connected devices, and manage interactions through the SnapDrop interface. In the next sections, we will further enhance the app by creating views for connected participants and handling real-time updates.

Get Free 10,000 Minutes Every Months

No credit card required to start

Step 5: Implement Participant View

In this step, we will create the participant view, which displays connected devices in real-time. This involves setting up the necessary HTML structure and adding JavaScript to manage participant connections and updates.

Creating Participant Views

The participant view will allow users to see which devices are connected and available for file sharing. We'll update the HTML structure and implement the required JavaScript logic to manage this feature.

[a] Displaying Connected Participants

Update the index.html file to include a section for displaying connected participants:

HTML

1   <!-- public/index.html -->
2   <!DOCTYPE html>
3   <html lang="en">
4   <head>
5       <meta charset="UTF-8">
6       <meta name="viewport" content="width=device-width, initial-scale=1.0">
7       <title>SnapDrop - Share Files Like AirDrop</title>
8       <link rel="stylesheet" href="styles.css">
9   </head>
10   <body>
11       <header>
12           <h1>SnapDrop</h1>
13           <p>Share files like AirDrop with WebRTC</p>
14       </header>
15       <main>
16           <section class="join-screen">
17               <input type="text" id="usernameInput" placeholder="Enter your username">
18               <button id="joinButton">Join</button>
19           </section>
20           <section class="file-input-section">
21               <input type="file" id="fileInput" accept="*/*">
22               <button id="sendButton">Send File</button>
23           </section>
24           <section class="participants-section">
25               <h2>Connected Devices</h2>
26               <div id="participantsList"></div>
27           </section>
28       </main>
29       <script src="main.js"></script>
30   </body>
31   </html>
This HTML structure includes:
  • A div element with the id participantsList to display connected devices.

[b] Managing Participant Connections

Update the main.js file to include logic for managing participant connections and updating the UI in real-time.

JavaScript

1   // main.js
2
3   document.addEventListener('DOMContentLoaded', () => {
4       // Existing event listeners
5       document.getElementById('fileInput').addEventListener('change', handleFileSelect);
6       document.getElementById('sendButton').addEventListener('click', sendFile);
7       document.getElementById('joinButton').addEventListener('click', joinNetwork);
8
9       let selectedFile;
10       let dataChannel;
11       let peerConnection;
12       const participantsList = document.getElementById('participantsList');
13
14       // Function to handle file selection
15       function handleFileSelect(event) {
16           selectedFile = event.target.files[0];
17           if (selectedFile) {
18               console.log('Selected file: ', selectedFile.name);
19               // Prepare the file for sending
20           }
21       }
22
23       // Function to send the selected file
24       function sendFile() {
25           if (selectedFile && dataChannel) {
26               const reader = new FileReader();
27               reader.onload = (event) => {
28                   const arrayBuffer = event.target.result;
29                   dataChannel.send(arrayBuffer);
30                   console.log('Sending file: ', selectedFile.name);
31               };
32               reader.readAsArrayBuffer(selectedFile);
33           } else {
34               console.log('No file selected or data channel is not open.');
35           }
36       }
37
38       // Function to join the WebRTC network
39       function joinNetwork() {
40           const username = document.getElementById('usernameInput').value;
41           if (username) {
42               console.log('Joining network as: ', username);
43               // Initialize WebRTC connection
44               initializeConnection(username);
45           } else {
46               alert('Please enter a username.');
47           }
48       }
49
50       // Function to initialize WebRTC connection
51       function initializeConnection(username) {
52           // Configuration for the peer connection
53           const configuration = {
54               iceServers: [
55                   {
56                       urls: 'stun:stun.l.google.com:19302' // Google's public STUN server
57                   }
58               ]
59           };
60
61           peerConnection = new RTCPeerConnection(configuration);
62
63           // Handle ICE candidate events
64           peerConnection.onicecandidate = (event) => {
65               if (event.candidate) {
66                   console.log('New ICE candidate: ', event.candidate);
67                   // Send the candidate to the remote peer
68               }
69           };
70
71           // Handle the connection state change
72           peerConnection.onconnectionstatechange = (event) => {
73               switch (peerConnection.connectionState) {
74                   case 'connected':
75                       console.log('Peers connected!');
76                       break;
77                   case 'disconnected':
78                   case 'failed':
79                       console.error('Peer connection failed.');
80                       break;
81                   case 'closed':
82                       console.log('Peer connection closed.');
83                       break;
84               }
85           };
86
87           // Set up data channel
88           dataChannel = peerConnection.createDataChannel('fileTransfer');
89
90           dataChannel.onopen = () => {
91               console.log('Data channel is open');
92           };
93
94           dataChannel.onclose = () => {
95               console.log('Data channel is closed');
96           };
97
98           // Handle incoming messages
99           dataChannel.onmessage = (event) => {
100               console.log('Received message: ', event.data);
101               // Handle the received file or data
102           };
103
104           // Simulate participant connection for demonstration purposes
105           addParticipant(username);
106       }
107
108       // Function to add a participant to the list
109       function addParticipant(username) {
110           const participantItem = document.createElement('div');
111           participantItem.classList.add('participant');
112           participantItem.innerText = username;
113           participantsList.appendChild(participantItem);
114           console.log('Participant added: ', username);
115       }
116   });
This JavaScript code:
  • Manages the list of connected participants by adding them to the participantsList element.
  • Simulates adding a participant for demonstration purposes.

[c] Updating the UI in Real-Time

To update the UI in real-time as participants join or leave, you would typically integrate with a signaling server. For simplicity, this example simulates participant management.
Here's how you can modify the initializeConnection function to dynamically update the participant list:

JavaScript

1   // Function to initialize WebRTC connection
2   function initializeConnection(username) {
3       // Configuration for the peer connection
4       const configuration = {
5           iceServers: [
6               {
7                   urls: 'stun:stun.l.google.com:19302' // Google's public STUN server
8               }
9           ]
10       };
11
12       peerConnection = new RTCPeerConnection(configuration);
13
14       // Handle ICE candidate events
15       peerConnection.onicecandidate = (event) => {
16           if (event.candidate) {
17               console.log('New ICE candidate: ', event.candidate);
18               // Send the candidate to the remote peer
19           }
20       };
21
22       // Handle the connection state change
23       peerConnection.onconnectionstatechange = (event) => {
24           switch (peerConnection.connectionState) {
25               case 'connected':
26                   console.log('Peers connected!');
27                   addParticipant(username);
28                   break;
29               case 'disconnected':
30               case 'failed':
31                   console.error('Peer connection failed.');
32                   removeParticipant(username);
33                   break;
34               case 'closed':
35                   console.log('Peer connection closed.');
36                   removeParticipant(username);
37                   break;
38           }
39       };
40
41       // Set up data channel
42       dataChannel = peerConnection.createDataChannel('fileTransfer');
43
44       dataChannel.onopen = () => {
45           console.log('Data channel is open');
46       };
47
48       dataChannel.onclose = () => {
49           console.log('Data channel is closed');
50       };
51
52       // Handle incoming messages
53       dataChannel.onmessage = (event) => {
54           console.log('Received message: ', event.data);
55           // Handle the received file or data
56       };
57
58       // Simulate participant connection for demonstration purposes
59       addParticipant(username);
60   }
61
62   // Function to remove a participant from the list
63   function removeParticipant(username) {
64       const participants = document.querySelectorAll('.participant');
65       participants.forEach(participant => {
66           if (participant.innerText === username) {
67               participantsList.removeChild(participant);
68               console.log('Participant removed: ', username);
69           }
70       });
71   }
This code dynamically adds or removes participants from the list based on the connection state changes.
By completing this step, you have successfully implemented the participant view for the SnapDrop application. Users can now see which devices are connected and ready for file sharing. In the next step, we will finalize the application by ensuring it runs smoothly and handling any remaining issues.

Step 6: Run Your Code Now

In this final step, we will ensure that the SnapDrop application runs smoothly by performing final setup steps, testing the application locally, debugging common issues, and providing tips for deployment as a Progressive Web App (PWA).

Final Setup Steps

Before running the application, make sure all dependencies are installed and the project is correctly set up. If you haven't already done so, install the required dependencies:

bash

1npm install
Ensure that your project structure looks like this:
1snapdrop/
23├── public/
4│   ├── index.html
5│   ├── main.js
6│   └── styles.css
7├── node_modules/
8├── package.json
9└── server.js

Running the Application Locally

To run the application locally, you need to start the server. Open your terminal, navigate to the project directory, and execute the following command:

bash

1node server.js
If you don't have a server.js file, you can create a simple Express server to serve your static files. Here’s an example server.js file:

JavaScript

1// server.js
2
3const express = require('express');
4const path = require('path');
5const app = express();
6
7const PORT = process.env.PORT || 3000;
8
9app.use(express.static(path.join(__dirname, 'public')));
10
11app.get('/', (req, res) => {
12    res.sendFile(path.join(__dirname, 'public', 'index.html'));
13});
14
15app.listen(PORT, () => {
16    console.log(`Server is running on port ${PORT}`);
17});
After creating the server.js file, run the server again:
1node server.js
Open your web browser and navigate to http://localhost:3000 to see the SnapDrop application in action.

Debugging Common Issues

Here are some common issues you might encounter and tips for debugging them:

WebRTC Connection Issues

  • Check for Browser Compatibility: Ensure that your browser supports WebRTC. Most modern browsers do, but you can check compatibility

    here

    .
  • ICE Candidate Gathering: Ensure that ICE candidates are being gathered and sent correctly. Check the browser console for any errors related to ICE candidates.

Data Channel Issues

  • Data Channel Not Opening: Ensure that the data channel is set up correctly and that both peers are accepting the data channel. Check for onopen and onclose events in the console.
  • Data Transfer Problems: Verify that the file is being read correctly and that the ArrayBuffer is sent through the data channel. Use console.log to check the file content before sending.

UI Issues

  • Participants Not Displayed: Ensure that participants are being added to the DOM correctly. Check the addParticipant function and make sure it updates the participantsList element.

Deployment Tips for a PWA

To deploy your SnapDrop application as a Progressive Web App (PWA), follow these additional steps:

[a] Add a Web App Manifest

Create a manifest.json file in the public directory:

json

1   {
2       "name": "SnapDrop",
3       "short_name": "SnapDrop",
4       "start_url": "/",
5       "display": "standalone",
6       "background_color": "#ffffff",
7       "theme_color": "#4CAF50",
8       "icons": [
9           {
10               "src": "icons/icon-192x192.png",
11               "sizes": "192x192",
12               "type": "image/png"
13           },
14           {
15               "src": "icons/icon-512x512.png",
16               "sizes": "512x512",
17               "type": "image/png"
18           }
19       ]
20   }
Link the manifest file in the index.html:

HTML

1   <link rel="manifest" href="manifest.json">

[b] Register a Service Worker

Create a service-worker.js file in the public directory:

JavaScript

1   // public/service-worker.js
2
3   self.addEventListener('install', event => {
4       event.waitUntil(
5           caches.open('snapdrop-v1').then(cache => {
6               return cache.addAll([
7                   '/',
8                   '/index.html',
9                   '/styles.css',
10                   '/main.js',
11                   '/manifest.json',
12                   '/icons/icon-192x192.png',
13                   '/icons/icon-512x512.png'
14               ]);
15           })
16       );
17   });
18
19   self.addEventListener('fetch', event => {
20       event.respondWith(
21           caches.match(event.request).then(response => {
22               return response || fetch(event.request);
23           })
24       );
25   });
Register the service worker in the main.js:

JavaScript

1   // main.js
2
3   if ('serviceWorker' in navigator) {
4       window.addEventListener('load', () => {
5           navigator.serviceWorker.register('/service-worker.js').then(registration => {
6               console.log('ServiceWorker registration successful with scope: ', registration.scope);
7           }, err => {
8               console.log('ServiceWorker registration failed: ', err);
9           });
10       });
11   }

[c] Deploy to a Hosting Service

Choose a hosting service like Netlify, Vercel, or GitHub Pages to deploy your PWA. Follow the hosting service's documentation for deploying static sites.
By completing these steps, you have successfully set up and run the SnapDrop application. You can now share files across devices seamlessly. Additionally, deploying the app as a PWA allows users to install it on their devices for a native-like experience.

Conclusion

This comprehensive guide has covered everything from setting up a SnapDrop-like application using WebRTC to deploying it as a PWA. With the knowledge gained, you can create a robust, cross-platform file-sharing tool that leverages modern web technologies to provide a seamless user experience. Whether for personal use or as part of a larger project, SnapDrop's implementation demonstrates the power and versatility of WebRTC in building real-time applications.

Want to level-up your learning? Subscribe now

Subscribe to our newsletter for more tech based insights

FAQ