Published on November 4, 2021 (over 2 years ago)

Surface real-time view and unique viewer counts in your video player

Ashley Huynh
By Ashley Huynh13 min readEngineering

In a time of never-ending news feeds and a 24-hour meme cycle, online video is becoming increasingly more collaborative, co-operative, and community-oriented. In this guide, we’ll discuss the power of surfacing real-time views and unique viewer stats in your application using Mux Data’s Real-Time Engagement Counts API, and show how to create a simple React application to accomplish this.

Real-time view and unique viewer counts are the active heartbeat of video performance, and are vital to connecting viewers to video experiences, and ensuring those experiences are delightful and consistent. We recently launched a new API in Mux Data that gives developers access to that real time data, and it’s now available as a public beta to all Mux Data users!

These counts can be used to surface the stats for the audience of a video (e.g., showing live audience counts to enrich the experience for fans during a concert) or to provide internal visibility of metrics (e.g., a live streaming company pushing metrics to an instructor during a live stream so they can better understand audience engagement).

In regards to real-time stats, “views” are defined as the number of views that are currently happening, while “unique viewers” reflects the number of unique viewers across those active views. Each unique viewer is only counted once, even if they are watching from multiple screens at the same time.

LinkLet’s start building!

Let’s get started by building a simple React application to surface real-time view and unique viewer counts! Before getting started, you will need to have the following items set up:

  • Have yarn installed on your machine--for more information on installing yarn, you can view the Installation Guide
  • Sign up for a free Mux account here (or log in if you already have one)
  • Have an example video you’d like to use in your app
  • Have a hosted link so you can embed the video

Note: the use of real-time metrics will work with any Mux Data integration and does not require Mux Video or the full setup described for this application. If you already have a video set up with Mux Data in an application, you can skip to step #6 in the guide or check out the See how many people are watching guide.

Link1. Set up a React application

