Skip to content
All fingerprint checksEnvironment & locale

speech synthesis voices (host OS speech engine)

Check id speech-voices

What an ordinary browser yields

Contextual only: a voice list is reported if the host speech engine exposes one.

What a detector infers

speechSynthesis.getVoices() enumerates the host operating system's speech engine — a resource stealth stacks essentially never touch, which makes it a useful window onto the real environment underneath a configured browser. The check records how many voices are present, how many distinct languages they span, and which one is marked default. It is deliberately informational and contributes nothing to the score: a non-empty list reports true, and an empty list reports N/A rather than a failure. The reason is that emptiness has too many innocent causes to attribute — Linux hosts without speech-dispatcher, VDI sessions, Windows N editions, offline machines, or simply voices that haven't finished loading at probe time — so treating it as evidence would produce false positives on ordinary desktops.

How to resolve it

Nothing to fix — this cell is contextual and unscored. Read it as a description of the host: the voice set and its languages reflect the real OS speech engine, so compare it against the platform and locale you intend to present. An empty list is not a defect and is not counted against the result.

What is speech synthesis fingerprinting?

Read in the wild by

PerimeterX / HUMAN

Enumerates speechSynthesis.getVoices(), re-reading the list via onvoiceschanged when it comes back empty, and hashes each voice's voiceURI, name, lang, localService and default into two separate payload fields: one covering every voice, and a second that excludes voices whose name contains "Google" or "Microsoft" — tagged browser and device respectively, separating browser-bundled voices from the host OS's own. A flag-gated routine additionally cross-checks the list against UA-CH client hints: when the brand list claims Google Chrome and the form factor is desktop but no voice name matches /google/i, it injects a hidden iframe probe pointing at an additional PerimeterX-controlled endpoint.

PerimeterX collector main.min.js (PX_RE corpus, stample/ifood/source/main.min.js; confirmed PX by _pxAppId, _pxJsClientSrc, regex "perimeterx\.net|a\.pxi\.pub", PX##### codes). Obfuscated strings resolve through the bundle's basE91 table (hP, 1152 entries) via `function hQ(t){return void 0===hO[t]?hO[t]=hM(hP[t]):hO[t]}`. COLLECTION: `qI(){try{pn=hS.speechSynthesis.getVoices(),hS[hQ(195)][hQ(196)]=function(){(!pn||pn&&0===pn.length)&&(pn=hS[hQ(195)].getVoices())}}catch(t){}}` with hQ(195)='speechSynthesis', hQ(196)='onvoiceschanged'. HASHING: `DW(){if(!qE())return{browser:iR(pd),device:iR(pd)};for(var t="",n="",e=0;e<pn.length;e++){var r=pn[e];n+=r.voiceURI+r[hQ(158)]+r.lang+r.localService+r[hQ(1009)],r.name&&-1===r.name[hQ(240)](DK)&&-1===r.name.indexOf(DL)&&(t+=r.name)}return{browser:iR(n),device:iR(t)}}` with hQ(158)='name', hQ(1009)='default', hQ(240)='indexOf', DK=jb("R29vZ2xl")='Google', DL=jb("TWljcm9zb2Z0")='Microsoft'; emitted as payload fields `var r=DW();n[hQ(1008)]=r.browser,n["BFAxGkEwMS4="]=r.device` (hQ(1008)='ICxVJmVMVRE='). UA-CH GATE (`Gw()`, invoked flag-gated as `nf(mU[lh])&&Gw()`): `hU[hQ(191)]&&hU.userAgentData[hQ(1066)]&&hS[hQ(195)]&&hS[hQ(195)].getVoices&&hU.userAgentData[hQ(1066)]([hQ(1067),"formFactor",hQ(1068)]).then(...n[4]=n.a&&n.a[hQ(781)]((function(t){return new RegExp(hQ(1069),"i").test(t.brand)})),...n[61]=n[5]&&n[5].some((function(){...return new RegExp("desktop","i")[hQ(236)](t[0])})),n[-34]=function(){...t(hS.speechSynthesis.getVoices().some((function(t){return new RegExp(hQ(1070),"i")[hQ(236)](t.name)})));hS.speechSynthesis.getVoices().length>0?e():hS.speechSynthesis.addEventListener(hQ(1071),e,((n={})[hQ(220)]=!0,n))},n[-34]().then((function(t){if(n[4]&&n[61]&&!t){var e=jb("aHR0cHM6Ly9jbGllbnQud3JhLWFwaS5uZXQ="),r=jb("SFlVMjMyL2lmcmFtZS5odG1s"),...i[hQ(1073)]=h,i.style.display="none"` — hQ(191)='userAgentData', hQ(1066)='getHighEntropyValues', hQ(1067)='brands', hQ(1068)='formFactors', hQ(781)='some' (NOT 'filter'), hQ(1069)='google chrome', hQ(1070)='google', hQ(1071)='voiceschanged', hQ(220)='once', hQ(236)='test', hQ(1073)='src'; base64 endpoints decode to 'https://client.wra-api.net' + 'HYU232/iframe.html'. Same construct in stample/walmart/source/main.min.js (`Am=fT("R29vZ2xl"),An=fT("TWljcm9zb2Z0")`; `nw(){try{mb=eM.speechSynthesis.getVoices(),eM.speechSynthesis.onvoiceschanged=...`) and stample/totalwine/source/main.min.js (`Ed=jw("R29vZ2xl"),Ee=jw("TWljcm9zb2Z0")`).

CreepJS

CreepJS, a research demo rather than a production detector, enumerates speechSynthesis.getVoices() and records the distinct voice languages and the default local voice (filtered on localService), a read on the host OS speech engine.

src/speech/index.ts: `const data = speechSynthesis.getVoices()`, `const languages = [...new Set(dataUnique.map((x) => x.lang))]`, `const defaultLocalVoices = dataUnique.filter((x) => x.default && x.localService)`

Kasada

Enumerates the speech-synthesis voice list.

speech_synthesis_voices

ips.js 427-probe teardown (emro.cat, Apr 2026)

Every attribution traces to a published artifact. See the sources and their limits.

Nearby checks in Environment & locale

See all 23 checks in Environment & locale
Who builds this test

Clearcote is a browser built for fingerprint coherence

It is a Chromium fork, maintained by the same people who wrote this reference. It ships as a compiled browser rather than as a stealth script injected into someone else's — which is a description of how it is built, and is not an argument about how it behaves on this check.

This audit takes no position on how Clearcote scores on Environment & locale checks, on this one, or anywhere else. It has no baseline corpus of other people's fingerprints to rank you against and no vendor scoreboard — nearly every check is self-referential, asking one browser the same question through two independent APIs and reporting whether both answers can be true at once. It runs identically on any browser, including ours. Run it on yours and read the result yourself.

See the other checks in Environment & locale — the family speech-voices belongs to.