v153 · dev trial · web api · speech
SpeechRecognitionResult Timestamps (WebSpeech API)
Two nullable attributes — audioStartTime and audioEndTime on SpeechRecognitionResult — that report the start and end of the source-audio segment a recognition result was transcribed from, in milliseconds relative to the time origin. They let applications align captions with a media timeline and measure on-device speech-recognition latency so they can fail over to a cloud backend before users notice lag.
The milestone=153 listing files this feature under “In developer trial (Behind a flag)” (verified 2026-07-29; the listing is authoritative). The spec change, WICG/speech-api PR #192, is open and unmerged, so attribute names and semantics can still change in review. There is no origin trial: the runtime flag record for WebSpeechTimestamps is status: experimental with no public: true (so there is no chrome://flags entry) and no origin_trial_feature_name. To test locally:
chrome --enable-blink-features=WebSpeechTimestamps
Feature-detect before relying on the attributes — when the flag is off, they do not exist on the interface at all:
const supported = 'audioStartTime' in SpeechRecognitionResult.prototype;
at a glance
| What it is | Two read-only nullable attributes, audioStartTime and audioEndTime, added to SpeechRecognitionResult; each reports a boundary of the audio segment that produced the result, as a DOMHighResTimeStamp in milliseconds relative to the time origin |
|---|---|
| Granularity | Per recognition result (segment-level), not per word. Chromium's speech pipeline carries per-word hypothesis-part offsets internally (TimingInformation in speech_recognition.mojom), but this feature exposes only the two segment bounds on the result object |
| Nullability | Both attributes return null when the underlying recognition engine does not provide segment timestamps — by design, so applications can detect unsupported engines rather than read a fabricated value |
| Privacy clamp | Values are quantized to 2 ms precision before reaching script (floor to a 2 ms multiple) — implemented in Chromium (FuzzTimestamp) and required by the spec PR's privacy section |
| Milestone listing | Chrome 153 — In developer trial (Behind a flag) (listing, verified 2026-07-29; the Intent to Prototype records “Target DevTrial: M153”) |
| Runtime feature | WebSpeechTimestamps (experimental, not public, no origin trial) in runtime_enabled_features.json5; implementation CL 8129198 merged 2026-07-28 |
| WPT | No dedicated tests: the wpt/speech-api directory (27 files, listed 2026-07-29) has no timestamp test and no reference to audioStartTime |
| ChromeStatus | 5811907077472256 — SpeechRecognitionResult Timestamps (WebSpeech API) (blink component Blink>Speech; tracking bug crbug.com/528037568) |
why it exists
The Web Speech API has always been a “black box” about timing: a SpeechRecognitionResult tells you what was recognized, but nothing about when in the audio it was recognized. The explainer frames two concrete problems this creates:
- Timeline association — developers cannot map transcribed text back to specific segments of the audio source, which blocks automated subtitle cue alignment and media-editing workflows that need to know where in a recording each phrase sits.
- Latency tracking & backend failover — as recognition moves on-device (SODA and related backends) to improve privacy and cut server cost, processing speed depends on the user's hardware. When a constrained device falls behind real time, caption lag accumulates silently. With no timing signal, an application cannot detect the lag or fail over to a cloud ASR backend — the user just sees stale captions in a live call.
Two segment timestamps per result answer both: caption tools get cue bounds, and latency-sensitive apps can compare audioEndTime with the event's delivery time to compute how far behind the engine is running (see measuring latency).
how it works
- The recognition engine (on-device SODA, or a remote service) processes audio and emits results tagged with timing information. In Chromium's speech pipeline the engine reports a
TimingInformationstruct per result:audio_start_time(“start time in audio time from the start of the SODA session” — the amount of audio input) andaudio_end_time(“elapsed processed audio from first frame after preamble”). - Blink constructs each
SpeechRecognitionResultwith those optional bounds (speech_recognition.ccpassesresult->audio_start_time/audio_end_timeintoSpeechRecognitionResult::Create). This applies to interim (provisional) results as well as final ones — the aggregation path inspeech_recognition.ccbuilds both from the same struct, so live-caption use cases get timestamps on interim hypotheses too. - When script reads
audioStartTime/audioEndTime, Blink returnsnullif the engine supplied no value, otherwise the value floored to a 2 ms multiple (FuzzTimestampin speech_recognition_result.cc) before it crosses the binding layer. - The application reads the bounds on each
resultevent, aligns text to its media timeline, or derives processing latency (below).
measuring latency
The explainer's flagship pattern: because both the result timestamps and Event.timeStamp are relative to the same time origin, subtracting audioEndTime from the event's timeStamp yields the processing lag between when the audio ended and when the result was delivered:
const processingLatencyMs = event.timeStamp - result.audioEndTime;
An application can watch that value and fail over to a cloud backend when it breaches a threshold (the explainer uses 1500 ms as an example). Two honest caveats: the measure includes binding/IPC delivery time, not pure engine time; and the 2 ms quantization floors both attributes, so latencies below ~2 ms are not resolvable — irrelevant in practice for a failover threshold measured in hundreds of milliseconds.
Source: explainer — Proposed Behavior & Example Usagesyntax
Proposed spec IDL (WICG/speech-api PR #192, open — quoted verbatim):
partial interface SpeechRecognitionResult {
readonly attribute DOMHighResTimeStamp? audioStartTime;
readonly attribute DOMHighResTimeStamp? audioEndTime;
};
Shipping Chromium IDL (speech_recognition_result.idl at trunk, 2026-07-29 — quoted verbatim, including the runtime gate):
[
LegacyNoInterfaceObject
] interface SpeechRecognitionResult {
readonly attribute unsigned long length;
getter SpeechRecognitionAlternative item(unsigned long index);
readonly attribute boolean isFinal;
[RuntimeEnabled=WebSpeechTimestamps] readonly attribute DOMHighResTimeStamp? audioStartTime;
[RuntimeEnabled=WebSpeechTimestamps] readonly attribute DOMHighResTimeStamp? audioEndTime;
};
The two attributes are the entire surface: no new methods, no options, no events. SpeechRecognitionAlternative (transcript, confidence) is unchanged — timestamps live on the result, not on each alternative hypothesis.
reference
SpeechRecognitionResult.audioStartTime— start of the audio segment for this result (nullable, 2 ms-quantized)SpeechRecognitionResult.audioEndTime— end of the audio segment; the input to the latency computation
examples
Live captioning with latency-based failover (adapted from the explainer):
const recognition = new SpeechRecognition();
recognition.continuous = true;
recognition.interimResults = true;
recognition.onresult = (event) => {
const result = event.results[event.resultIndex];
if (result.audioEndTime !== null) {
// How far behind the audio is the engine?
const processingLatencyMs = event.timeStamp - result.audioEndTime;
if (processingLatencyMs > 1500) {
console.warn(`ASR lag detected (${processingLatencyMs}ms) — failing over to cloud`);
switchToCloudBackend();
}
}
};
recognition.start();
Aligning a caption to a media timeline (the audio source is a <video> element routed into recognition):
recognition.onresult = (event) => {
for (let i = event.resultIndex; i < event.results.length; i++) {
const result = event.results[i];
if (result.isFinal && result.audioStartTime !== null) {
cues.push({
start: result.audioStartTime / 1000, // ms → s for VTTCue
end: result.audioEndTime / 1000,
text: result[0].transcript,
});
}
}
};
browser compatibility
Interim table. There is no BCD key for audioStartTime/audioEndTime (the file covers only length, item(), isFinal, verified 2026-07-29) and a webstatus.dev query returns no feature for the attributes. Rows below are compiled from the linked primary sources, not from BCD.
| Browser | Support | Evidence |
|---|---|---|
| Chrome / Edge (Chromium) | Developer trial 153 — behind WebSpeechTimestamps (experimental, not public) | milestone=153 listing; flag record; CL 8129198 |
| Firefox | No signal (and no SpeechRecognitionResult support at all per BCD) | ChromeStatus API record vendor views; BCD |
| Safari | No signal (supports the prefixed host API since 14.1 per BCD, not these attributes) | ChromeStatus API record vendor views; BCD |
For reference, BCD records the host interface SpeechRecognitionResult as Chrome 33+, Safari 14.1+ (prefixed lineage), Firefox never — the attributes above ship independently of that baseline.
security and privacy
- Fingerprinting via precise timing — sub-millisecond timing enables hardware profiling (CPU speed, thermal throttling, system load) and cross-origin tracking. The spec PR adds a mitigation clause: user agents MUST apply timestamp fuzzing and precision reduction to both attributes before exposing them to script, “e.g. by rounding to 2ms precision”.
- Chromium implements exactly that —
FuzzTimestampfloors each value to a multiple of 2 ms (kFuzzInterval = base::Milliseconds(2)) inside the getter, so unquantized values never reach the binding layer (speech_recognition_result.cc). - No new capability — the attributes expose timing metadata about audio the page already routed into recognition under the existing Web Speech API permission model (microphone permission / MediaStreamTrack input); they grant no access to new audio, engines, or devices.
- Null, not zero, for unsupported engines — an engine that cannot supply timestamps yields
null, so the API does not leak a bogus “zero latency” signal that applications might misread as healthy.
The quantization in concrete terms:
// Engine reports: audio_start_time = 1234.5678 ms
// Attribute reads: result.audioStartTime === 1234 (floored to a 2 ms multiple)
// Engine absent: result.audioStartTime === null (no fuzzing applied)
Source: PR #192 privacy clause; explainer — Security and Privacy Considerations; speech_recognition_result.cc
specifications
| Document | Status |
|---|---|
| WICG/speech-api PR #192 — Update SpeechRecognitionResult with audio timing attributes | Open, unmerged (fetched 2026-07-29) — adds the two attributes plus the mandatory fuzzing clause; the normative text this page quotes |
| Explainer — SpeechRecognitionResult timestamps (alanding@google.com, evliu@google.com) | Merged into WICG/speech-api 2026-07-14/15; the motivation, usage pattern, and alternatives-considered record. (The Intent to Prototype links the same files on the WebAudio/web-speech-api fork.) |
| Web Speech API (community report / ED) | Host specification; does not yet contain the attributes (0 occurrences of audioStartTime, verified 2026-07-29) — PR #192 must merge first |
| blink-dev — Intent to Prototype: SpeechRecognitionResult Timestamps | Posted 2026-07-16 by Alan Ding; records “Target DevTrial: M153”, tracking bug 528037568; no review replies on the thread at fetch time |
see also
- Chrome Platform Status — SpeechRecognitionResult Timestamps (WebSpeech API) (API record)
- MDN — SpeechRecognitionResult (host interface; MDN does not cover these attributes — the page has no timestamp mention and the member URL 404s, verified 2026-07-29)
- MDN — Event.timeStamp (the other operand of the latency computation)
- Chromium CL 8129198 — the implementation (merged 2026-07-28)
- Chrome Platform Showcase — interactive demos for this feature
- webstatus.dev — speech-recognition feature (Baseline limited)