What it measures
The set of speech synthesis voices available to the browser, identified by name, BCP-47 language tag, and whether the voice is provided by a local on-device TTS service. Different browsers and operating systems expose different voice sets, making this an effective browser and platform discriminator.
How it is collected
Many browsers do not populate speechSynthesis.getVoices() synchronously on first call. The collector attempts a synchronous read first; if the result is empty it registers a voiceschanged event listener and waits a short event-listener timeout. Voices are sorted by name using localeCompare before serialisation, ensuring hash determinism regardless of the iteration order returned by different browsers. Each voice's name, language tag, and local-service flag are combined into a single string, and all voice strings are joined before hashing via xxHash64.
Confidence rules
| Confidence | Trigger |
|---|---|
| normal | At least one voice obtained within the event-listener timeout |
| absent | speechSynthesis absent, getVoices() always empty, or voiceschanged never fires within the event-listener timeout |
Why engine-bound
Voice availability is a function of both the browser engine and the operating system. Chrome on macOS exposes macOS system voices and Google TTS voices (localService: false); Firefox exposes a different subset; Safari uses AVSpeechSynthesizer voices. The same OS with different browsers can yield a different voice list because browsers selectively expose or filter the OS voice registry. The localService flag adds entropy by differentiating cloud-only voices from on-device voices.
Last reviewed 2026-06-04

