I’ll admit it: I’m a skimmer. I’ve never fully read that 400-page book you recommended to me, nor can I bring myself to sit through most Martin Scorsese films. You really have to captivate me to get me to buy into the whole piece of content. Otherwise, I’m more likely to give it a quick glance, tune in to the best parts, and move along.
So, naturally, the video heatmap overlay is one of my favorite web-based video player innovations. At a glance, you can see the most watched parts of a video before watching even a second of playback.
This is super handy for those times when you’re trying to show someone that one funny part of a video or find the point in the cooking video where they stop talking and start sharing the actual recipe. Honestly, I’ve probably reclaimed hours of my life with this little feature.
If you’re offering content that falls on the longer side (or simply want to offer some nice features for your attention-limited audience), you might consider implementing something similar in your application’s video playback. In this article, we’ll go over one way you can set up viewer playback tracking and generate a video heatmap to display in a React app using mux-player-react and a little bit of custom code.
Let’s think through what we need to achieve video tracking at a high level. The first step is to track the history of each second of the video so we know which have been watched and which haven't. We can store this history in application state as the video playback progresses.
Then, when the viewer finishes watching the video, we’ll send that data off to the server to be recorded in a database. Once it’s recorded, we can combine the records of several unique viewing sessions to tally up our final results.
In future viewing sessions, we’ll pull this tally back into the application for rendering in an area chart.
To accomplish all of this, let’s create a custom React hook that we can use with our video player. We’ll call this hook usePlaybackVector, and it will accept only one argument: the video DOM element that we’ll be tracking.
The first piece of information we’ll need is the length of the video. We can get this duration by writing a function that uses the video element’s seekable property to fetch the time range of the video runtime in seconds.
Once we have the duration, we’ll fill a vector tracking array with a zero for each second in the video, indicating that the individual second has not been viewed yet.
Now that we've determined the length of the video and initialized our playback vector with zeros, we’ll want to continuously update this vector while the video is being watched.
Here, the updatePlaybackVector function ensures that we only update the playback vector when the video is playing. If a specific second is being viewed for the first time, we mark it as viewed by setting its corresponding index to 1. If the second was previously viewed, we increment its count.
To ensure this function runs at the right times, let’s attach it to the timeupdate event of the video player.
Once the viewer finishes watching or when certain events occur (e.g., closing the page or loading a different video into the player), we need to record the playback vector to persistent storage. This will capture the viewing pattern for the playback session.
To make sure this recording happens reliably, we can bind recordPlaybackVector to various events:
At the end of all this, our hook will return the current playbackVector, which represents the viewing pattern for the video. By combining this vector along with any other viewing session vectors you have stored for a particular video, you can create heatmaps, show popular segments, and analyze user engagement throughout an individual viewing session.
Here’s the final tracker code that we end up with:
Nice! Now we need a video to actually track. We can create a new component at App.jsx, import the hook, and pass it a reference to a video player within our application. Naturally, we’ll use Mux Player to play our Mux video back with ease:
The hard part is done. We have the viewing session data, so now all that’s left is to use a charting library to visualize it.
For this example, we’ll use Recharts since their AreaChart component is about as simple as it gets. You can add it to your project by typing npm i recharts into your CLI. Next, we’ll create a new Heatmap.jsx component that we can use to contain the code responsible for rendering the area chart. Here’s what that code looks like:
For simplicity, we’re only passing the playback vector that’s currently stored in memory for the current viewing session to the heatmap. In a production application, you’d pull the aggregate stats from your server in the expected format and then pass them along to your Heatmap component.
We’ll also add the Heatmap component to our root application so that it will display underneath the video player:
Give the video a play and watch as your heatmap chart populates with your viewer stats in real time.
This is a great and common use case for visualizing playback stats, but don’t limit yourself to this type of implementation. You now have the data to build other interesting features! Here are a few other million-dollar ideas:
Video percentage completion: Only enable certain actions in your application once a certain percentage of your video has been viewed.
Individual heatmap displays: Keep individual viewer session data and provide detailed playback insights into every video view.
Transcript content analysis: Combine your viewing data with your timed transcript to understand which parts of your video script align with increased viewership.
Leveraging your viewing data can give you greater insights into your content performance while also making the application experience better for your end users. If you like a challenge, see if you can come up with other ways to use this data (and, if you do, let us know.)