Skip to content

How analytics works on the Arc Audio Player

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

Two layers apply here:

  1. Playback quality telemetry, which the player collects automatically and reports to Arc XP for service monitoring. Enabled by default per player instance; you can opt out.
  2. Product analytics (your tool of choice), which you wire yourself. The library exposes the events you need so you can forward them to GA, Segment, Adobe, Amplitude, or anything else.

1. Built-in playback telemetry (QoE)

The player automatically collects playback quality-of-experience telemetry and reports it to Arc XP for platform-wide playback-health monitoring. Collection runs through Mux, a third-party QoE service, under Arc XP’s account: you have nothing to set up, and the data never reaches any analytics account of yours.

When it runs

Telemetry starts automatically when the player loads a clip by media-id (this also requires org-id):

<arc-audio-player
org-id="your-org-id"
media-id="some-media-id"
env="prod"
></arc-audio-player>

Direct-stream playback (the stream attribute instead of media-id) sends no telemetry.

What the player collects

  • 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, operating system, connection metadata

Turn off telemetry

To turn the built-in telemetry off, add the disable-default-analytics attribute (or set disableDefaultAnalytics: true inside config; the config value wins over the attribute when both exist). Turn it off for consent or data-processing reasons, or because you already run a separate analytics setup:

<arc-audio-player org-id="your-org-id" media-id="some-media-id" disable-default-analytics></arc-audio-player>

Opting out only disables the telemetry. The arc-player-* events that follow still fire, so you can turn off the built-in reporting and run separate analytics on those events.

Telemetry scope

This telemetry reports playback service health: errors, buffering, and startup performance. To measure engagement (clicks, click-driven versus autoplay-driven plays, funnels, sign-ups, or attribution), wire the player’s events into a separate tool by using the patterns later in this guide.


2. Tracking playback yourself

Prefer the built-in playback events

The library already emits high-level playback events on the player element (bubbling), so in most cases you do not need the manual timeupdate math in the next section:

  • arc-player-playback-started: fires once when playback actually begins.
  • arc-player-playback-progress: fires once per watched quartile, with detail.quartile = 25 | 50 | 75 (the player ignores seeks, so scrubbing does not false-fire).
  • arc-player-playback-completed: fires on completion.

Each event’s detail includes the player instance and a context snapshot. They bubble, so one document-level listener covers every player on the page:

document.addEventListener('arc-player-playback-started', (e) => {
myAnalytics.track('audio_play', { mediaId: e.detail.player.getAttribute('media-id') });
});

Reach for the manual approach below only when you need custom thresholds.

Manual tracking with native media events

The library dispatches a arc-player-ready event on document after the player mounts. Its detail.player holds the custom-element instance, which exposes mediaElement (the underlying <audio> element) through a getter.

Native HTML5 media events offer another way to track reproductions:

<arc-audio-player org-id="..." media-id="..."></arc-audio-player>
<script>
document.addEventListener('arc-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, and so on).


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. This approach pierces the shadow DOM, so internal markup can change between library versions.
Player-container clicks (a click that lands 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('arc-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 stays vendor-neutral for product analytics: any SDK works. The following example uses Google Analytics 4:

document.addEventListener('arc-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(...), and so on.


Reference: available events and properties

All event names use the standard arc-player-* form. Only arc-player-ready and arc-player-error have earlier koi-player-* aliases that still fire for back-compat, and the playback events have no earlier alias.

NameWhereWhat you get
arc-player-readyDispatched once per player (on the element and document)detail.player, the custom-element instance
arc-player-errorDispatched if the player fails to initializedetail.player, detail.error
arc-player-playback-startedDispatched on the player element when playback beginsdetail.player, detail.context
arc-player-playback-progressDispatched once per watched quartiledetail.quartile (25/50/75), detail.player, detail.context
arc-player-playback-completedDispatched on completiondetail.player, detail.context
player.mediaElementGetter on the audio playerThe underlying <audio> element; use it to listen to native HTML5 media events
player.shadowRootOpen mode, direct accessMedia-chrome control elements (use for click tracking)