Skip to content

How analytics works on koi-player

This guide explains how analytics work in the <koi-audio-player> and <koi-card-audio-player> web components, what is captured automatically, and how a publisher embedding the player can track user reproductions and clicks themselves.

There are two layers:

  1. Media QoE analytics (Mux) — already wired into the library. Captures plays, pauses, seeks, completion, buffering, startup time, and errors. Enabled by default per player instance.
  2. Product analytics (your tool of choice) — not wired. The library exposes the events you need so you can forward them to GA, Segment, Adobe, Amplitude, or anything else.

1. Built-in Mux analytics (QoE)

The library auto-instruments the underlying <audio> element with Mux via src/analytics.ts.

Enabling

Mux initialization is automatic when both attributes are present:

<koi-audio-player
org-id="your-org-id"
media-id="some-media-id"
env="prod"
></koi-audio-player>
  • org-id — maps to Mux sub_property_id and is required.
  • media-id — maps to Mux video_id and is required.
  • env — "prod" or "sandbox". Each routes to a different Mux environment key (see getPublicKey in src/analytics.ts).
  • If stream is used instead of media-id, Mux is not initialized.

What Mux records automatically

No code on your side. Mux captures:

  • Views and play starts
  • Pause, seek (seeking / seeked), resume
  • Completion (ended)
  • Rebuffering count and duration
  • Startup time, time to first frame
  • Playback errors
  • Device, browser, OS, connection metadata

Data shows up in your Mux dashboard keyed off the org-id you set.

Sampling

The sampleRate value in the player’s meta object controls how often Mux is initialized (probabilistic, per page load). When a session is sampled out, the library dispatches a koi-analytics-skipped event internally for its own use; that event bus is not currently exported from the package, so external consumers cannot observe it without a library change.

What Mux is NOT for

Mux is media QoE. It will not tell you:

  • Which button the user clicked
  • Whether a play came from a user click vs. autoplay
  • Funnel conversions, sign-ups, scroll depth, attribution

For any of those, use the product-analytics integration patterns below.


2. Tracking audio reproductions yourself

The library dispatches a koi-player-ready event on document once the player has mounted. Its detail.player is the custom-element instance, which exposes mediaElement (the underlying <audio> element) via a getter.

Native HTML5 media events are the cleanest way to track reproductions:

<koi-audio-player org-id="..." media-id="..."></koi-audio-player>
<script>
document.addEventListener('koi-player-ready', (e) => {
const player = e.detail.player;
const audio = player.mediaElement;
const id = player.getAttribute('media-id');
audio.addEventListener('play', () => myAnalytics.track('audio_play', { mediaId: id }));
audio.addEventListener('pause', () => myAnalytics.track('audio_pause', { mediaId: id, t: audio.currentTime }));
audio.addEventListener('ended', () => myAnalytics.track('audio_complete', { mediaId: id }));
// Quartile progress
const fired = new Set();
audio.addEventListener('timeupdate', () => {
if (!audio.duration) return;
const pct = (audio.currentTime / audio.duration) * 100;
[25, 50, 75].forEach((q) => {
if (pct >= q && !fired.has(q)) {
fired.add(q);
myAnalytics.track('audio_progress', { mediaId: id, quartile: q });
}
});
});
});
</script>

Replace myAnalytics.track(...) with your provider’s call (gtag, analytics.track, window.adobeDataLayer.push, etc.).


3. Tracking clicks

Click scopeHow to hook it
Play / pause button (distinguish user click from autoplay / scripted play)Reach into the player’s open shadow root: player.shadowRoot.querySelector('media-play-button') and add a pointerdown listener. The native play event alone cannot tell you whether the user clicked.
Other control-bar buttons (volume, mute, fullscreen)Same shadow-root pattern: query the media-chrome custom elements (media-mute-button, media-volume-range, …) inside player.shadowRoot and attach click/change listeners. Be aware this is shadow-DOM piercing — internal markup can change between library versions.
Player-container clicks (any click on the host element)Listen on the host element itself: player.addEventListener('click', …). Clicks on shadow internals retarget to the host, so this catches generic clicks without piercing the shadow DOM.

Worked example — distinguishing user-driven play from autoplay

document.addEventListener('koi-player-ready', (e) => {
const player = e.detail.player;
const audio = player.mediaElement;
const playBtn = player.shadowRoot.querySelector('media-play-button');
let userClicked = false;
if (playBtn) {
playBtn.addEventListener('pointerdown', () => { userClicked = true; });
}
audio.addEventListener('play', () => {
myAnalytics.track('audio_play', {
mediaId: player.getAttribute('media-id'),
source: userClicked ? 'user_click' : 'programmatic',
});
userClicked = false;
});
});

4. Wiring to a specific vendor

The player is intentionally vendor-neutral for product analytics — any SDK works. Example using Google Analytics 4:

document.addEventListener('koi-player-ready', (e) => {
const player = e.detail.player;
const audio = player.mediaElement;
const id = player.getAttribute('media-id');
audio.addEventListener('play', () => gtag('event', 'audio_play', { media_id: id }));
audio.addEventListener('ended', () => gtag('event', 'audio_complete', { media_id: id }));
});

Swap gtag(...) for Segment’s analytics.track(...), Adobe’s _satellite.track(...), Amplitude’s amplitude.track(...), etc.


Reference: event surfaces available today

SurfaceWhereWhat you get
document event koi-player-readyDispatched once per player from BasePlayer.finalizeSetupdetail.player — the custom-element instance
document event koi-player-errorDispatched if the player fails to initializedetail.player, detail.error
player.mediaElementGetter on KoiAudioPlayerThe underlying <audio> element — listen to native HTML5 media events here
player.shadowRootOpen mode — direct accessMedia-chrome control elements (use for click tracking)