Description
SpeechRecognition.available({ langs: ["en-US"], processLocally: true }) returns "downloading" but nothing actually downloads. SpeechRecognition.install(...) returns a promise that never resolves. No SODA or Speech Recognition components show up in brave://components. The API surface is exposed but the SODA component fetcher does not seem wired up
Steps to reproduce
- Open DevTools console on any https page
- Run
await SpeechRecognition.available({ langs: ["en-US"], processLocally: true })
- Run
await SpeechRecognition.install({ langs: ["en-US"], processLocally: true })
- Open
brave://components in another tab and search for SODA or Speech Recognition
Actual result
- Step 2 returns
"downloading" instantly
- Step 3 returns a
Promise { <pending> } that never resolves
- Step 4 shows no SODA or Speech Recognition entries among the ~24 components
- No matching network activity at the OS level
- Polling
available() every 2s for 10 minutes keeps returning "downloading":
async function waitForLang(lang, { intervalMs = 2000, timeoutMs = 10 * 60_000 } = {}) {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
const s = await SpeechRecognition.available({ langs: [lang], processLocally: true });
if (s === "available") return true;
if (s === "unavailable") return false;
await new Promise(r => setTimeout(r, intervalMs));
}
return false;
}
await waitForLang("en-US"); // → false after 10 min, status stays "downloading" the whole time
Expected result
One of two reasonable outcomes:
install() actually triggers the SODA component fetch (whether proxied through go-updater.brave.com or via a bypass), resolves within reasonable time, the component appears in brave://components, and available() flips to "available" (matches Chrome stable behavior)
- If Brave has not wired up on-device speech recognition yet,
available() should return "unavailable" so feature detection works correctly
The current state is the worst of both worlds: feature detection passes, but the install path is a dead end. There is no client-side way to distinguish "actually downloading a large model" from "silently stuck" short of an arbitrary timeout
Reproduces how often
Easily reproduced
Brave version (brave://version info)
- Brave: 1.89.145 Chromium: 147.0.7727.137 (Official Build) (arm64)
- Revision: 888db4a4613788705fd9850bc0a36cd253fa2260
- OS: macOS Version 26.3 (Build 25D125)
Channel information
Reproducibility
Miscellaneous information
Root cause appears to be that Brave disables the Live Caption / on-device speech support layer in the build (which is what hosts SODA in Chromium) while leaving the Blink Web Speech API surface untouched. So SpeechRecognition, available, install, and processLocally are all reachable from JS, feature detection passes, but no install path can complete because the backing component pipeline is gated off at the C++ level.
Context from #3725: @iefremov mentioned that the plan was to switch to on-device recognition once Chromium 139 shipped the relevant Web Speech improvements which happened in August 2025 with Chrome 139. About nine months later, the API surface is reachable in Brave stable but the backing stack is disabled, producing the half-exposed state described above
Code evidence from brave-core (asked Copilot to search the repo with the diagnosis as a hypothesis):
- Live Caption (the Chromium subsystem that hosts SODA) is hard-disabled
chromium_src/components/live_caption/caption_util.cc forces the support check to false:
bool IsLiveCaptionFeatureSupported() {
return false;
}
https://github.com/brave/brave-core/blob/44ee9ede9332d3289c848b342684765effa2b94b/chromium_src/components/live_caption/caption_util.cc#L14-L18
kLiveCaption is set to disabled-by-default in chromium_src/media/base/media_switches.cc:
OVERRIDE_FEATURE_DEFAULT_STATES({{
{kLiveCaption, base::FEATURE_DISABLED_BY_DEFAULT},
{kEnableTabMuting, base::FEATURE_ENABLED_BY_DEFAULT},
}});
https://github.com/brave/brave-core/blob/44ee9ede9332d3289c848b342684765effa2b94b/chromium_src/media/base/media_switches.cc#L10-L15
- Settings UI for captions is explicitly removed in
browser/resources/settings/br/a11y_page.ts, which explains why brave://settings/?search=live caption is empty
https://github.com/brave/brave-core/blob/44ee9ede9332d3289c848b342684765effa2b94b/browser/resources/settings/br/a11y_page.ts#L10-L29
Smallest possible fix to help web developers right now, even if full SODA support is not on the roadmap: make available({ processLocally: true }) return "unavailable" while the backing layer is disabled, so feature detection can route around the API on Brave instead of hanging client code
Description
SpeechRecognition.available({ langs: ["en-US"], processLocally: true })returns"downloading"but nothing actually downloads.SpeechRecognition.install(...)returns a promise that never resolves. No SODA or Speech Recognition components show up inbrave://components. The API surface is exposed but the SODA component fetcher does not seem wired upSteps to reproduce
await SpeechRecognition.available({ langs: ["en-US"], processLocally: true })await SpeechRecognition.install({ langs: ["en-US"], processLocally: true })brave://componentsin another tab and search for SODA or Speech RecognitionActual result
"downloading"instantlyPromise { <pending> }that never resolvesavailable()every 2s for 10 minutes keeps returning"downloading":Expected result
One of two reasonable outcomes:
install()actually triggers the SODA component fetch (whether proxied throughgo-updater.brave.comor via a bypass), resolves within reasonable time, the component appears inbrave://components, andavailable()flips to"available"(matches Chrome stable behavior)available()should return"unavailable"so feature detection works correctlyThe current state is the worst of both worlds: feature detection passes, but the install path is a dead end. There is no client-side way to distinguish "actually downloading a large model" from "silently stuck" short of an arbitrary timeout
Reproduces how often
Easily reproduced
Brave version (brave://version info)
Channel information
Reproducibility
Miscellaneous information
Root cause appears to be that Brave disables the Live Caption / on-device speech support layer in the build (which is what hosts SODA in Chromium) while leaving the Blink Web Speech API surface untouched. So
SpeechRecognition,available,install, andprocessLocallyare all reachable from JS, feature detection passes, but no install path can complete because the backing component pipeline is gated off at the C++ level.Context from #3725: @iefremov mentioned that the plan was to switch to on-device recognition once Chromium 139 shipped the relevant Web Speech improvements which happened in August 2025 with Chrome 139. About nine months later, the API surface is reachable in Brave stable but the backing stack is disabled, producing the half-exposed state described above
Code evidence from brave-core (asked Copilot to search the repo with the diagnosis as a hypothesis):
chromium_src/components/live_caption/caption_util.ccforces the support check tofalse:https://github.com/brave/brave-core/blob/44ee9ede9332d3289c848b342684765effa2b94b/chromium_src/components/live_caption/caption_util.cc#L14-L18
kLiveCaptionis set to disabled-by-default inchromium_src/media/base/media_switches.cc:https://github.com/brave/brave-core/blob/44ee9ede9332d3289c848b342684765effa2b94b/chromium_src/media/base/media_switches.cc#L10-L15
browser/resources/settings/br/a11y_page.ts, which explains whybrave://settings/?search=live captionis emptyhttps://github.com/brave/brave-core/blob/44ee9ede9332d3289c848b342684765effa2b94b/browser/resources/settings/br/a11y_page.ts#L10-L29
Smallest possible fix to help web developers right now, even if full SODA support is not on the roadmap: make
available({ processLocally: true })return"unavailable"while the backing layer is disabled, so feature detection can route around the API on Brave instead of hanging client code