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 up to VOICES_TIMEOUT_MS = 500 ms. Voices are sorted by name using localeCompare before serialisation — ensuring hash determinism regardless of the iteration order returned by different browsers. Each voice becomes the string name:lang:localService, and all voice strings are joined with the pipe delimiter before hashing via xxHash64.
Confidence rules
| Confidence | Trigger |
|---|---|
| normal | At least one voice obtained within 500 ms |
| absent | speechSynthesis absent, getVoices() always empty, or voiceschanged never fires within 500 ms |
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.

