Simple Peer Tutorial: Add TURN Server for Video, DataChannel

Ali AsgherAli Asgher
8 min read

This article was originally written on the Metered.ca Blog: Simple Peer Tutorial: Add TURN Server for Video, DataChannel

Simple Peer is a JavaScript Library for implementing peer-to-peer communication

This library is built on top of webrtc, at its basic implementation the Simple Peer provides a straightforward API for setting up P2P communication. It has the following features

  • Signalling

  • Connection management

  • Media Streams

  • Data Channels

When and why you need a TURN server

In Peer-to-Peer communications involving webrtc libraries such as Simple peer, it is important to establish a direct communication channel.

Due to various network configurations and restrictions such as NAT routers and firewalls, the direct connections is not possible

This where TURN servers come into play, The simple peer does not come with a TURN server, and you need to add it from a turn server provider

Issues with Different Internet Connections

  • Symmetric NAT: Some networks use symmetric NATs which makes it impossible to have peer to peer direct connectivity without a TURN server

  • Firewalls: Many firewalls block incoming connections from external sources

  • Carrier Grade NAT: Mobile networks and Internet service providers use Carrier Grade NAT which adds another layer of complexity and thus makes it difficult to establish connection without a TURN server

NAT traversal problems

  • IP address Obfuscation

  • Port mapping complications:

  • Blocked connections

Role of TURN servers

  • Data Relay

  • Bypassing restrictions

  • Enhanced Reliability

Metered TURN servers

  1. API: TURN server management with powerful API. You can do things like Add/ Remove credentials via the API, Retrieve Per User / Credentials and User metrics via the API, Enable/ Disable credentials via the API, Retrive Usage data by date via the API.

  2. Global Geo-Location targeting: Automatically directs traffic to the nearest servers, for lowest possible latency and highest quality performance. less than 50 ms latency anywhere around the world

  3. Servers in all the Regions of the world: Toronto, Miami, San Francisco, Amsterdam, London, Frankfurt, Bangalore, Singapore,Sydney, Seoul, Dallas, New York

  4. Low Latency: less than 50 ms latency, anywhere across the world.

  5. Cost-Effective: pay-as-you-go pricing with bandwidth and volume discounts available.

  6. Easy Administration: Get usage logs, emails when accounts reach threshold limits, billing records and email and phone support.

  7. Standards Compliant: Conforms to RFCs 5389, 5769, 5780, 5766, 6062, 6156, 5245, 5768, 6336, 6544, 5928 over UDP, TCP, TLS, and DTLS.

  8. Multi‑Tenancy: Create multiple credentials and separate the usage by customer, or different apps. Get Usage logs, billing records and threshold alerts.

  9. Enterprise Reliability: 99.999% Uptime with SLA.

  10. Enterprise Scale: With no limit on concurrent traffic or total traffic. Metered TURN Servers provide Enterprise Scalability

  11. 5 GB/mo Free: Get 5 GB every month free TURN server usage with the Free Plan

  12. Runs on port 80 and 443

  13. Support TURNS + SSL to allow connections through deep packet inspection firewalls.

  14. Supports both TCP and UDP

  15. Free Unlimited STUN

Configuring the Simple Peer with TURN server

When you are creating peer to peer connections across different internet connections, that are behind strict NAT and firewall rules. Incorporating a TURN server is necessary to smooth connectivity

Setting Up TURN server credentials

Step 1: go to Metered.ca/stun-turn

Go to Metered.ca/stun-turn and create a free account by clicking on the Get Started button to create a free account

Step 2: Create a TURN server credential

After you sign up for a free account you will land up in the dashboard

here click on the Click Here to Generate your First Credential to create your turn server credential

Step 3 Copy the credentials

After you create your first credential you will get your username and password.

Click on the instructions button to get your ice server array

Copy this ICE server array to paste in the Simple Peer in order to add turn server to your Simple Peer

Modifying the config Option in Simple Peer to add a TURN server

