Skip to content

Usage (Web)

The SDK can be used either with the generic ClientAnalytics class for plain HTML5 video, or with player-specific adapters that provide tighter integration with popular video players.

Plain HTML5 Video

Create a ClientAnalytics instance with your API key, then call measure() on the video element:

<script src="surfmeter-client-analytics.js"></script>
<script>
  const sca = new surfmeter.ClientAnalytics('YOUR_API_KEY', {});

  const video = document.querySelector('video');
  const handler = sca.measure(video, {
    streamId: 'my-stream-id',
    metadata: {
      userId: 'user123',
      channelId: 'channel456',
    },
  });
</script>

Video.js

Use the VideoJsClientAnalytics class. For HLS/DASH streaming with quality tracking, configure Video.js to use MSE rather than native playback:

<link href="https://vjs.zencdn.net/8.16.1/video-js.css" rel="stylesheet" />
<script src="https://vjs.zencdn.net/8.16.1/video.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/videojs-contrib-quality-levels@4/dist/videojs-contrib-quality-levels.min.js"></script>
<script src="surfmeter-videojs-analytics.js"></script>

<video-js id="my-player" class="vjs-default-skin" controls width="640" height="360"></video-js>

<script>
  var player = videojs('my-player', {
    html5: {
      vhs: { overrideNative: true },
      nativeAudioTracks: false,
      nativeVideoTracks: false,
    },
  });

  player.src({ src: 'https://example.com/stream.m3u8', type: 'application/x-mpegURL' });

  var analytics = new surfmeter.VideoJsClientAnalytics('YOUR_API_KEY', {});
  var handler = analytics.measure(player, { streamId: 'YOUR_STREAM_ID' });
</script>

The videojs-contrib-quality-levels plugin is recommended for tracking bitrate, resolution, and frame rate changes. Without it, the adapter still collects standard HTML video element metrics but cannot track player-level quality switches.

Shaka Player

Use the ShakaplayerAnalytics class. Call measure() after the player has loaded a source:

<script src="https://cdn.jsdelivr.net/npm/shaka-player/dist/shaka-player.compiled.js"></script>
<script src="surfmeter-shaka-player-analytics.js"></script>

<script>
  const analytics = new surfmeter.ShakaplayerAnalytics('YOUR_API_KEY', {});
  const video = document.getElementById('video');
  const player = new shaka.Player(video);

  player.load('https://example.com/stream.mpd').then(() => {
    const handler = analytics.measure(player, { streamId: 'YOUR_STREAM_ID' });
  });
</script>

THEOplayer

Use the TheoplayerAnalytics class. Requires THEOplayer v8+:

<script src="https://cdn.jsdelivr.net/npm/theoplayer@10/THEOplayer.js"></script>
<script src="surfmeter-theoplayer-analytics.js"></script>

<script>
  const analytics = new surfmeter.TheoplayerAnalytics('YOUR_API_KEY', {});
  const player = new THEOplayer.Player(document.getElementById('player'), {
    libraryLocation: 'https://cdn.jsdelivr.net/npm/theoplayer@10/',
  });

  player.source = {
    sources: [{ src: 'https://example.com/stream.m3u8', type: 'application/x-mpegurl' }],
  };

  const handler = analytics.measure(player, { streamId: 'YOUR_STREAM_ID' });
</script>

ES Module Usage

All adapters can also be used as ES modules:

// Plain HTML5 video
import { ClientAnalytics } from '@surfmeter/client-analytics';

// Video.js
import { VideoJsClientAnalytics } from '@surfmeter/videojs-analytics';

// Shaka Player
import { ShakaplayerAnalytics } from '@surfmeter/shaka-player-analytics';

// THEOplayer
import { TheoplayerAnalytics } from '@surfmeter/theoplayer-web-analytics';

// // Chromecast
// import { ChromecastAnalytics2, TheoChromecastAnalytics } from '@surfmeter/chromecast-analytics';

API Reference

Constructor Options

All adapters accept the same constructor signature: new Analytics(apiKey, opts).

  • apiKey (string) — your Surfmeter API key.
  • opts (object):
    • fingerprint (string | () => string | Promise\<string>) — a unique identifier for the device or session, used to correlate measurements across page reloads. When omitted, the library generates one automatically using FingerprintJS (open-source edition), with crypto.randomUUID() as a fallback.

Measurement Options

analytics.measure(player, options?) starts monitoring. The options object supports:

  • streamId — a stable identifier for the content being played (video ID, channel ID, etc.). Recommended for all MSE-based players (Video.js, Shaka, THEOplayer), since the <video> element's src will be a blob: URL rather than the actual stream URL. If the SDK cannot determine the video source, it silently skips sending data.
  • metadata — arbitrary key-value object attached to the measurement. Useful for segmenting data in the dashboard (e.g., user ID, channel, content title).
  • params — additional parameters for the measurement.

Handler

The object returned by measure() exposes:

  • surfmeterMonitoringId — unique identifier for this measurement session.
  • destroy() — stop monitoring, remove all event listeners, and clean up. Call this when you tear down the player or navigate away.
  • onPlaybackError(error?) — manually report an error from your application code (e.g., DRM failures, network errors caught outside the player). Accepts either a plain string or a structured object:

    // Simple string message
    handler.onPlaybackError('Some Error Message');
    
    // Structured error details
    handler.onPlaybackError({
      message: 'Failed to load segment',
      errorType: 'NETWORK',
      errorCode: '404',
    });
    
    - onAdBreakStarted() — signal that an ad break has started. - onAdBreakEnded() — signal that an ad break has ended.

Setting Metadata After Initialization

You can update metadata on a running measurement:

analytics.setMetadata(handler.surfmeterMonitoringId, {
  userId: 'user123',
  channelId: 'channel456',
});

Similarly, you can set additional parameters:

analytics.setMeasurementParams(handler.surfmeterMonitoringId, {
  customParam: 'value',
});

Warning

Please make sure that if you send personal data such as customer IDs, names, or email addresses, you have the necessary consent of the user to do so.

What It Monitors

All adapters collect the following metrics:

  • Playback state transitions (playing, paused, seeking, stalling, ended)
  • Buffering and stalling events
  • Video and audio codec changes
  • Bitrate changes (video and audio)
  • Resolution changes (width, height)
  • Frame rate changes
  • Playback errors
  • Time updates and duration
  • Display size

Player-specific adapters additionally track quality information from the player's own API (e.g., quality levels in Video.js, track/variant data in Shaka Player, media track qualities in THEOplayer).

On WebKit browsers (Safari, iOS), bitrate is estimated from decoded byte counts on the video element rather than from player-level APIs.

Debug Logging

All adapters export an enableDebugLogs() function. Call it before creating the analytics instance to see detailed logs in the browser console:

import { enableDebugLogs } from '@surfmeter/videojs-analytics';

enableDebugLogs('trace'); // 'trace' | 'debug' | 'info' | 'warn' | 'error'

Cleanup

Always call handler.destroy() when you are done with the player:

handler.destroy();
player.dispose(); // or equivalent player cleanup

This removes all event listeners, stops data collection, and sends any remaining buffered data.