Hardware & Capabilities · Browser Tests
Speech Voices Test
List every text-to-speech voice your browser exposes, a small but distinctive fingerprint signal.
Voices load asynchronously, so the list may arrive after the first voiceschanged event rather than on the first call.
Voice list
Distinct languages
List signature
Nothing is spoken and nothing is uploaded. The list is read from your browser and shown only here, and this page never calls speak().
On this page
What the API exposes
The Web Speech API gives a page two capabilities: it can speak text, and it can ask which voices are installed. This tool only uses the second one. A single call to window.speechSynthesis.getVoices() returns an array of SpeechSynthesisVoice objects describing everything the browser can speak with.
The count, the languages and the mix of local and network voices are the interesting parts. A stock Windows laptop reports a handful of system voices, a macOS machine reports a longer set, and a phone may add several depending on which language packs the owner installed. Android varies widely in particular, because the Google speech engine downloads voices on demand rather than shipping them all up front.
No audio is involved
Reading the list does not play anything and does not need the microphone. It is a metadata read, the same kind of lookup as asking how many fonts are installed.
Where the voices come from
The list is less a browser feature than an inventory of your operating system. On Windows the voices come from the SAPI and OneCore engines, which is why they are named Microsoft David, Zira or Mark, and why extra ones appear when you add a language pack. macOS uses a different stack and ships a larger set with friendlier names such as Samantha, Alex or Daniel. Android routes through Google Speech Services, and a Linux desktop usually relies on eSpeak NG or the Speech Dispatcher stack, so its list can be short or empty.
On top of the local engines, Chromium browsers add network voices, the ones with localService set to false. These are generated on Google’s servers and streamed back as audio, which is why they need a connection and why they appear on Windows, macOS and Android alike. Firefox generally keeps to the system voices and exposes no network set of its own.
Why the list arrives late
A common trap with this API is trusting the first call. On several browsers, getVoices() returns an empty array the first time it is run, and only later does the engine finish enumerating what is installed. The signal that the list is ready is the voiceschanged event on the speechSynthesis object.
The panel handles this three ways. It reads once on load, it subscribes to voiceschanged both as an event listener and as a plain handler property, because browsers have differed over which they honour, and it reads again after 1.5 seconds as a safety net. If all three come back empty, the tool reports that rather than showing a blank list, since no voices is itself a real answer and usually means no engine is present for that browser.
Reading a voice entry
Each entry in the list carries a small set of properties worth knowing.
- name is the display name of the voice, such as Microsoft David or Google UK English Male.
- lang is a BCP-47 language tag like en-GB or de-DE, naming the language and often the region.
- localService is true when the voice runs on your device and false when the audio is generated over the network.
- default marks the voice the browser would pick for that language if a page asked for one without naming it.
- voiceURI is the identifier the page would pass to select the voice, and it is also the string most likely to differ between machines.
The distinct language chips above collapse the list down to the languages you actually have, which is easier to read than forty near-identical rows, and the list hash below them condenses the whole set into a single short value for comparison.
Why the list is a fingerprint
The point of browser fingerprinting is finding signals that are stable for one machine and rare across machines. The voice list fits both conditions uncomfortably well. It changes only when the operating system, the browser or the installed language packs change, so it stays put for months, and the exact set is not the same for any two people.
For most machines the list contributes only a few bits, because many people run a stock English install. The signal grows when the mix is unusual, for example a user with four languages, or a Linux desktop with a particular eSpeak build. It is one column, not an identity, and it matters because it joins the other columns. The font detection test measures a similar kind of list, and the fingerprint test combines both with the rest of your browser surface.
What this page does not do
The tool reads the voice list and stops there. It does not call speak(), so nothing is synthesised, no audio is played and no connection is opened for a network voice. It does not request microphone access, it does not store the list and it does not send it anywhere. The list never leaves this page.
It is worth knowing that a page can also infer voices by attempting to speak and watching for errors, so a site that wanted this signal would not need permission. Blocking speech synthesis entirely is possible in some browsers and extensions, but it will also break legitimate accessibility features such as screen readers that rely on the same API. Treat the list as one more ordinary browser property rather than a lever on its own.