React is a JavaScript library created by Facebook that enables the creation of interactive UIs. To quickly set up a React application, you’ll utilize create-react-app. Run the following commands to install the package and initialize a React project in a new project directory, then go into the directory and start up the app in development mode (once started up, the app will be able to be viewed at http://localhost:3000):

$ yarn add create-react-app $ yarn create react-app desired-app-name $ cd desired-app-name $ yarn start

If you have any issues with these commands, visit the Getting Started guide for help with troubleshooting.

Link2. Get started with Mux Video

In order to add your video to your application and prepare it for playback, you’ll need to upload it into Mux as an asset. Once you have a hosted URL for a video file and a Mux account, you can get started with the Video API.

Follow steps 1–3 of the Stream Video Files guide, where you’ll cover retrieving an API access token from your Mux account, submitting a POST request to upload the video as an asset, and waiting for the video to download and process. As you follow along, please note the following:

  • When generating the API access token in step #1, check all permissions provided (Mux Video, Mux Data, and System) to ensure you have the right permissions to generate a signing key using the System API in a later portion of the tutorial:
  • Due to the nature of this project, it’s recommended to use cURL or a GUI such as Insomnia or Postman to run the API request for step #3.
  • Hold on to your PLAYBACK_ID, MUX_TOKEN_ID, and MUX_TOKEN_SECRET because you’ll need these later!

Link3. Create a VideoPlayer component

Once the video has been successfully uploaded as an asset and the status is marked as "ready," you’ll need to embed the video you just uploaded into the React application.

To confirm whether the video is ready, check that the asset appears with the "ready" status in your Mux Account Dashboard > /Video > Assets.

For easy embedding and configuration with Mux Video and Mux Data, you can utilize the mux-video package. Install it by running the following command:

$ yarn add @mux-elements/mux-video

Once installed, create a new file for your VideoPlayer component in src called VideoPlayer.js. Add the following into the file:

import React from "react"; import '@mux-elements/mux-video'; export default function VideoPlayer () { return ( <mux-video controls playback-id="${PLAYBACK_ID}" env-key="${ENV_KEY}" stream-type="vod" style={{ width: "100%", maxWidth: "500px" }} /> ); }

The PLAYBACK_ID will be pulled from your video asset from step 2. You can find your ENV_KEY in the Mux Environments dashboard for the environment you generated your access token with:

Please note: The ENV_KEY is different from your API token. The  ENV_KEY is a client-side key used for Mux Data monitoring.

To verify correct setup of Mux Data, watch your video a few times via your app, then check out the Metrics (via Mux Account Dashboard > /Data > Metrics) to see if those new views show up. For Mux Video users, you can also see your views show up in real-time on the Assets page for the video you’re using.

Link4. Add your VideoPlayer component to your App.js to surface it

Right now, when you navigate to http://localhost:3000/, you’ll see this view:

In order to display the newly created VideoPlayer component, you’ll need to adjust the src/App.js file in your React project to import the VideoPlayer component you created in the previous step, and surface it within the App:

import './App.css'; import VideoPlayer from './VideoPlayer.js'; function App() { return ( <div className="App"> <VideoPlayer /> </div> ); } export default App;

Your video should now surface when you navigate to http://localhost:3000:

Link5. Keep track of viewers and surface them in the UI

At this point, you have a simple React app with a video displaying alongside Mux Data to keep track of video metrics and performance.

Since the desired goal for the app is to surface real-time views and unique viewers in the video player, the next steps would be to add variables to surface the view and unique viewer counts, and also to adjust the current templating of how the video is surfaced in the VideoPlayer component to accommodate a text overlay which can display the desired metrics.

React's State Hook is a perfect candidate to keep track of real-time views and unique viewers and live update the state variables even when they’re surfaced in the UI. To use the State Hook, adjust the react import statement at the top of VideoPlayer.js to include useState:

import React, { useState } from "react";

Once imported, you can initialize state variables to keep track of view and unique viewer counts. Within the VideoPlayer component, right after the export default function VideoPlayer() { line, initialize two state variables, one for each metric. Since each variable is meant to hold an integer count, each variable can be initialized with an initial value of 0.

export default function VideoPlayer() { const [viewCount, setViewCount] = useState(0); const [viewerCount, setViewerCount] = useState(0); ...

For each state variable initialized in the example above, the first parameter will be the actual reference name used to retrieve the current variable value, while the second parameter will be the function used to set or update the value of the variable.

Now that the state variables have been initialized, the work to adjust the templating and add the overlay count can begin.

Adjust the return statement in VideoPlayer so that the video is wrapped in a div with a className called video-container. Then, create a div called video-overlay within the video-container. In video-overlay, two text values will be surfaced: one that shows the viewCount state variable, and another that shows the viewerCount state variable.

return ( <div className="video-container"> <div className="video-overlay"> {"Views: " + viewCount + ", "} {"Unique Viewers: " + viewerCount} </div> <mux-video controls playback-id=${PLAYBACK_ID} env-key=${ENV_KEY} stream-type="vod" style={{ width: "100%", maxWidth: "500px" }} /> </div> );

After adding in the div containers needed for the overlay effect, create a new file in src called VideoPlayer.css to style the outer video container and position the contents of the video-overlay div over it. The VideoPlayer.css file will look like this:

.video-container { display: inline-block; position: relative; } .video-overlay { position: absolute; left: 0px; top: 0px; margin: 10px; padding: 5px 5px; font-size: 20px; font-family: Helvetica; color: #FFF; background-color: rgba(50, 50, 50, 0.3); }

Lastly, import the CSS styling in for the VideoPlayer component by adding the following as an import statement at the top of the file:

import './VideoPlayer.css';

Once that’s been added in, you should see an overlay on your video player when visiting http://localhost:3000:

Link6. Set up your JSON Web Token

Once everything in the application is ready to surface information, you can work on retrieving information from the Real-time Engagement Counts API to pull the desired views and unique viewers. In order to use this API, you’ll need a JSON Web Token (JWT).

In order to generate the JWT, you’ll need to create a signing key for the Data product. You can do this either in the UI here or through a POST request utilizing your MUX_TOKEN_ID and MUX_TOKEN_SECRET:

curl -X POST -H "Content-Type: application/json" -u ${MUX_TOKEN_ID}:${MUX_TOKEN_SECRET} ''

Once the POST request has been run, this is the expected response:

{ "data": { "private_key": "(base64-encoded PEM file with private key)", "id": "(unique signing-key identifier)", "created_at": "(UNIX Epoch seconds)” } }

For generating your signing key in either the UI or via API request, you’ll receive a base64-encoded private_key and a signing key id. Copy and save both of these somewhere.

Once the keys have been generated, create a JSON Web Token. In order to do this, install the base64url package to decode your private key to successfully pass it in to your JWT, and install jsonwebtoken to be able to create and sign your JWT:

$ yarn add base64url $ yarn add jsonwebtoken

Aside from your private signing key and signing key ID, you’ll also need your playback ID from step #2. Using these items, you can now implement a script to be able to easily generate JWTs and log them to your terminal or console. Create a new file in src within your project called jwt.js, then add in the following code:

const jwt = require('jsonwebtoken'); const base64 = require('base64url'); const privateKey = base64.decode(${PRIVATE_SIGNING_KEY}); const keyId = ${SIGNING_KEY_ID}; const playbackId = ${PLAYBACK_ID}; const token = jwt.sign({ sub: playbackId, aud: “playback_id”, exp: + 600, // UNIX Epoch seconds when the token expires kid: keyId }, privateKey, { algorithm: 'RS256' } ); console.log(token);

To run the above script, run the following command in your project’s root directory:

$ node src/jwt.js

This will output a JWT that you’ll be able to use to make API calls. Keep this JWT somewhere to be able to reference it for the next step.

For a more in-depth tutorial of using the Real-time Engagement Counts API, if you’d rather use another identifier for your video such as asset_id or live_stream_id, or if you run into any issues with these instructions, please check out the See how many people are watching guide.

Link7. Test your JWT

With the token ready to go, let’s test out the API! Run the following request and plug in your JWT generated in the previous step:

curl '{JWT}'

Since there are currently no views, you can expect to get something back like:

{"data": [{"views": 0, "viewers": 0, "updated_at": "2021-09-28T18:21:19Z"}]}

In order to ensure you’re pulling in the right live data, test out live views by opening http://localhost:3000 in several browser windows and emulating live video plays, then running the above command to see if it reports these live views. You can also compare the viewer metrics from the Real-time Dashboard (via Mux Account Dashboard > /Data > Real Time) to verify if you’re getting the correct results and the right view and viewer counts.

Link8. Set up the useInterval hook to make a recurring API call and update the view and viewer counts

Once you verify that you have access to your real-time view and unique viewer counts via an API call, you can now plug that information into your application. To add in the API call to the VideoPlayer component, create an async function, getData(), which will make the API call and then set the view and unique viewer counts based on the data returned from the request:

async function getData() { const response = await fetch("{JWT}"); const data = await response.json(); setViewCount([0].views); setViewerCount([0].viewers); }

Note: the JWT is being embedded directly into the request URL for demo purposes. If you’re looking to use this for a non-demo or client-facing application, please consider making the JWT an environment variable for an additional layer of security.

Since real-time counts happen very quickly and are extremely dynamic, the useInterval hook will come in handy to call getData() at frequently specified intervals (please note: Realtime Data APIs are rate limited to one request per second). You can install the library needed to use this hook by running the following command:

$ yarn add usehooks-ts

Once installed, import the useInterval hook in VideoPlayer.js:

import { useInterval } from 'usehooks-ts';

The useInterval hook requires a function body with code it will execute as its first parameter, and a specified interval of time (in milliseconds) between function calls. A successfully executed useInterval hook that calls getData() every 15 seconds will look like this:

useInterval( () => { getData(); }, 15000 // interval is every 15 seconds (recommended to be 15-30 seconds depending on video length) )

Once the useInterval hook has been implemented, your app is ready to surface real-time view and unique viewer counts in the player!


Data is important for all video pursuits, big and small. However, before you try to use your data to tell a bigger story, you may want to look at whether your videos are gaining traction with your audience by focusing on the basics like real-time view and viewer counts. We hope that providing the ability to easily surface these metrics will improve both viewer and monitoring experiences alike!

If you were able to create something cool from this guide or it helped you, please reach out! We’d love to hear how customers are using these tools and what their experiences are with using Mux.

Written By

Ashley Huynh

Previously coded at Mailchimp. Loves going to art museums and reading all the plaques. Avid consumer of punk music, KBBQ, and tiny cat memes.

Leave your wallet where it is

No credit card required to get started.