Snowplow media tracking on web
This plugin is the recommended way to manually track media events from video players. This plugin allows you to implement media tracking for any player.
The plugin is available since version 3.12 of the tracker.
Snowplow media events and entities must be manually tracked.
Install plugin
- JavaScript (tag)
- Browser (npm)
| Tracker Distribution | Included |
|---|---|
sp.js | ❌ |
sp.lite.js | ❌ |
Download:
| Download from GitHub Releases (Recommended) | Github Releases (plugins.umd.zip) |
| Available on jsDelivr | jsDelivr (latest) |
| Available on unpkg | unpkg (latest) |
window.snowplow(
'addPlugin',
'https://cdn.jsdelivr.net/npm/@snowplow/browser-plugin-media@latest/dist/index.umd.min.js',
['snowplowMedia', 'SnowplowMediaPlugin']
);
npm install @snowplow/browser-plugin-mediayarn add @snowplow/browser-plugin-mediapnpm add @snowplow/browser-plugin-media
import { newTracker, trackPageView } from '@snowplow/browser-tracker';
import { SnowplowMediaPlugin } from '@snowplow/browser-plugin-media';
newTracker('sp1', '{{collector_url}}', {
appId: 'my-app-id',
plugins: [ SnowplowMediaPlugin() ],
});
Usage
The Snowplow media tracking APIs enable you to track events from media playback on the web and in mobile apps. Track changes in playback state (play, pause, seek), playback position (ping and percentage progress events), and ad playback (ad breaks, ad progress, ad clicks).
For details on the events and entities tracked, see the media tracking overview.
See the tracked media events and entities live as you watch a video in our React example app. View the source code.
Quick start
A media tracking session follows this lifecycle:
- Start tracking when the player loads (before playback begins)
- Update player state every second during playback
- Track events as they occur (play, pause, seek, etc.)
- End tracking when the user leaves
- JavaScript (tag)
- Browser (npm)
// 1. Start tracking when player loads
const id = crypto.randomUUID();
window.snowplow('startMediaTracking', {
id,
player: {
duration: 300,
label: 'My Video Title',
mediaType: 'video',
playerType: 'html5'
}
});
// 2. Update player state every second
setInterval(() => {
window.snowplow('updateMediaTracking', {
id,
player: { currentTime: player.currentTime }
});
}, 1000);
// 3. Track events as they occur
window.snowplow('trackMediaPlay', { id });
window.snowplow('trackMediaPause', { id });
window.snowplow('trackMediaSeekStart', { id });
window.snowplow('trackMediaSeekEnd', { id });
window.snowplow('trackMediaEnd', { id });
// 4. End tracking when done
window.snowplow('endMediaTracking', { id });
import {
startMediaTracking,
updateMediaTracking,
endMediaTracking,
trackMediaPlay,
trackMediaPause,
trackMediaSeekStart,
trackMediaSeekEnd,
trackMediaEnd,
MediaType
} from "@snowplow/browser-plugin-media";
// 1. Start tracking when player loads
const id = crypto.randomUUID();
startMediaTracking({
id,
player: {
duration: 300,
label: 'My Video Title',
mediaType: MediaType.Video,
playerType: 'html5'
}
});
// 2. Update player state every second
setInterval(() => {
updateMediaTracking({
id,
player: { currentTime: player.currentTime }
});
}, 1000);
// 3. Track events as they occur
trackMediaPlay({ id });
trackMediaPause({ id });
trackMediaSeekStart({ id });
trackMediaSeekEnd({ id });
trackMediaEnd({ id });
// 4. End tracking when done
endMediaTracking({ id });
Configuration
Configure media tracking when you call startMediaTracking. All configuration is optional.
Player properties
Set initial player properties to populate the media player entity. The label property is recommended as it helps identify content during analysis.
- JavaScript (tag)
- Browser (npm)
window.snowplow('startMediaTracking', {
id,
player: {
currentTime: 0, // Current playback position in seconds
duration: 300, // Total duration in seconds
ended: false, // Whether playback has ended
livestream: false, // Whether this is a live stream
label: 'My Video', // Human-readable title (recommended)
loop: false, // Whether to restart after ending
mediaType: 'video', // 'video' or 'audio'
muted: false, // Whether audio is muted
paused: true, // Whether playback is paused
pictureInPicture: false, // Whether in picture-in-picture mode
playerType: 'html5', // Player type identifier
playbackRate: 1.0, // Playback speed (1 = normal)
quality: '1080p', // Quality level
volume: 100 // Volume percentage (0-100)
}
});
import { startMediaTracking, MediaType } from "@snowplow/browser-plugin-media";
startMediaTracking({
id,
player: {
currentTime: 0, // Current playback position in seconds
duration: 300, // Total duration in seconds
ended: false, // Whether playback has ended
livestream: false, // Whether this is a live stream
label: 'My Video', // Human-readable title (recommended)
loop: false, // Whether to restart after ending
mediaType: MediaType.Video, // MediaType.Video or MediaType.Audio
muted: false, // Whether audio is muted
paused: true, // Whether playback is paused
pictureInPicture: false, // Whether in picture-in-picture mode
playerType: 'html5', // Player type identifier
playbackRate: 1.0, // Playback speed (1 = normal)
quality: '1080p', // Quality level
volume: 100 // Volume percentage (0-100)
}
});
Ping events
Media ping events are sent at regular intervals (default: 30 seconds) to report playback position. You can configure ping behavior in the starting configuration.
- JavaScript (tag)
- Browser (npm)
window.snowplow('startMediaTracking', {
id,
pings: {
pingInterval: 30, // Seconds between pings (default: 30)
maxPausedPings: 1 // Max pings while paused (default: 1)
}
});
// Or turn off pings entirely
window.snowplow('startMediaTracking', { id, pings: false });
startMediaTracking({
id,
pings: {
pingInterval: 30, // Seconds between pings (default: 30)
maxPausedPings: 1 // Max pings while paused (default: 1)
}
});
// Or turn off pings entirely
startMediaTracking({ id, pings: false });
Percentage progress
Track events when playback reaches specified percentage boundaries.
- JavaScript (tag)
- Browser (npm)
window.snowplow('startMediaTracking', {
id,
boundaries: [10, 25, 50, 75, 90] // Fire events at these percentages
});
startMediaTracking({
id,
boundaries: [10, 25, 50, 75, 90] // Fire events at these percentages
});
Session tracking
The media session entity is attached to all media events by default. It's optional for the Media Player data model, so you could choose not to include it.
We recommend tracking this entity.
- JavaScript (tag)
- Browser (npm)
window.snowplow('startMediaTracking', { id, session: true });
startMediaTracking({ id, session: true });
Custom entities
You can attach custom entities to all events for this media tracking instance.
- JavaScript (tag)
- Browser (npm)
window.snowplow('startMediaTracking', {
id,
context: [
{
schema: 'iglu:com.example/video_metadata/jsonschema/1-0-0',
data: { contentId: 'abc123', category: 'tutorial' }
}
]
});
startMediaTracking({
id,
context: [
{
schema: 'iglu:com.example/video_metadata/jsonschema/1-0-0',
data: { contentId: 'abc123', category: 'tutorial' }
}
]
});
Filter events
- JavaScript (tag)
- Browser (npm)
Only track specific event types by providing an allowlist:
window.snowplow('startMediaTracking', {
id,
captureEvents: ['play', 'pause', 'end'] // Only track these events
});
When users scrub through video, media players can generate many rapid seek or volume change events. The tracker automatically coalesces these into just the first and last event. This behavior is optional:
// Turn off filtering entirely
window.snowplow('startMediaTracking', { id, filterOutRepeatedEvents: false });
// Or configure separately for seek and volume events
window.snowplow('startMediaTracking', {
id,
filterOutRepeatedEvents: {
seekEvents: true,
volumeChangeEvents: false,
flushTimeoutMs: 5000 // Time before flushing buffered events (default: 5000)
}
});
Only track specific event types by providing an allowlist:
import { MediaEventType } from "@snowplow/browser-plugin-media";
startMediaTracking({
id,
captureEvents: [MediaEventType.Play, MediaEventType.Pause, MediaEventType.End]
});
When users scrub through video, media players can generate many rapid seek or volume change events. The tracker automatically coalesces these into just the first and last event. This behavior is optional:
// Turn off filtering entirely
startMediaTracking({ id, filterOutRepeatedEvents: false });
// Or configure separately for seek and volume events
startMediaTracking({
id,
filterOutRepeatedEvents: {
seekEvents: true,
volumeChangeEvents: false,
flushTimeoutMs: 5000 // Time before flushing buffered events (default: 5000)
}
});
Tracking events
Track events by calling the appropriate function when player events occur. For a complete list of available events and their properties, see the media events reference.
Playback events
- JavaScript (tag)
- Browser (npm)
window.snowplow('trackMediaReady', { id }); // Player is ready
window.snowplow('trackMediaPlay', { id }); // Playback started
window.snowplow('trackMediaPause', { id }); // Playback paused
window.snowplow('trackMediaEnd', { id }); // Playback ended
window.snowplow('trackMediaSeekStart', { id }); // Seek operation started
window.snowplow('trackMediaSeekEnd', { id }); // Seek operation ended
window.snowplow('trackMediaBufferStart', { id }); // Buffering started
window.snowplow('trackMediaBufferEnd', { id }); // Buffering ended
import {
trackMediaReady, trackMediaPlay, trackMediaPause, trackMediaEnd,
trackMediaSeekStart, trackMediaSeekEnd, trackMediaBufferStart, trackMediaBufferEnd
} from "@snowplow/browser-plugin-media";
trackMediaReady({ id }); // Player is ready
trackMediaPlay({ id }); // Playback started
trackMediaPause({ id }); // Playback paused
trackMediaEnd({ id }); // Playback ended
trackMediaSeekStart({ id }); // Seek operation started
trackMediaSeekEnd({ id }); // Seek operation ended
trackMediaBufferStart({ id }); // Buffering started
trackMediaBufferEnd({ id }); // Buffering ended
Player state events
- JavaScript (tag)
- Browser (npm)
window.snowplow('trackMediaPlaybackRateChange', { id, newRate: 1.5 });
window.snowplow('trackMediaVolumeChange', { id, newVolume: 80 });
window.snowplow('trackMediaFullscreenChange', { id, fullscreen: true });
window.snowplow('trackMediaPictureInPictureChange', { id, pictureInPicture: true });
window.snowplow('trackMediaQualityChange', {
id,
newQuality: '1080p',
bitrate: 5000,
framesPerSecond: 30,
automatic: true
});
window.snowplow('trackMediaError', {
id,
errorCode: '500',
errorName: 'NetworkError',
errorDescription: 'Failed to load media'
});
import {
trackMediaPlaybackRateChange, trackMediaVolumeChange, trackMediaFullscreenChange,
trackMediaPictureInPictureChange, trackMediaQualityChange, trackMediaError
} from "@snowplow/browser-plugin-media";
trackMediaPlaybackRateChange({ id, newRate: 1.5 });
trackMediaVolumeChange({ id, newVolume: 80 });
trackMediaFullscreenChange({ id, fullscreen: true });
trackMediaPictureInPictureChange({ id, pictureInPicture: true });
trackMediaQualityChange({
id,
newQuality: '1080p',
bitrate: 5000,
framesPerSecond: 30,
automatic: true
});
trackMediaError({
id,
errorCode: '500',
errorName: 'NetworkError',
errorDescription: 'Failed to load media'
});
Ad events
Track advertising events with ad and ad break information. See the media ad and media ad break entity schemas for property details.
- JavaScript (tag)
- Browser (npm)
// Ad break events
window.snowplow('trackMediaAdBreakStart', {
id,
adBreak: {
breakId: 'break-1',
name: 'pre-roll',
breakType: 'linear',
podSize: 3
}
});
window.snowplow('trackMediaAdBreakEnd', { id });
// Ad events
window.snowplow('trackMediaAdStart', {
id,
ad: {
adId: 'ad-1',
name: 'Product Ad',
creativeId: 'creative-1',
duration: 30,
skippable: true
}
});
window.snowplow('trackMediaAdFirstQuartile', { id });
window.snowplow('trackMediaAdMidpoint', { id });
window.snowplow('trackMediaAdThirdQuartile', { id });
window.snowplow('trackMediaAdComplete', { id });
window.snowplow('trackMediaAdSkip', { id });
window.snowplow('trackMediaAdClick', { id });
window.snowplow('trackMediaAdPause', { id });
window.snowplow('trackMediaAdResume', { id });
import {
trackMediaAdBreakStart, trackMediaAdBreakEnd, trackMediaAdStart,
trackMediaAdFirstQuartile, trackMediaAdMidpoint, trackMediaAdThirdQuartile,
trackMediaAdComplete, trackMediaAdSkip, trackMediaAdClick,
trackMediaAdPause, trackMediaAdResume, MediaPlayerAdBreakType
} from "@snowplow/browser-plugin-media";
// Ad break events
trackMediaAdBreakStart({
id,
adBreak: {
breakId: 'break-1',
name: 'pre-roll',
breakType: MediaPlayerAdBreakType.Linear,
podSize: 3
}
});
trackMediaAdBreakEnd({ id });
// Ad events
trackMediaAdStart({
id,
ad: {
adId: 'ad-1',
name: 'Product Ad',
creativeId: 'creative-1',
duration: 30,
skippable: true
}
});
trackMediaAdFirstQuartile({ id });
trackMediaAdMidpoint({ id });
trackMediaAdThirdQuartile({ id });
trackMediaAdComplete({ id });
trackMediaAdSkip({ id });
trackMediaAdClick({ id });
trackMediaAdPause({ id });
trackMediaAdResume({ id });
Custom events
Track custom self-describing events within the media session. The tracker automatically attaches media entities.
- JavaScript (tag)
- Browser (npm)
window.snowplow('trackMediaSelfDescribingEvent', {
id,
event: {
schema: 'iglu:com.example/video_interaction/jsonschema/1-0-0',
data: { action: 'share', platform: 'twitter' }
}
});
import { trackMediaSelfDescribingEvent } from "@snowplow/browser-plugin-media";
trackMediaSelfDescribingEvent({
id,
event: {
schema: 'iglu:com.example/video_interaction/jsonschema/1-0-0',
data: { action: 'share', platform: 'twitter' }
}
});
Update player state
- JavaScript (tag)
- Browser (npm)
Update the player state every second during playback. This ensures accurate metrics in ping events and the session entity. This function does not send any events.
window.snowplow('updateMediaTracking', {
id,
player: { currentTime: player.currentTime }
});
Update the player state every second during playback. This ensures accurate metrics in ping events and the session entity. This function does not send any events.
import { updateMediaTracking } from "@snowplow/browser-plugin-media";
updateMediaTracking({
id,
player: { currentTime: player.currentTime }
});
Page activity during playback
When users watch video, they may not interact with the page, causing page pings to stop. The media plugin automatically keeps page activity alive during playback by calling updatePageActivity whenever media events are tracked.
You can turn off this behavior if not needed:
- JavaScript (tag)
- Browser (npm)
window.snowplow('startMediaTracking', {
id,
updatePageActivityWhilePlaying: false
});
startMediaTracking({
id,
updatePageActivityWhilePlaying: false
});