Live events / Shows
Live shows — concerts, awards, talk shows
Segment-aware analytics for any live event that isn't a sports match. Audience engagement is first-class — votes, polls, reactions, chat, donations, super-chat.
How shows differ from sports
Sports have phases and a score. Shows have segments and engagement. The dashboard renders these differently:
- A wicket or goal becomes a marker on the timeline. A segment change becomes a
segment_startmarker plus a new bar on the segment-retention chart. - Score progression matters in sports. It doesn’t in shows — what matters is which segments held the audience and which lost them.
- Audience reactions are passive in sports. In shows they’re active — viewers vote, react, donate, super-chat. We capture those as first-class engagement events, not just generic markers.
Concept: the active show context
Calling startLiveShow(ctx) on the SDK installs a show context that the SDK stamps onto every event you emit until you call stopLiveShow(). This works just like the live-match context but with show-flavored fields — and it auto-stamps the current live_segment and live_segment_index so retention queries can correlate every event with which segment was on screen.
type LiveShowContext = {
eventId: string; // = the match_id you registered + content_id you send
segment?: string; // free-form segment name e.g. 'Best Actor'
segmentIndex?: number; // zero-based ordering, useful for sorting
getLatencyMs?: () => number | undefined; // sampled per event
};Concert example
Concerts are mostly performance-driven. Use segments to mark each artist or set, and reactions to gauge audience response in real-time. Donations are common for ticketed virtual concerts.
import Tapemetric from '@tapemetric/analytics';
const tm = Tapemetric.init({ apiKey: 'tm_live_yourkey' });
// Viewer joins
tm.startLiveShow({
eventId: 'arijit_singh_live_2026',
segment: 'pre_show',
segmentIndex: 0,
getLatencyMs: () => Math.round((hls.liveSyncPosition - video.currentTime) * 1000),
});
tm.trackPlayStart({
contentId: 'arijit_singh_live_2026',
contentType: 'live',
});
// Set 1 begins — fires segment_start marker, updates auto-stamp
tm.setSegment('Set 1 — Tum Hi Ho medley', 1);
// Audience reactions
tm.trackReaction('heart');
tm.trackReaction('fire');
// A super-chat tip during the encore
tm.setSegment('Encore — Channa Mereya', 5);
tm.trackDonation(500, { display_name: 'Riya', message_length: 24 });
tm.stopLiveShow();Awards show example
tm.startLiveShow({
eventId: 'filmfare_2026',
segment: 'red_carpet',
segmentIndex: 0,
});
tm.trackPlayStart({ contentId: 'filmfare_2026', contentType: 'live' });
// Hosts come on
tm.setSegment('Opening monologue', 1);
// People's choice voting opens
tm.trackShowMarker('vote_open', 'Best film — public vote');
tm.trackPoll('best_film_2026', 'Animal');
tm.trackPoll('best_film_2026', 'Jawan');
tm.trackShowMarker('vote_close', 'Best film — public vote');
// Best Actor segment
tm.setSegment('Best Actor', 4);
tm.trackShowMarker('winner_announced', 'Best Actor — Shah Rukh Khan');
tm.trackReaction('clap');
// In Memoriam
tm.setSegment('In Memoriam', 8);
tm.stopLiveShow();Talk show with live polls
tm.startLiveShow({
eventId: 'koffee_with_karan_s8e12',
segment: 'opening',
segmentIndex: 0,
});
// Move to Rapid Fire round
tm.setSegment('Rapid Fire', 3);
// Chat metadata only — never the message body
tm.trackChatMessage({ length: 42, has_emoji: true });
// Audience poll embedded in player
tm.trackPoll('best_guest_appearance', 'Deepika Padukone');
// Vote for next week's guest
tm.trackVote('Ranveer Singh', { context: 'next_week_guest' });
tm.stopLiveShow();iOS
import TapemetricAnalytics
Tapemetric.shared.startLiveShow(
eventId: "filmfare_2026",
segment: "red_carpet",
segmentIndex: 0,
getLatencyMs: { [weak self] in
guard let p = self?.player else { return nil }
return Int(p.liveLatencySeconds * 1000)
}
)
Tapemetric.shared.setSegment("Best Actor", index: 4)
Tapemetric.shared.trackShowMarker(
kind: "winner_announced",
label: "Best Actor — Shah Rukh Khan"
)
Tapemetric.shared.trackReaction(kind: "clap")
Tapemetric.shared.trackDonation(amountInr: 500.0)
Tapemetric.shared.stopLiveShow()Android
import com.tapemetric.analytics.Tapemetric
Tapemetric.startLiveShow(
eventId = "koffee_with_karan_s8e12",
segment = "opening",
segmentIndex = 0
)
Tapemetric.setSegment("Rapid Fire", index = 3)
Tapemetric.trackPoll("best_guest_appearance", "Deepika Padukone")
Tapemetric.trackReaction("fire")
Tapemetric.trackChatMessage(mapOf("length" to 42, "has_emoji" to true))
Tapemetric.stopLiveShow()Engagement event reference
| Method | Event type | Use it for |
|---|---|---|
trackVote(option) | vote | Single-pick votes — best song, fan favourite, next-week guest |
trackPoll(pollId, choice) | poll_response | Multi-question polls during a show |
trackReaction(kind) | reaction | Emoji reactions during performances |
trackChatMessage(props) | chat_message | Chat metadata only (length, has_emoji) — not the body |
trackDonation(amount) | donation | Tips / super-chat with INR amount |
trackShowMarker(kind, label) | live_marker | Custom moments — winner announced, encore, host change |
trackChatMessage. Send length and emoji presence as properties; the message text has no analytics value and creates a privacy problem.Segment retention algorithm
The retention chart is computed by collecting all distinct anonymous_id values per segment, then taking the set intersection with the previous segment to compute "held_from_previous". Hold rate is intersection size / previous segment size. This is more meaningful than a sliding-window concurrency view because it captures viewer-level continuity — a viewer who left at the In Memoriam segment and came back for Best Picture counts as having dropped, not held.
Segments endpoint
GET /v1/live/matches/{id}/segments
Authorization: Bearer eyJ...
200 OK
{
"match_id": "filmfare_2026",
"title": "Filmfare Awards 2026",
"kind": "awards",
"segments": [
{ "index": 0, "label": "red_carpet",
"viewers": 250000, "held_from_previous": null, "hold_rate": null },
{ "index": 1, "label": "Opening monologue",
"viewers": 410000, "held_from_previous": 230000, "hold_rate": 0.92 },
{ "index": 4, "label": "Best Actor",
"viewers": 580000, "held_from_previous": 380000, "hold_rate": 0.95 },
{ "index": 8, "label": "In Memoriam",
"viewers": 380000, "held_from_previous": 320000, "hold_rate": 0.65 }
]
}Engagement endpoint
GET /v1/live/matches/{id}/engagement
Authorization: Bearer eyJ...
200 OK
{
"match_id": "filmfare_2026",
"title": "Filmfare Awards 2026",
"kind": "awards",
"engagement": {
"vote": { "count": 47210, "unique_users": 32104 },
"poll_response": { "count": 18933, "unique_users": 14889 },
"reaction": { "count": 142550, "unique_users": 38120 },
"chat_message": { "count": 23005, "unique_users": 9180 },
"donation": { "count": 412, "unique_users": 387 }
}
}Operational best practices
- Plan segments before broadcast. A rough rundown with 10–20 segments works well. Too few and the retention curve is uninformative; too many and the dashboard gets noisy.
- Use stable segment names.
'Best Actor'not'Best Actor (40 min in)'. The dashboard groups by exact label. - Throttle reactions. If your UI lets viewers tap a heart 10 times per second, throttle to one per second on the client. Otherwise a hundred-thousand viewer concert generates a million events per second of reactions.
- Pair vote_open / vote_close markers with the actual voting window so you can compute participation rate (votes / unique viewers during the window).
- For watch parties, set
kind: 'watch_party'and use segments for the underlying content’s natural breaks (episode 1, episode 2). The host’s commentary sits in'host_change'markers.
When to use match vs show kind
If the event has teams and a score, use kind: 'match'. If it has segments and audience interaction, use kind: 'show' (or one of the more specific kinds). For hybrid events — say a cricket exhibition with celebrity halftime performances — pick whichever metric matters more for your business and use markers/segments to capture the rest. The dashboard renders one or the other; it doesn’t switch mid-event.