In this tutorial, we’re going to build a WebRTC live stream broadcasting application for one-to-many video communication. In other words, we’ll build Periscope or Meerkat using WebRTC, enabling one-way video streaming from a single broadcaster to any number of viewers, all in the browser!
We’ll cover how to use the PubNub WebRTC SDK with a wrapper class to easily create live embeddable streaming content, and implement PubNub Presence to get a realtime count of stream viewers.
Check out the live broadcast video streaming demo here, open up a couple windows, and watch it in action!
And if you want to check out the project code for the tutorial series, it’s available here.
If you haven’t yet checked out the previous posts, we built a WebRTC video chat app with a number of different features:
That’s right! Let’s get to it.

A Note on Testing and Debugging
If you try to open file://<your-webrtc-project>
in your browser, you will likely run into Cross-Origin Resource Sharing (CORS) errors since the browser will block your requests to use video and microphone features. To test your code you have a few options. You can upload your files to a web server, like Github Pages if you prefer. However, to keep development local, I recommend you setup a simple server using Python.
To do this, open your terminal and change directories into your current project and depending on your version of Python, run one of the following modules.
cd <project-dir> # Python 2 python -m SimpleHTTPServer <portNo> # Python 3 python -m http.server <portNo>
For example, I run Python2.7 and the command I use is python -m SimpleHTTPServer 8001
. Now I can go to http://localhost:8001/index.html
to debug my app! Try making an index.html
with anything in it and serve it on localhost before you continue.
Step 1: The HTML5 Backbone
I am going to try to keep the HTML simple for the sake of the demo. Create a file called stream.html
, and in it put the following code.
<div id="vid-box"><!-- Stream goes here --></div> <form name="streamForm" id="stream" action="#" onsubmit="return stream(this);"> <input type="text" name="streamname" id="streamname" placeholder="Pick a stream name!" /> <input type="submit" name="stream_submit" value="Stream"> <div id="stream-info">Watching: <span id="here-now">0</span></div> </form> <form name="watchForm" id="watch" action="#" onsubmit="return watch(this);"> <input type="text" name="number" placeholder="Enter stream to join!" /> <input type="submit" value="Watch"/> </form> <div id="inStream"> <button id="end" onclick="end()">Done</button> <br> Generate Embed: <button onclick="genEmbed(400,600)">Tall</button><button onclick="genEmbed(600,400)">Wide</button><button onclick="genEmbed(500,500)">Square</button><br> <div id="embed-code"></div> </div>
This will give you some inputs and options, as well as a video holder where you can place your local stream, to see what you are streaming to the world.
I added a icon of a person to indicate the number of people watching your stream.
You will notice there are a few functions that we will need to create in the JavaScript:
– stream(form)
: Begin streaming video on channel in form.
– watch(form)
: Join the steam channel provided by the form.
– end()
: Stop streaming.
– genEmbed(width,height)
: Generate an embeddable code for the stream.
Step 2: The JavaScript Imports
There are three libraries that you will need to include to make WebRTC operations much easier:
- Include jQuery to make modifying DOM elements a breeze.
- Include the PubNub JavaScript SDK to facilitate the WebRTC signaling.
- Include the PubNub WebRTC SDK, and SDK Wrapper libraries which makes placing phone calls as simple as calling the
dial(number)
function.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> <script src="https://cdn.pubnub.com/pubnub-3.7.14.min.js"></script> <script src="https://cdn.pubnub.com/webrtc/webrtc.js"></script> <script src="https://cdn.pubnub.com/webrtc/rtc-controller.js"></script>
Now we are ready to write our broadcasting functions for stream
and watch
!
Step 3: Broadcasting Your Stream
In order to start broadcasting your streams, you will need a publish and subscribe key. To get your pub/sub keys, you’ll first need to sign up for a PubNub account. Once you sign up, you can find your unique PubNub keys in the PubNub Developer Dashboard. The free Sandbox tier should give you all the bandwidth you need to build and test your WebRTC Application.
3.1 Prepare to Broadcast
First, let’s locate our video holder for our local stream, embed code holder, and here-now holder for presence updates. Also, let’s create a global variable that will hold our current stream name.
var video_out = document.getElementById("vid-box"); var embed_code = document.getElementById("embed-code"); var here_now = document.getElementById('here-now'); var streamName;
3.2 Broadcast & Configurations
Now, time to implement our stream
function that will setup the WebRTC connection for broadcasting.
function stream(form) { streamName = form.streamname.value || Math.floor(Math.random()*100)+''; // Random stream if not provided var phone = window.phone = PHONE({ number : streamName, // listen on username else random publish_key : 'your_pub_key', // Your Pub Key subscribe_key : 'your_sub_key', // Your Sub Key oneway : true, // One-Way streaming enabled broadcast : true // True since you are the broadcaster }); var ctrl = window.ctrl = CONTROLLER(phone); ctrl.ready(function(){ form.streamname.style.background="#55ff5b"; form.stream_submit.hidden="true"; ctrl.addLocalStream(video_out); ctrl.stream(); // Begin streaming video }); ctrl.streamPresence(function(m){ here_now.innerHTML=m.occupancy; }); return false; // So form does not submit }
You are now streaming your video on channel streamName
. If you remember instantiating the phone
object in the past, you will notice that we added some new configurations, oneway
and broadcast
. The oneway
configuration simply sets the WebRTC SDK up for the possibility one-way streaming.
Then, broadcast
is set to true to indicate that your phone
will be the one broadcasting, not receiving the stream. Indicating oneway
will allow a user to watch your stream without ever having to grant mic and video permissions.
The ctrl.ready
function takes a callback to execute when it is ready to stream. First, we simply change the input to green to indicate success, and hide the submit button. Then, we use ctrl.addLocalStream(video_out)
to place our local stream in the video_out div. Finally, a call to ctrl.stream
subscribes us to a stream channel to begin broadcasting.
The last thing we do in our stream
function is make a ctrl.streamPresence(function(m){...})
handler. This subscribes you to presence events on your stream, then hands all presence events to the callback function. I simply pull off the current occupancy and display it in our here_now
div.
To spark your creativity, see the documentation on PubNub Presence.
Step 4: Join a Stream
Now that we have a user streaming (our broadcaster), we need to enable other users to join and view that stream. The watch
function takes the value from the HTML input and joins that stream.
function watch(form){ var num = form.number.value; // Stream to join var phone = window.phone = PHONE({ number : "Viewer" + Math.floor(Math.random()*100), // Random name publish_key : 'your_pub_key', // Your Pub Key subscribe_key : 'your_sub_key', // Your Sub Key oneway : true // One way streaming enabled }); var ctrl = window.ctrl = CONTROLLER(phone, true); ctrl.ready(function(){ ctrl.isStreaming(num, function(isOn){ if (isOn) ctrl.joinStream(num); else alert("User is not streaming!"); }); }); ctrl.receive(function(session){ session.connected(function(session){ video_out.appendChild(session.video); }); }); ctrl.streamPresence(function(m){ here_now.innerHTML=m.occupancy; }); return false; // Prevent form from submitting }
The initial setup looks very similar, but you will notice we only specify oneway
this time. This user is only receiving the stream, not broadcasting as well.
When the controller is ready to connect, it uses ctrl.isStreaming(number, function(isStrm){})
to make sure that the user they want to watch is currently streaming. This function takes a number and a callback. It checks if the number is streaming and passes a boolean value isStrm
to the callback function. If the user is streaming, we use ctrl.joinStream
to join the stream. No need to grant access to mic and video because we are only watching the stream.
Then, we use ctrl.streamPresence
again to update the amount of users currently watching the stream via PubNub Presence.
And just like that, we’ve built a realtime live broadcast video app with WebRTC. The coolest part is yet to come though. Next we will set up an embeddable live streaming video.
Step 5: Embeddable Live Streams
Lastly, we want to make the live stream embeddable in any browser. Creating an embeddable live stream is as simple as making an iframe
whose contents automatically joins your live stream.
5.1 Generate the Embed Code
Before we get to coding the HTML file that will load your video in the iframe, let’s write the function that will create the embeddable code.
function genEmbed(w,h){ if (!streamName) return; // If global var not set, not streaming var url = "http://<your-webstie>/embed.html?stream=" + streamName; var embed = document.createElement('iframe'); embed.src = url; embed.width = w; embed.height = h; embed.setAttribute("frameborder", 0); embed_code.innerHTML = 'Embed Code: '; embed_code.appendChild(document.createTextNode(embed.outerHTML)); }
You can see that our code will open an iframe
that displays the contents of a file on your website called embed.html
, and passes the parameter stream=streamName
. For debugging, you can use http://localhost:[your-port]/embed.html
.
5.2 Create the Embed HTML & CSS
In the same directory as your stream.html
, create a file called embed.html
. The HTML portion of your embed file is extremely basic:
<div id="vid-box"></div> <div id="stream-info"><span id="here-now">0</span></div>
Just a box to place the stream in and a presence counter. I will leave most of the styling to your personal preference, but in order to make sure the video takes up as much of the iframe
as it can, I recommend some of the following stylings:
#vid-box{ width: 100%; height: 100%; text-align: center; } #vid-box video{ width: 100%; height: 100%; } #stream-info{ position: absolute; bottom: 3vh; right: 5vw; }
This will ensure that vid-box
and the video
element that will be placed inside of it take up as much of the screen as possible.
5.3 The Embeddable Content’s JavaScript
This embeddable HTML will need to include some of the JavaScript imports as well, that way you can embed it on any page. Below your HTML, put the following JavaScript imports:
<script src="https://cdn.pubnub.com/pubnub.min.js"></script> <script src="http://kevingleason.me/SimpleRTC/js/webrtc.js"></script> <script src="http://kevingleason.me/SimpleRTC/js/rtc-controller.js"></script>
All that is left to do now is create a final script tag that gets the stream name from the URL parameters, and then does the exact same thing as our watch
function from part three.
First, set some global variables and make sure that there is a parameter stream
in the URL query string.
(function(){ var urlargs = urlparams(); var video_out = document.getElementById("vid-box"); var stream_info = document.getElementById("stream-info"); var here_now = document.getElementById("here-now"); // Handle error if stream is not in urlargs. if (!('stream' in urlargs)) { handleNoStream(); return; } // Get URL params function urlparams() { var params = {}; if (location.href.indexOf('?') < 0) return params; PUBNUB.each( location.href.split('?')[1].split('&'), function(data) { var d = data.split('='); params[d[0]] = d[1]; } ); return params; } function handleNoStream(){ video_out.innerHTML="<h2>That stream no longer exists!</h2>"; stream_info.hidden=true; } ... }())
Notice that we wrap the entire function in an anonymous function call (function(){ ... }())
. This is to preserve the scope of these variables, so they won’t interfere with the JavaScript on their embedded page. (It is called anonymous because it has no name and it calls itself!)
The function urlparams
can be copied exactly. It simply chops everything off the URL after ?
and then creates a map
of type string->string
for the query string key-value pairs. We create this map in urlargs
and then check if it contains a key stream
. If it doesn’t, handleNoStream
is called.
This function is also called if there was no user streaming to the channel you tried joining, or when the streaming user ends broadcast. It is essentially an error handler, and this implementation hides the presence counter and displays some text.
Now, in the same anonymous function, right below handleNoStream
, we will place the following code, which looks almost identical to watch
.
var phone = window.phone = PHONE({ number : "EmbedViewer" + Math.floor(Math.random()*100), // random viewer name publish_key : 'your_pub_key', // Your Pub Key subscribe_key : 'your_sub_key', // Your Sub Key oneway : true, }); var ctrl = window.ctrl = CONTROLLER(phone); ctrl.ready(function(){ ctrl.isStreaming(stream, function(isOn){ if (isOn) ctrl.joinStream(stream); else handleNoStream(); }); }); ctrl.receive(function(session){ session.connected(function(session){ stream_info.hidden=false; video_out.appendChild(session.video); }); session.ended(function(session){ handleNoStream(); }); }); ctrl.streamPresence(function(m){ here_now.innerHTML = m.occupancy; }); ctrl.unable(function(){ handleNoStream(); });
The only real differences between this block of code and watch
from Part 3, is that we now catch ctrl.unable
which is called if the browser is not WebRTC compatible, and session.ended
which is called when the broadcaster stops streaming. Both callbacks invoke handleNoStream
and display the “No Stream” message.
5.4 Try It Out
Start broadcasting a stream and then visit the page /embed.html?stream=myStreamName
. If all goes well, you will see your stream load automatically! When you are ready to take your site live, remember to change the url from 4.1 genEmbed
to your website’s name.

What Now?
This guide has only provided simple suggestions for your video streaming app. Get creative! These are very open callbacks. You can customize the way your embed page looks by styling the HTML, if you want the video smaller or larger, it is just a plain video
HTML element and it can be modified with CSS. Make something cool!