Skip to main content
Track video engagement to build video-based cohorts and understand viewing behavior.

Initialization

Player Lifecycle

Best Practices

Overview

Video tracking in the Permutive JavaScript SDK is handled by the CTV addon. The addon manages engagement timing automatically and tracks the standard Permutive video events — Videoview and VideoCompletion — without requiring manual interval logic or custom event construction. The CTV addon is the correct approach for video tracking on:
  • Standard web pages with embedded video players
  • Web-based CTV applications (Samsung Tizen, LG WebOS, HbbTV)
The CTV addon must be enabled for your project before use. Enable it in the Permutive dashboard under Settings > Integrations.

Initializing the CTV Addon

Initialize the addon when a user starts watching a video. Calling permutive.addon("ctv", ...) automatically tracks a Videoview event and prepares the addon for engagement tracking.
Deprecation notice: The top-level videoProperties and client inputs are deprecated. Specify them through eventProperties instead, as eventProperties.video and eventProperties.client. eventProperties is applied at the top level of the video events the addon tracks, so it can also carry any custom top-level properties defined in your event schema:
After initialization, the addon is available at permutive.addons.ctv.

Duration

Pass duration in milliseconds. This is required for the VideoCompletion event to include an accurate completion percentage. If the duration is not known at initialization time, you can set it later using .setDuration() before calling .stop().

Player Lifecycle

Map your video player’s events to the CTV addon methods. The addon handles engagement timing internally — there is no need to implement your own interval or timer logic. Positions are passed in milliseconds.

Generic HTML5 Video Example

Handling Duration Not Yet Available

If duration is not available when the video starts (common with live streams or when metadata loads asynchronously), initialize without it and call .setDuration() once the value is known:

Handling Seek and Scrub

When a user seeks to a new position, call .play() with the new position. This correctly updates the engagement tracker’s reference point without inflating engaged time:

Handling Buffering

Pause engagement tracking while the player is buffering so that buffer time is not counted as active engagement:

Video Ad Tracking

Track video ad events using the addon’s .track() method. For mid-roll ads, pause the main content tracker while the ad plays and resume it when the ad finishes.

Ad Event Reference

Ad Event Properties

Properties for video ad events are nested under the ad key:

Pre-roll Ads

Mid-roll Ads

Post-roll Ads

Video Properties

Pass video metadata under the eventProperties.video key when initializing the addon. All properties are optional but richer metadata enables more precise cohort building. Properties are type-checked by the addon. A property with the wrong type (for example, passing audio_language as an array instead of a string) will be silently removed from the event rather than causing a schema error.

Complete Working Example

A full integration with an HTML5 video element, including ad break handling:

Engagement Tracking Patterns

The CTV addon tracks engagement automatically. You do not need to implement interval timers or milestone checks manually. The addon’s internal engagement model works as follows:
  1. Play — When .play() is called, the engagement clock starts.
  2. Pause — When .pause() is called, the clock stops. Time elapsed since the last .play() call is accumulated.
  3. Resume — When .play() is called again, the clock resumes from the current accumulated total.
  4. Stop — When .stop() is called, the final accumulated engaged_time and the calculated completion percentage are included in the VideoCompletion event automatically.
This means skipped content (via seeking) is never counted as engaged time, and buffering time can be excluded by calling .pause() during buffer states.

Best Practices

The VideoCompletion event is only tracked when .stop() is called. Make sure you call it not just when a video ends naturally, but also when a user navigates away or closes the player early.
The completion percentage in VideoCompletion is calculated from the duration you provide. Set duration in milliseconds at initialization, or call .setDuration() once metadata is available. Do not guess or hardcode durations.
Call permutive.addon("ctv", ...) once per piece of content. Calling it again reinitializes the addon and tracks a new Videoview event. If the user watches multiple videos in a session, reinitialize for each new piece of content after calling .stop() on the previous one.
Call .pause() when a mid-roll ad starts so that ad playback time is not included in the main content’s engaged_time. Resume with .play() when the ad finishes.
Every property you pass in eventProperties.video becomes available for cohort building and targeting. At minimum, include title. Adding genre, content_type, season_number, and episode_number significantly increases the granularity of video-based cohorts.

Troubleshooting

Problem: The Videoview event is not visible in Permutive analytics.Solutions:
  • Confirm the CTV addon is enabled under Settings > Integrations in the Permutive dashboard.
  • Verify permutive.addon("ctv", ...) is being called in the browser console — it should not throw an error.
  • Check that the SDK is initialized before permutive.addon("ctv", ...) is called.
  • Allow 5-10 minutes for events to appear in the dashboard after they fire.
Problem: Calling permutive.addons.ctv.play() throws because the addon is not available.Solutions:
  • Ensure permutive.addon("ctv", ...) has been called before accessing permutive.addons.ctv.
  • Add a guard check: if (permutive.addons.ctv) { ... } before calling addon methods.
  • Confirm the CTV addon is enabled in your workspace settings — it will not be registered if it has not been enabled.
Problem: The VideoCompletion event has a completion value of 0 or is missing it.Solutions:
  • Ensure duration is passed (in milliseconds) when calling permutive.addon("ctv", ...).
  • If duration is not available at initialization, call .setDuration(ms) before .stop() is called.
  • Verify that the duration value is in milliseconds, not seconds.
Problem: The engaged_time in VideoCompletion includes buffering or paused time.Solutions:
  • Call .pause() when the player enters a buffering state (waiting event on HTML5 video).
  • Call .pause() when the player is paused by the user.
  • Verify that .pause() is being called before ad breaks start.
Problem: Multiple Videoview events fire for a single piece of content.Solutions:
  • permutive.addon("ctv", ...) fires a Videoview each time it is called. Guard the initialization so it only runs once per content session.
  • Use a flag or check permutive.addons.ctv to avoid re-initializing on repeated play events from the player.

CTV Video Tracking

Video event schema and cross-platform tracking patterns

Video Integrations

Pre-built integrations for JW Player, Brightcove, YouTube, and more

Event Tracking

General event tracking with the JavaScript SDK

Web CTV

Full CTV addon reference for web-based CTV platforms