Simple peer lets you customize the ICE array that is the Interactive Connectivity Establishment array during the connection process

Here I have converted the default config option to add the Metered.ca TURN server

{
  initiator: false,
  channelConfig: {},
  channelName: '<random string>',
  config: { iceServers: [
      {
        urls: "stun:stun.relay.metered.ca:80",
      },
      {
        urls: "turn:global.relay.metered.ca:80",
        username: "87a3a559dd4cff69c4e5eae5",
        credential: "O2WMwfz+PFmqp4kV",
      },
      {
        urls: "turn:global.relay.metered.ca:80?transport=tcp",
        username: "87a3a559dd4cff69c4e5eae5",
        credential: "O2WMwfz+PFmqp4kV",
      },
      {
        urls: "turn:global.relay.metered.ca:443",
        username: "87a3a559dd4cff69c4e5eae5",
        credential: "O2WMwfz+PFmqp4kV",
      },
      {
        urls: "turns:global.relay.metered.ca:443?transport=tcp",
        username: "87a3a559dd4cff69c4e5eae5",
        credential: "O2WMwfz+PFmqp4kV",
      },
  ]},
  offerOptions: {},
  answerOptions: {},
  sdpTransform: function (sdp) { return sdp },
  stream: false,
  streams: [],
  trickle: true,
  allowHalfTrickle: false,
  wrtc: {}, // RTCPeerConnection/RTCSessionDescription/RTCIceCandidate
  objectMode: false
}

Code Example: Adding TURN server to Simple Peer

Below is a comprehensive example on how to integrate the TURN server with the Simple Peer

HTML (index.html)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Simple Peer with TURN Server</title>
    <style>
      #outgoing {
        width: 600px;
        word-wrap: break-word;
        white-space: normal;
      }
    </style>
  </head>
  <body>
    <form>
      <textarea id="incoming" placeholder="Paste remote signal data here"></textarea>
      <button type="submit">Submit</button>
    </form>
    <pre id="outgoing"></pre>
    <video id="remoteVideo" autoplay playsinline></video>
    <script src="https://unpkg.com/simple-peer/simplepeer.min.js"></script>
    <script>
      // TURN server configuration
      const peerConfig = {
        iceServers: [
      {
        urls: "stun:stun.relay.metered.ca:80",
      },
      {
        urls: "turn:global.relay.metered.ca:80",
        username: "87a3a559dd4cff69c4e5eae5",
        credential: "O2WMwfz+PFmqp4kV",
      },
      {
        urls: "turn:global.relay.metered.ca:80?transport=tcp",
        username: "87a3a559dd4cff69c4e5eae5",
        credential: "O2WMwfz+PFmqp4kV",
      },
      {
        urls: "turn:global.relay.metered.ca:443",
        username: "87a3a559dd4cff69c4e5eae5",
        credential: "O2WMwfz+PFmqp4kV",
      },
      {
        urls: "turns:global.relay.metered.ca:443?transport=tcp",
        username: "87a3a559dd4cff69c4e5eae5",
        credential: "O2WMwfz+PFmqp4kV",
      },
  ]
      };

      // Create a new peer
      const peer = new SimplePeer({
        initiator: location.hash === '#1', // Use #1 in URL to set as initiator
        trickle: false, // Disable trickle ICE
        config: peerConfig // Include the TURN server configuration
      });

      // Handle errors
      peer.on('error', err => console.error('Error:', err));

      // Generate signaling data
      peer.on('signal', data => {
        document.querySelector('#outgoing').textContent = JSON.stringify(data);
      });

      // Handle form submission to input remote signaling data
      document.querySelector('form').addEventListener('submit', ev => {
        ev.preventDefault();
        const incomingData = document.querySelector('#incoming').value;
        try {
          peer.signal(JSON.parse(incomingData));
        } catch (e) {
          console.error('Invalid signaling data:', e);
        }
      });

      // Once connected, send a message
      peer.on('connect', () => {
        console.log('Peer connected!');
        peer.send('Hello from peer!');
      });

      // Receive messages from remote peer
      peer.on('data', data => {
        console.log('Received message:', data.toString());
      });

      // Handle receiving a remote stream (e.g., video)
      peer.on('stream', stream => {
        const videoElement = document.getElementById('remoteVideo');
        if ('srcObject' in videoElement) {
          videoElement.srcObject = stream;
        } else {
          videoElement.src = window.URL.createObjectURL(stream); // Fallback for older browsers
        }
      });

      // Optional: Get local media stream (audio/video) and add to peer
      navigator.mediaDevices
        .getUserMedia({ video: true, audio: true })
        .then(stream => {
          peer.addStream(stream);
        })
        .catch(err => {
          console.error('Failed to get local stream:', err);
        });
    </script>
  </body>
