| Component name | speech_voices |
|---|---|
| In the visitorId | no (reported, kept out of both ids) |
| structuredId group | none |
| Hashing | always a digest, except the plain value none when nothing is found |
| Excluded under | no browser class |
| Inner timeout | 1000 ms |
| Added in | signalsVersion 1 |
Computers and phones come with voices for reading text aloud, and which voices are installed depends on the operating system, its language packs and the browser. WhiskerJS asks the browser for that list and keeps a short hash of it. Nothing is ever spoken. Some browsers deliver the list late, so it is reported but kept out of the identifiers.
What it reads
speechSynthesis.getVoices(): for each voice its name, lang, localService and default flags.
How the technique works
- Without
speechSynthesis.getVoicesthe component isunavailable/no_api. - The list is read. If it is empty and the browser supports events, WhiskerJS listens for
voiceschangedand waits up to 600 ms, whichever comes first, then reads the list again and removes the listener. - Each voice becomes
name|lang|local|default(the flags as1or0); the entries are sorted and joined with;. - The value is always hashed; an empty list gives the plain value
none.
Why it helps
Voice lists differ a lot between operating systems, system languages and browsers (some browsers add network voices of their own, marked not local). Two devices with the same screen and fonts can still differ here.
Your server can use it as context next to the stable ids, for example to see that two results with the same visitorId also have the same voices, or to notice a minimal environment with no voices at all.
Stability
Medium: some browsers fill the list only after a delay, so one run can see none and the next the full list. Installing a language pack or voice changes it too. That is why it is outside both ids.
Browser support and what blocks it
Most current browsers, including Chrome, Edge, Safari, iOS browsers and Firefox, have speechSynthesis. Headless and minimal builds often have no voices and report none. No browser class excludes it.
Privacy
Installed voices can hint at the system languages a user has added. WhiskerJS reports only a digest and produces no sound. Leave it out with collect({ exclude: ['speech_voices'] }) if you do not need it; no id changes.
How WhiskerJS handles noise and failures
| Status | ruleId | When |
|---|---|---|
ok |
a digest, or plain none |
|
unavailable |
no_api |
no speechSynthesis.getVoices |
unavailable |
no_digest |
a non-empty list without crypto.subtle |
blocked |
threw, security_error |
reading the voices throws |
excluded |
caller_excluded |
you excluded it |
timed_out |
timeout, aborted |
budget or abort |
The 600 ms wait sits inside the 1000 ms inner timeout.
Example value
In the container test, headless Chromium and Firefox reported none and headless WebKit reported the digest 89cde6c218596206e4119d0f5cf15c09.
Questions and answers
Does WhiskerJS make the browser speak?
No. It only calls speechSynthesis.getVoices() to read the list. No utterance is created and nothing is spoken.
Why is the list sometimes empty?
Some browsers load voices asynchronously. WhiskerJS waits up to 600 ms for the voiceschanged event; if the list is still empty it reports the plain value none.