WebRTC Live Video Stream Broadcasting from One-to-Many

7 min read PubNub Staff on Aug 27, 2015
Try PubNub Today

Free up to 1MM monthly messages. No credit card required.

Subscribe to our newsletter

By submitting this form, you are agreeing to our Terms and Conditions and Privacy Policy.

How to build a WebRTC live stream for video enabling a user to broadcast video from one-to-many using the WebRTC API. It's DIY Periscope and Meerkat!

Good News

We recommend checking out our updated WebRTC video chat tutorial.

Read tutorial here →

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 real-time 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.

Vonage Integration
Simple, powerful voice calls and video chat
Easily build applications that let users connect, invite one another to calls, and stream audio and video to one or more parties with Vonage Voice and Vonage Video integrations.
Learn more

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 name="watchForm" id="watch" action="#" onsubmit="return watch(this);">
	<input type="text" name="number" placeholder="Enter stream to join!" />
	<input type="submit" value="Watch"/>

<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>

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:

  1. Include jQuery to make modifying DOM elements a breeze.
  2. Include the PubNub JavaScript SDK to facilitate the WebRTC signaling.
  3. Include the PubNub WebRTC SDK, and SDK Wrapper libraries which makes placing phone calls as simple as calling the dial(number) function.

<script src=""></script>
<script src=""></script>
<script src=""></script>
<script src=""></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 = = 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.addLocalStream(video_out);; 	// 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 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 = = 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.isStreaming(num, function(isOn){
			if (isOn) ctrl.joinStream(num);
			else alert("User is not streaming!");
	    session.connected(function(session){ video_out.appendChild(; });
	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 real-time 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: ';

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:

	width: 100%;
	height: 100%;
	text-align: center;

#vid-box video{
	width: 100%;
	height: 100%;

	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=""></script>
<script src=""></script>
<script src=""></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.


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)) {

// Get URL params
function urlparams() {
    var params = {};
    if (location.href.indexOf('?') < 0) return params;
        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>";


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 = = 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.isStreaming(stream, function(isOn){
		if (isOn) ctrl.joinStream(stream);
		else handleNoStream();
    session.connected(function(session){ stream_info.hidden=false; video_out.appendChild(; });
    session.ended(function(session){ handleNoStream(); });
	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.

Vonage Integration
Simple, powerful voice calls and video chat
Easily build applications that let users connect, invite one another to calls, and stream audio and video to one or more parties with Vonage Voice and Vonage Video integrations.
Learn more

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!

More from PubNub

Top Trends to Engage Your App Users
InsightsSep 27, 20224 min read

Top Trends to Engage Your App Users

Take a look at the top trends that are the most effective in attracting customers and reducing churn, and how you can incorporate...

PubNub Staff

PubNub Staff

Comparing Game Engines: Unity vs Unreal vs the Rest
Real-Time Gaming BlogSep 21, 20225 min read

Comparing Game Engines: Unity vs Unreal vs the Rest

Comparing the major game engines: Unity vs Unreal Engine vs Corona SDK vs GameMaker Studio, including the benefits and cons of...

PubNub Staff

PubNub Staff

Python Socket Programming: Client, Server and Peer Libraries
BuildSep 21, 20226 min read

Python Socket Programming: Client, Server and Peer Libraries

Sockets (aka socket programming) enable programs to send and receive data, bi-directionally, at any given moment. This tutorial...

PubNub Staff

PubNub Staff