</html>

Explanation of the code:

  1. Adding Simple Peer via CDN

  2. TURN server Configuration

    1. The PeerConfig Object includes the TURN server configuration that we created above on Metered.ca TURn server
  3. Creating a Peer Instance:

    1. The initiator flag is set based URL hash

    2. trickle is set to false in order to gather all ICE candidates before signalling, this might increase connection time but simplifies the signalling process

  4. Handling Signalling data

    1. The signal event outputs signlling data that needs to be sent to the remote peer

    2. users are manually copying and pasting the signalling data in the above example for simplicity

  5. Establishing connections

    1. the connect event indicates that the peer to peer connection is established.

    2. You can start sending data by using peer.send()

  6. Handling streams

    1. The code requests the user media devices that is camera and microphones

    2. the local steams is added to peer.addStream(stream)

    3. the stream event is then used to display the remote video stream

Verifying connections via TURN server

To make sure that the integrgration of TURN server is successful and working with Simple Peer correctly

It is essential to test the setup and confirm that the connections are established using the turn server

Using the Metered TURN server Testing Tool

The Metered.ca provides a handy online tool to test your TURN server configuration

you can access the link here: TURN Server Testing tool

Verifying TURN server connection in your Application

Even if you have a successful TURN server running, it is important that your application uses the TURN server to establish the connection properly

How to verify TURN server utilization

  1. Inspect ICE candidates

    1. You can remove the stun candidates from the ICE array in order to send all the data through the TURN server
  2. Edit the ICE array

    1. Edit the ICE array to delete the STUN server urls
[

//Here we have commented the STUN server urls so that the STUN //servers will not be used and all the data will be transmitted //through turn servers only

//      {
//        urls: "stun:stun.relay.metered.ca:80",
//      },
      {
        urls: "turn:global.relay.metered.ca:80",
        username: "87a3a559dd4cff69c4e5eae5",
        credential: "O2WMwfz+PFmqp4kV",
      },
      {
        urls: "turn:global.relay.metered.ca:80?transport=tcp",
        username: "87a3a559dd4cff69c4e5eae5",
        credential: "O2WMwfz+PFmqp4kV",
      },
      {
        urls: "turn:global.relay.metered.ca:443",
        username: "87a3a559dd4cff69c4e5eae5",
        credential: "O2WMwfz+PFmqp4kV",
      },
      {
        urls: "turns:global.relay.metered.ca:443?transport=tcp",
        username: "87a3a559dd4cff69c4e5eae5",
        credential: "O2WMwfz+PFmqp4kV",
      },
  ]

Now, if still your video / audio calling is working then you have successfully implemented TURN servers in your application

Common Issues and How to Resolve Them

1. Connection is established when on the same network but fails when switched to different internet connections

When two peers are on the same network establishing a peer to peer connection is easy because there is no Network Addressee Translation (NAT) traversal and firewall

When these peers are across the internet then there are NAT and firewall issues that block peer to peer direct connectivity

to solve this problem simply add a turn server to your ICE array in the simple peer configuration

we have already shown you how to do this above: Modifying the config Option in Simple Peer to add a TURN server

1
Subscribe to my newsletter

Read articles from Ali Asgher directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ali Asgher
Ali Asgher