Repository navigation
Add Speaker Diarization (Speaker Turn Attribution) to SpeechRecognition #226
Description
Summary
We propose extending SpeechRecognition, SpeechRecognitionOptions, and SpeechRecognitionResult to support opt-in speaker diarization—splitting recognition results at speaker turn boundaries (speakerChanged) and attributing each SpeechRecognitionResult to a session-ephemeral relative speaker label (speaker: "1", "2", ...).
(Companion explainer: explainers/speaker-diarization.md; also relates to #105, #192, #208, #213, and #216.)
Motivation
While recent updates to SpeechRecognition enable on-device processing (processLocally: true), MediaStreamTrack and AudioData (#216) inputs, and result timestamps (speechStartTime / speechEndTime, #192), the API still returns one undifferentiated stream of text. Applications cannot tell where one speaker stopped and another began, or which utterances share a speaker:
- Shared-Mic Conferencing & Interviews: Multiple participants speaking into a shared room microphone collapse into an unattributed wall of text, degrading live captions and meeting summaries.
- Accessible Multi-Speaker Subtitling (
WebVTT<v>Voice Spans &>>Markers): Captioning guidelines require marking speaker changes (>>) and identifying speakers (<v Speaker 1>), which currently cannot be automated when buildingVTTCues fromSpeechRecognitionResult. - Eliminating Duplicate In-Page Audio & Embedding Pipelines: Privacy-sensitive apps using
processLocally: true(clinical, legal, educational, enterprise) currently must capture raw PCM viagetUserMedia(), download separate tens-of-MB speaker models per origin in WebAssembly, compute voice embeddings in page script, and manually reconcile misaligned timestamps againstSpeechRecognitionResult. - Talk-Time Analytics & On-Device LLM Pipelines: Combining measured
speechEndTime - speechStartTime(Update SpeechRecognitionResult with speech timing attributes #192) with anonymous"1","2"speaker labels enables talk-time analytics and lets apps pass plain-text attributed transcripts to on-device LLMs (e.g., the Prompt API) for role inference ("clinician"vs."patient") without exposing voice data to either script or the LLM.
Proposed Solution (Web IDL)
enum SpeechRecognitionDiarization {
"none",
"speaker-turns",
"speaker-labels"
};
partial interface SpeechRecognition {
attribute SpeechRecognitionDiarization diarization;
attribute unsigned long minSpeakers;
attribute unsigned long maxSpeakers;
};
partial dictionary SpeechRecognitionOptions {
SpeechRecognitionDiarization diarization = "none";
};
partial interface SpeechRecognitionResult {
readonly attribute boolean speakerChanged;
readonly attribute DOMString? speaker;
};
// Added to SpeechRecognitionErrorCode:
// "diarization-not-supported"Example Usage
const recognition = new SpeechRecognition();
recognition.lang = 'en-US';
recognition.continuous = true;
recognition.processLocally = true;
recognition.diarization = 'speaker-labels';
recognition.maxSpeakers = 4;
recognition.onresult = (event) => {
for (let i = event.resultIndex; i < event.results.length; ++i) {
const result = event.results[i];
if (!result.isFinal) continue;
const text = result[0].transcript.trim();
const vtt = result.speaker ? `<v Speaker ${result.speaker}>${text}` : text;
captionTrack.addCue(new VTTCue(result.speechStartTime, result.speechEndTime, vtt));
}
};Proposed Algorithm & Edge-Case Rules
- Configuration & Explicit Failure:
diarization,minSpeakers, andmaxSpeakers(0= no hint) are read whenstart()is called;minSpeakers/maxSpeakersare ignored unlessdiarizationis"speaker-labels".- If
minSpeakers > 0,maxSpeakers > 0, andminSpeakers > maxSpeakers,start()throws aRangeError. - If the user agent cannot provide the requested
diarizationmode for the active language andprocessLocallysetting, the start session algorithm queues anerrorevent with"diarization-not-supported"(MUST NOT silently downgrade to"none").
- Result Splitting &
speakerChangedInvariant:- When
diarizationis not"none", the user agent ends the currentSpeechRecognitionResultat each detected speaker turn (in addition to normal pause endpointing and, when combined with What happens if the language isn't specified when using SpeechRecognition, and how to recognized multiple languages #208, detected language switches). speakerChangedisfalsefor the first result of a session (index === 0) and whenever a new result starts after a pause or language switch by the same speaker; it istrueonly when the speaker differs from the preceding result. In"speaker-labels"mode, whenever consecutive results both have non-nullspeaker,speakerChangedequalsspeaker !== previous.speaker.
- When
- Label Format,
nullSemantics & Cardinality:- In
"speaker-labels"mode, labels are canonical 1-indexed decimal strings ("1","2", ...) assigned in order of first appearance in the session and reset to"1"on each newstart()session. speakeris alwaysnullin"none"and"speaker-turns"modes. In"speaker-labels"mode,speakerMAY benullon early interim results (before ~1–2s of a new voice has been heard) or on final results too brief/ambiguous to attribute (e.g., short backchannels like"mm-hmm").- If
maxSpeakers > 0, the user agent MUST NOT emit more thanmaxSpeakersdistinct labels;minSpeakersacts as an advisory hint.
- In
- Overlapping Speech, Multi-Channel & Single-Shot (
continuous = false):- Overlapping speech is attributed to the dominant speaker in v1 (no source separation).
- Multi-channel separation is out of scope (applications with per-participant channels can split channels via Web Audio and run separate sessions).
- When
continuousisfalse, the session's single result ends at the first detected speaker turn.
- Streaming vs. Faster-Than-Realtime (
AudioData, Add explainer for speech recognition on AudioData #216) Stability:- On
isFinal === falseinterim results,speakerandspeakerChangedare provisional; onceisFinal === true, they are immutable. - For live streams (microphone /
MediaStreamTrack), user agents SHOULD balance finalization delay against attribution stability. ForAudioDatainput (Add explainer for speech recognition on AudioData #216), user agents SHOULD leverage queued lookahead (up to end-of-input) so finalized labels are globally consistent across the recording.
- On
Privacy & Security Considerations
- Strictly Ephemeral, Non-Biometric State: Labels are computed solely from the current session's audio. User agents MUST NOT expose raw speaker embeddings, MUST NOT match voices against stored OS/browser/cloud profiles (extending the existing prohibition on personalizing
MediaStreamTrackrecognition), and MUST discard all voice representations when the session ends, is aborted, or the document unloads. - Net Data Minimization: Keeping voice embeddings inside the user agent and surfacing only session-scoped
"1","2"labels avoids forcing web pages to capture raw microphone audio (getUserMedia()) and compute retainable voice embeddings in page script. - Anti-Fingerprinting: Canonical
"1","2"strings prevent engine-prefix leaks;available()reusesSpeechRecognitionAvailabilityStatuswith storage-key download masking (Add privacy and security mitigations to the Web Speech API #165); turn bounds add no timing side channel beyond Update SpeechRecognitionResult with speech timing attributes #192. - Bystander Privacy & Permissions Policy: Session-ephemeral clustering prevents cross-session tracking of secondary speakers/bystanders, and diarization inherits existing
"microphone"and"on-device-speech-recognition"('self', #165) policy gating ("service-not-allowed"/"not-allowed").
Alternatives Considered
speakeronSpeechRecognitionAlternative: Acoustic diarization describes the audio span rather than individual N-best lexical alternatives;SpeechRecognitionResultmatchesspeechStartTime/speechEndTime(Update SpeechRecognitionResult with speech timing attributes #192) andlang(What happens if the language isn't specified when using SpeechRecognition, and how to recognized multiple languages #208).- Boolean
diarizationwithout"speaker-turns":"speaker-turns"serves live captioning (>>), uses less compute, and avoids computing voice embeddings altogether. - Implying diarization from
quality: "conversation"(Add quality level hint to SpeechRecognitionOptions #186): Quality tiers describe acoustic robustness, whereas diarization alters segmentation and has distinct privacy and model-download requirements. - Transient
onspeakerchangeDOM events only: Decouples speaker attribution from cumulativeevent.results.
Open Questions for WG Discussion
-
Unified Result & Sub-Result Metadata (with Multilingual
lang, #208):- Should speaker attribution and per-result
lang(What happens if the language isn't specified when using SpeechRecognition, and how to recognized multiple languages #208) share the exact same result-splitting rule (ending aSpeechRecognitionResultwhenever either speaker or language changes, withspeakerChanged === falseacross same-speaker code-switches)? - Is result-level splitting sufficient for v1, or does fragmentation from mid-sentence code-switching / rapid interjections require moving both features together to a shared sub-result
segmentsstructure (time ranges onSpeechRecognitionResultvs. word spans onSpeechRecognitionAlternative)?
- Should speaker attribution and per-result
-
Streaming (
interimResults) vs. Offline (AudioData, #216) Label Revisions:- Is keeping
isFinal === trueresults strictly immutable sufficient for v1 (using provisional interim updates + slight finalization delay for live streams, and full lookahead clustering forAudioDatarecordings)? - If live streaming engines need post-finalization cluster merges later, which mechanism is preferable:
- (a) None in v1 (immutable
isFinal, current proposal); - (b) Non-mutating merge event (
speakerlabelsmergedwith{ from: "3", to: "1" }) that lets apps merge earlier speaker groups without mutating finalizedSpeechRecognitionResultobjects; or - (c)
onspeakerlabelchange(updatedResultIndices) updating finalized results in place?
- (a) None in v1 (immutable
- Is keeping
-
Audio Timeline Alignment (#192, #213, #216) & Media Pause/Seek:
- Turn-split
[speechStartTime, speechEndTime]bounds use the session audio timeline (0.0sat first consumed frame in Update SpeechRecognitionResult with speech timing attributes #192 / The timestamp of various events is under-specified, and not implemented per spec in shipped implementations #213, orAudioData.timestampin Add explainer for speech recognition on AudioData #216). - Per The timestamp of various events is under-specified, and not implemented per spec in shipped implementations #213, pausing or seeking an
HTMLMediaElementcaptured viacaptureStream()requires starting a newSpeechRecognitionsession—which resets session-scoped speaker labels back to"1". Is this session-reset boundary acceptable for media-element captioning (givenAudioDataAdd explainer for speech recognition on AudioData #216 handles full-file transcription without pause/seek resets)?
- Turn-split
-
Privacy Gating,
processLocally, andPermissions-Policy:- Should
"speaker-labels"requireprocessLocally = true(strongest privacy guarantee), or also allow cloud-backedSpeechRecognitionimplementations that support ephemeral diarization? - Should
"speaker-labels"be gated by a dedicatedPermissions-Policyfeature (e.g.,"speaker-diarization", default allowlist'self') and/or distinct error code ("diarization-not-allowed"vs. reusing"service-not-allowed")?
- Should
-
Naming & Constraints:
- Naming bikeshed:
diarizationvs.speakerDiarizationvs.speakerAttribution;speakervs.speakerLabel;speakerChangedvs.speakerChange. - Is treating
maxSpeakersas a hard cap andminSpeakersas a soft hint interoperable across engines?
- Naming bikeshed:
Prior Art & Multi-Engine On-Device Precedent
| System | Configuration | Speaker-Count Hints | Granularity | Label Format |
|---|---|---|---|---|
| Google Cloud Speech-to-Text | diarizationConfig (enableSpeakerDiarization) |
minSpeakerCount, maxSpeakerCount |
Word | speakerTag (int), speakerLabel (string) |
| Amazon Transcribe | ShowSpeakerLabels / ShowSpeakerLabel |
MaxSpeakerLabels |
Word / segment | spk_0, spk_1, ... |
| Azure AI Speech | ConversationTranscriber |
— | Utterance | Guest-1, Guest-2, ... |
| AssemblyAI | speaker_labels |
speakers_expected |
Utterance / word | A, B, ... |
| Deepgram | diarize=true |
— | Word | 0, 1, ... |
| W3C WebVTT | Voice spans <v Name> |
n/a | Cue span | Free-form voice string |
Published On-Device Diarization Precedent:
- Turn-to-Diarize: Online Speaker Diarization Constrained by Transformer Transducer Speaker Turn Detection (arXiv:2109.11641) — inline transducer turn tokens (
"speaker-turns") paired with compact turn embeddings ("speaker-labels"). - Highly Efficient Real-Time Streaming and Fully On-Device Speaker Diarization with Multi-Stage Clustering (arXiv:2210.13690) — bounded-cost online clustering for long local sessions.
- Who Said What? Recorder's On-Device Solution for Labeling Speakers (Google Research Blog).
Activity
alan33d commentedon Oct 27, 2026
Companion PRs:
explainers/speaker-diarization.md)index.bs)