Tracking Youtube video engagement with Usermaven
Track how users interact with embedded YouTube videos on your site, including plays, progress milestones, and completions. This guide provides a JavaScript solution using the YouTube Iframe Player API.
Goal
To send events to Usermaven when a user:
- Starts playing a YouTube video.
- Reaches specific progress milestones (e.g., 25%, 50%, 75% viewed).
- Watches the entire video (100%).
Prerequisites
- Usermaven JavaScript SDK: Ensure the Usermaven SDK is installed and initialized on your website.
- YouTube iframe API: You’ll need to include the YouTube iframe API script on your page.
Implementation steps
1. HTML setup
Add a <div> element to your HTML where the YouTube player will be embedded. Give it a unique ID (e.g.,
player).
<!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
<div id="player"></div>
<!-- 2. Include the YouTube Iframe API script -->
<!-- This should ideally be placed before your custom script or at the end of the body -->
<script src="https://www.youtube.com/iframe_api"></script>2. Javascript tracking code
Add the following JavaScript code to your page. It’s best placed in a <script> tag after the YouTube API
script include, or in your main JavaScript file.
// --- Configuration ---
let player;
let videoInterval;
let videoEventName = 'sample-video'; // Customize this for different videos
const videoId = 'M7lc1UVf-VE'; // REPLACE with your YouTube Video ID
const breakpoints = [0.25, 0.5, 0.75]; // Progress milestones to track (25%, 50%, 75%)
// --- State Variables ---
let passedBreakpoint = 0; // Tracks the last breakpoint successfully reported
let hasTrackedInitialPlay = false; // Ensures "clicked" event fires only once per play session
// 1. This function creates an <iframe> (and YouTube player)
// after the API code downloads.
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
// 'player' is the ID of your div
height: '360',
width: '640',
videoId: videoId,
playerVars: {
// Add any playerVars you need, e.g.:
// 'autoplay': 0, // 0 for no autoplay, 1 for autoplay
// 'controls': 1, // 0 for no controls, 1 for controls
// 'modestbranding': 1, // Hide YouTube logo
// 'rel': 0 // Do not show related videos at the end
},
events: {
onReady: onPlayerReady,
onStateChange: onPlayerStateChange,
},
});
}
// 2. The API will call this function when the video player is ready.
function onPlayerReady(event) {
// You can do initial setup here if needed, e.g., mute the player
// event.target.mute();
console.log('YouTube Player ready for video:', videoId);
}
// 3. The API calls this function when the player's state changes.
function onPlayerStateChange(event) {
if (event.data === YT.PlayerState.PLAYING) {
// Video is playing
if (!hasTrackedInitialPlay) {
// This is the first play event for this "session"
usermaven('track', `clicked-${videoEventName}`);
hasTrackedInitialPlay = true;
passedBreakpoint = 0; // Reset breakpoints for this new viewing session
}
// Start checking progress if not already doing so
if (!videoInterval) {
videoInterval = setInterval(checkVideoState, 1000); // Check every second
}
} else if (event.data === YT.PlayerState.PAUSED || event.data === YT.PlayerState.BUFFERING) {
// Video paused or buffering, stop checking progress
clearInterval(videoInterval);
videoInterval = null;
} else if (event.data === YT.PlayerState.ENDED) {
// Video ended
checkVideoState(); // Run one last time to catch any final breakpoint
usermaven('track', `watched-${videoEventName}-100%`);
clearInterval(videoInterval);
videoInterval = null;
hasTrackedInitialPlay = false; // Reset for a potential replay
// passedBreakpoint will be reset on the next PLAYING event
} else if (event.data === YT.PlayerState.UNSTARTED || event.data === YT.PlayerState.CUED) {
// Video is reset (e.g. new video loaded) or cued.
clearInterval(videoInterval);
videoInterval = null;
hasTrackedInitialPlay = false;
passedBreakpoint = 0;
}
}
// 4. Function to check video progress and track breakpoints
function checkVideoState() {
if (!player || typeof player.getDuration !== 'function' || typeof player.getCurrentTime !== 'function') {
return; // Player not ready or methods unavailable
}
const duration = player.getDuration();
if (duration === 0) {
return; // Duration not available yet
}
const currentTime = player.getCurrentTime();
const viewedPercentage = currentTime / duration;
breakpoints.forEach((breakpoint) => {
if (passedBreakpoint < breakpoint && viewedPercentage >= breakpoint) {
passedBreakpoint = breakpoint; // Mark this breakpoint as passed
usermaven('track', `watched-${videoEventName}-${breakpoint * 100}%`);
}
});
}This is a placeholder. Your actual Usermaven SDK setup will provide the
usermaven()function.// function usermaven(type, eventName, properties) { // console.log(`Usermaven Event: ${type} - ${eventName}`, properties || ""); // }
3. Customization
videoEventName:
Change sample-video to a unique, descriptive name for your video (e.g., product-demo-video,
homepage-banner-video). This helps segment your analytics in Usermaven.
videoId:
Replace "M7lc1UVf-VE" with the actual YouTube Video ID you want to embed and track. The Video ID is the
string of characters after v= in a YouTube URL (e.g., for https://www.youtube.com/watch?v=dQw4w9WgXcQ, the
ID is dQw4w9WgXcQ).
breakpoints:
Adjust the [0.25, 0.5, 0.75] array if you want to track different progress milestones (e.g.,
[0.1, 0.5, 0.9] for 10%, 50%, 90%).
Player dimensions & playerVars:
Modify height, width, and the playerVars object within onYouTubeIframeAPIReady to customize the
player’s appearance and behavior. See YouTube Player Parameters .
How it works
onYouTubeIframeAPIReady():
This function is automatically called by the YouTube API once it’s loaded. It initializes the YouTube player
(YT.Player) targeting your <div id="player">.
onPlayerStateChange(event):
This function listens to player state changes:
- PLAYING: When the video starts playing (or resumes), it tracks a
clicked-{videoEventName}event (only once per “play session” thanks tohasTrackedInitialPlay). It also starts an interval (videoInterval) to periodically check progress.passedBreakpointis reset here to allow tracking for replays. - PAUSED / BUFFERING: Clears the progress-checking interval to save resources.
- ENDED: Tracks a
watched-{videoEventName}-100%event and clears the interval. It also resetshasTrackedInitialPlayso if the user replays, the “clicked” event fires again. - UNSTARTED / CUED: Resets tracking state if a new video is cued or the player is reset.
checkVideoState():
Called every second while the video is playing.
- It calculates the current
viewedPercentage. - It iterates through your defined
breakpoints. If theviewedPercentagehas passed a new breakpoint (and that breakpoint hasn’t been reported yet, managed bypassedBreakpoint), it sends awatched-{videoEventName}-{percentage}%event to Usermaven.
Example usermaven events
Assuming videoEventName is "product-tour":
- User clicks play:
usermaven("track", "clicked-product-tour") - User watches 25% of the video:
usermaven("track", "watched-product-tour-25%") - User watches 50% of the video:
usermaven("track", "watched-product-tour-50%") - User watches 75% of the video:
usermaven("track", "watched-product-tour-75%") - User watches the entire video:
usermaven("track", "watched-product-tour-100%")
Important considerations
Single Page Applications (SPAs):
If your site is an SPA and you dynamically load/unload YouTube players without a full page refresh, ensure
this script is re-initialized or that player instances are correctly managed (destroyed and recreated) to
avoid issues with stale player objects or event listeners. The UNSTARTED / CUED state handling helps here.
Multiple videos on one page:
If you have multiple YouTube players on a single page, you’ll need to adapt this script. Each player would
need its own YT.Player instance, and you’d likely need to manage state variables (like passedBreakpoint,
videoInterval) distinctly for each player, perhaps by associating them with the player’s ID or an object.
Error handling:
For production, you might want to add more robust error handling (e.g., try...catch blocks) around API
calls.