Skip to content

Show live audience activity

Use this when many users should see the same live activity state: who is present, how often an action is happening, or the current intensity of a continuous signal.

What you implement: clients submit small telemetry actions and poll one aggregate context for display. TalkLabs maintains the synchronized counters, gauges, presence windows, and normalized intensity values.

ExperienceSignalClient writesUI reads
“People here now”PresencePeriodic heartbeat per userActive user count
Reaction pulse or applauseMetricIncrement on each actionWindow value, total, intensity
Sound or interaction meterGaugeNormalized sample from 0 to 1Average, active contributors, intensity
Mobile client with several signalsBatchBuffer mixed operations for 1–2 secondsSame context aggregate

contextid is a customer-defined ID. Use a value your product already knows, such as live-event-chat, so chat and telemetry screens can address the same experience without another lookup.

TaskOperation
Mark a user presentRecord presence heartbeat
Record an actionIncrement a counter
Record a continuous valueRecord a gauge sample
Flush several actionsSubmit a telemetry batch
Render synchronized stateRead the live meter
live-activity.js
const origin = 'https://api.sportstalk247.com';
const contextId = 'live-event-chat';
const headers = {
'x-api-token': API_TOKEN,
'Content-Type': 'application/json',
};
async function post(path, body) {
const response = await fetch(
`${origin}/api/v3/${APP_ID}/telemetry/contexts/${contextId}/${path}`,
{ method: 'POST', headers, body: JSON.stringify(body) },
);
if (!response.ok) throw new Error(`telemetry failed: ${response.status}`);
}
export function heartbeat() {
return post('presence/viewers/heartbeat', { userId: 'u-8842' });
}
export function react() {
return post('metrics/reaction.primary/increment', {
userId: 'u-8842',
by: 1,
});
}

Send presence heartbeats while the experience is visible. Stop when the user leaves; the active count falls as the presence window expires.

export async function readActivity() {
const response = await fetch(
`${origin}/api/v3/${APP_ID}/telemetry/contexts/${contextId}`,
{ headers },
);
if (!response.ok) throw new Error(`meter failed: ${response.status}`);
const { data } = await response.json();
activityStore.replace(data);
}

Bind each reading’s normalized intensity to animation scale, opacity, or meter fill. Use the raw fields when the UI needs an exact count or average.

Buffer increments, samples, and heartbeats locally, then send them together:

await post('batch', {
userId: 'u-8842',
ops: [
{ op: 'heartbeat', key: 'viewers' },
{ op: 'increment', key: 'reaction.primary', by: 3 },
{ op: 'sample', key: 'interaction.level', value: 0.72 },
],
});

The batch endpoint reports invalid items without discarding valid items in the same request. Inspect per-item results before clearing a retry buffer.

  • Label exact counts differently from normalized intensity.
  • Reduce motion when the user requests it.
  • Pause visual polling in hidden tabs when a stale display is acceptable.
  • Stop microphone or sensor sampling immediately when the feature is inactive.
  • Use stable customer-defined context and signal keys.
  • Buffer rapid actions and flush them with the batch operation.