av.heartbeat is what keeps an AV session alive between play/stop boundaries and what enables time-spent metrics to be calculated accurately.
What av.heartbeat does
While a piece of content is playing, the Piano SDK fires av.heartbeat events at a regular interval. Each heartbeat:
-
Marks that the session is still active.
-
Carries the current
av_position(cursor position in the content). -
Provides the timestamp that subsequent time-spent calculations use as the "previous event" anchor.
Without heartbeats, AV Insights only knows when sessions start and end — it can't measure how much content was actually consumed between those bookends.
Configuring the heartbeat delay
The heartbeat delay is not set by default — you have to configure it. Without an explicit value, heartbeats won't fire and time-spent metrics on long content will under-read. The minimum allowed delay is 5 seconds.
The configuration accepts two formats: a single value (constant delay for the whole playback) or an object (stepped delays that change as the playback progresses).
Constant delay (integer)
Pass a single integer (in seconds). That delay applies to the entire playback.
Example: a value of 10 fires a heartbeat every 10 seconds from the start of playback through to the end.
Stepped delay (object)
Pass an object keyed by minute of playback, where each value is the heartbeat delay (in seconds) starting from that minute.
Example:
{ 0: 5, 1: 10, 5: 60 }
Read as:
-
From minute 0 to minute 1: a heartbeat every 5 seconds.
-
From minute 1 to minute 5: a heartbeat every 10 seconds.
-
From minute 5 onwards: a heartbeat every 60 seconds.
This stepped pattern is useful for long-form content where you want high resolution at the start (to detect early drop-off) and lower event volume once the user is genuinely engaged.
Stopping heartbeats with 0
A value of 0 means no heartbeat — neither the timer fires nor the event is sent.
-
In integer format,
0disables heartbeats for the entire playback. Use this only if you've decided you don't need time-spent metrics on this content. -
In object format, a
0at any step stops heartbeats for the rest of the playback from that minute onwards. Example:{ 0: 5, 10: 0 }heartbeats every 5 seconds for the first 10 minutes, then stops entirely.
Trade-offs
Decreasing the delay improves time-resolution but raises event volume (and the analytics cost / client CPU that comes with it). Increasing the delay reduces volume but coarsens the measurement — sessions ending between heartbeats lose their tail-end consumption time. The stepped object format is the standard way to get the best of both: fine resolution early, coarse later.
Hard upper bound: 2 hours
Whatever delay you configure, keep it well under 2 hours. Piano's processing excludes any AV event whose av_duration (the gap to the previous AV event) exceeds 2 hours, as a data-quality safeguard. A heartbeat delay configured above that ceiling — whether as a constant integer or as a high step in the object format — will cause the following event to be dropped from reports.
Server-side (AV Light Advanced)
In server-side AV Light Advanced mode, you fire heartbeat events manually rather than configuring the SDK. The recommended cadence is at each video quartile (25%, 50%, 75%, 100%) for long-form content, or every few seconds for short clips. The same 0 = no heartbeat rule applies — if you don't fire heartbeats, the session has no time-spent data.
Heartbeats in D+1: merging and event-count effects
Consecutive heartbeats are merged into a single event by the daily optimisation routine (D+1). Two practical effects:
-
m_eventsandm_events_allmetrics exclude heartbeats. A visit consisting only of heartbeat and refresh events can appear in reports as a visit with 0 events and 0 page views — that's expected, not a bug. -
Property changes during heartbeat-only sequences can be lost. If a property (for example
av_language) changes value on a heartbeat and no non-heartbeat event follows to "anchor" the new value, the change is dropped during heartbeat merging in D+1. To preserve mid-stream property changes, fire a non-heartbeat event (av.pause, a static event likeav.subtitle.on, etc.) immediately after the property change.
When the timer pauses and resumes
The heartbeat timer:
-
Pauses when the user pauses the player (
av.pausefires). -
Resumes when the user resumes (
av.resumefires). -
Resets to a new session when the user navigates to a new page or when a new
av_content_idstarts. -
Stops when the content reaches the end or when
av.stopis fired explicitly.
If you see heartbeats continuing while the player is paused, the SDK isn't being told about the pause — typically because the av.pause event isn't wired to the player's pause handler.
Page-change behavior
When the user navigates to a new page while a media is playing in the background (a sidebar audio player, for example), the heartbeat timer continues if the SDK is kept alive across navigations (single-page application), and resets to a fresh session if the page genuinely reloads.
If your site is a SPA but heartbeats are still resetting on navigation, the SDK is probably being reinitialized on each route change rather than persisting — that's a setup issue worth addressing for AV Insights specifically.
Heartbeat in multi-tab / background tab scenarios
Modern browsers throttle JavaScript timers in background tabs to save CPU. This means:
-
A heartbeat configured for short intervals (the 5-second minimum, for example) can fire much less frequently in a background tab — some browsers cap timers to once per minute or longer.
-
AV Insights' historical processing accounts for this by relying on the positions reported by each heartbeat rather than the number of heartbeats fired, but very long gaps may still underestimate consumed time.
For background-audio use cases where accurate time tracking matters even when the tab isn't focused, consider firing heartbeats from a Web Worker or relying on player-native event hooks that aren't subject to the throttle.
Common heartbeat problems
|
Symptom |
Most likely cause |
|---|---|
|
Heartbeats fire twice per interval |
Two heartbeat timers running — typical after a misconfigured event listener that registers the timer on every play instead of once at SDK init. |
|
Heartbeats continue while paused |
|
|
Heartbeats reset every few seconds |
SDK is being reinitialized too often (often on every route change in a SPA). |
|
No heartbeats at all in a session |
Either the heartbeat delay was never configured (there is no default — you have to set it), the configured value was |
|
Heartbeats fire after content ends |
The |