acceptable-summer-49233
02/23/2026, 7:26 AMtall-airline-16756
02/23/2026, 7:26 AMtall-airline-16756
02/23/2026, 7:26 AMuser_input_transcribed event: each Transcript object includes start_time and end_time. When you receive a final transcript, subtract start_time from end_time to get the utterance duration.
◦ Example: on user_input_transcribed, if transcript.is_final, compute duration = transcript.end_time - transcript.start_time.
• Using STT metrics (STTMetrics): the metrics include audio_duration, which represents the length of the audio segment processed by STT. This gives you the actual audio length for that transcription request.
If you’re using VAD instead of (or in addition to) STT, you can also track VAD events (e.g., start/end of speech) and timestamp them to compute durations, though the transcript timestamps or audio_duration are the most precise.
Would you like a short code example in the language you’re using (Python/Node/etc.), or do you want help choosing between transcript timestamps vs. audio_duration for your specific use case?
Sources: Transcriber | LiveKit Documentation | STT Metrics | LiveKit Documentation | VAD Metrics | LiveKit Documentationacceptable-summer-49233
02/23/2026, 7:28 AMtall-airline-16756
02/23/2026, 7:28 AMSTART_OF_SPEECH / END_OF_SPEECH or similar event names your VAD emits).
2. On start: record a start timestamp for that user (Date.now() or performance.now()).
3. On end: compute segment duration = endTime - startTime, add to that user’s accumulated speech time, then clear the start timestamp.
4. Optionally: ignore very short segments by thresholding (e.g., < 100 ms) to filter noise.
Example (pseudo/Node.js — adapt to your VAD event API):
// Map to track current speech start and accumulated duration per user
const userSpeechState = new Map();
// Example event handler names; replace with your VAD integration events
// e.g., vad.on('start_of_speech', handler) and vad.on('end_of_speech', handler)
function onStartOfSpeech(userId) {
const now = Date.now();
const state = userSpeechState.get(userId) || { start: null, total: 0 };
// If already in speech, ignore repeated starts
if (!state.start) {
state.start = now;
userSpeechState.set(userId, state);
}
}
function onEndOfSpeech(userId) {
const now = Date.now();
const state = userSpeechState.get(userId);
if (!state || !state.start) return; // no matching start
const segmentMs = now - state.start;
// optional: ignore very short segments (noise)
const MIN_SEGMENT_MS = 100;
if (segmentMs >= MIN_SEGMENT_MS) {
state.total += segmentMs;
}
state.start = null;
userSpeechState.set(userId, state);
// convert to seconds for display
const seconds = (segmentMs / 1000).toFixed(2);
console.log(`User ${userId} spoke for ${seconds}s this segment. Total: ${(state.total/1000).toFixed(2)}s`);
}
// Example usage wiring (replace with actual VAD emitter)
vad.on('start_of_speech', ({ userId }) => onStartOfSpeech(userId));
vad.on('end_of_speech', ({ userId }) => onEndOfSpeech(userId));
// If you need the total at any time:
function getTotalSpeechSeconds(userId) {
const state = userSpeechState.get(userId) || { total: 0, start: null };
let total = state.total;
// include in-progress segment if any
if (state.start) total += (Date.now() - state.start);
return total / 1000;
}
Notes and best practices
• Use high-resolution timers if you need sub-ms accuracy (e.g., performance.now()).
• Debounce/merge short gaps: VAD sometimes produces very short silences during natural pauses. If you want continuous speech counting across short gaps, treat short silence gaps (e.g., < 200–300 ms) as still part of the same utterance.
• If you already run STT, you can cross-check with transcript `start_time`/`end_time` or STTMetrics.audio_duration to validate VAD durations.
• Track by unique user ID that your session provides so counts are per-user.
Would you like a version tailored to a specific VAD library/event shape (for example, the exact LiveKit VAD event names and payloads), and do you want code that merges short pauses automatically?