speechSynthesis.getVoices() returned 199 voices in Google Chrome 153 on our Mac and 180 in every unbranded Chromium build of the same version, ours included. The 180 are macOS's own voices and matched exactly. The other 19 are Google's network voices, Google US English, Google Deutsch, Google 日本語 and sixteen more, and only Google's branded build has them. A page reads the whole list in one call with no permission, so a browser that says it is Chrome and lists 180 voices has told the page what it really is.

Where the nineteen come from

The extra voices are not in Chromium's source as voices. They arrive from a component extension that is compiled in only when Chromium is built as Google Chrome, under the is_chrome_branded build flag. Every build without that flag, which means every Chromium anyone else compiles, lists only what the operating system provides.

On a Mac those 180 voices are Apple's: Albert, Alice, Alva, Amélie and the rest. Chrome adds its own after them: Google UK English Female, Google UK English Male, Google español, Google français, Google italiano, Google polski, Google русский, Google 한국의 and others. They are network voices, so their localService reads false, which no Apple voice does.

The list also has a timing quirk that matters for anyone measuring it. Chrome fills it in asynchronously. A call made as the page loads can return an empty array, and the list appears after the voiceschanged event fires.

Every profile is drawn from one real computer, not from separate listsIn Mosaic: Machines that exist. GPU, screen, CPU and fonts from one real device. Free holds 3 profiles. Pro is $49 a month for any number.

How it works: Machines that exist

How it stayed hidden

We were chasing a verdict at the time. IPHey scored our profiles Unreliable, 90 out of 100, with one signal, an inconsistent browser fingerprint, while a branded Chrome on the same Mac and the same exit, minutes apart, scored Trustworthy at 100. So we compared our engine with that Chrome, surface by surface.

Almost everything matched. Twenty-two fields that depend on branding, including Widevine robustness levels, proprietary codecs, window.chrome, plugins and brands, were identical once two launch switches were in place. Twenty-eight deeper fields were identical in both the page and an isolated world: canvas, audio, WebGL parameters and extensions, font metrics, getClientRects to twelve decimals, Intl, property order, descriptors, native function sources, media queries, scrollbar width. All fourteen request headers matched, in order and case. The TLS ClientHello and the HTTP/2 settings had no differing fields.

The voice list was in that comparison, and that is the uncomfortable part. Our script read it synchronously, before it filled in. It got 0 from Chrome and 0 from our engine, compared 0 with 0, and then carried an exemption for the row: "loads asynchronously; races the probe". Measured, found equal and excused, three ways of not looking at one field. The probe now waits for voiceschanged, the exemption is gone, and the first honest read gave 199 against 180.

The control that should have come first

Before the voices turned up we had spent four days assuming something in our patch series caused the IPHey signal. Google's own unpatched Chrome for Testing 153.0.8010.36 earns the identical verdict. One ninety-second run rules out every patch at once. We now ask whether a stock Chromium already does something before we look for the cause in our own work.

That control also says something about the voices. Chrome for Testing is unbranded, so it lists 180 too. The voice gap is not about our engine at all; it is about Chromium against Chrome, and any browser built from Chromium source that claims to be Chrome carries it unless someone puts the voices back. An anti-detect browser built on Chromium has the same gap until it restores them.

Putting the voices back

Patch 0145-branded-tts-voices adds the 19 voices to Tesseract Chrome, our engine, at the point where the branded extension's own voices would arrive: after the platform's voices are collected and before Chrome sorts the list. So Chrome's own rules apply to them unchanged. They are ordered the way Chrome orders them, only the first voice is marked default, and Chrome's gate that hides remote voices on a cellular connection still applies. Names and languages were recorded from the branded Chrome on the same machine, in its order. We did not invent any of them, because an invented voice would be a new tell rather than a removed one.

It fixed the list. It did not, on its own, move IPHey's verdict, whose cause turned out to be somewhere else. We kept the patch anyway: a voice list that disagrees with the browser's name is a difference any page can read in one call, whatever one detector weighs. Why owning the engine is the only place a fix like this can go is in why we built our own Chromium, and how the rest of a profile is drawn is on real fingerprints.