浏览器语音能力已经今非昔比。根据 Chrome Platform Status 的数据,Web Speech API 的使用量在 2025-2026 年间增长了 220%,从在线教育的语音答题到 SaaS 产品的语音指令,从无障碍辅助工具到 AI 对话界面的语音输入层,语音交互正在成为现代 Web 应用的标配能力。如果你还在用第三方 SDK(如科大讯飞、百度语音)来实现浏览器端的语音功能,这篇文章会让你重新审视——浏览器原生的 Web Speech API 已经能覆盖 80% 以上的常见场景,而且零依赖、零费用、零延迟。
🎙️ 一、Web Speech API 全景解析
1.1 两大核心接口
Web Speech API 包含两个完全独立的接口,解决两个不同的问题:
| 维度 | SpeechRecognition | SpeechSynthesis |
|---|---|---|
| 功能 | 语音 → 文字(ASR) | 文字 → 语音(TTS) |
| 核心能力 | 实时语音识别、连续转写 | 多语言朗读、语速语调控制 |
| 浏览器支持 | Chrome 33+、Safari 14.1+ | 所有主流浏览器 |
| 依赖 | 需要麦克风权限 | 无特殊权限 |
| 网络要求 | Chrome 默认在线识别 | 完全离线可用 |
| 中文支持 | ✅ 优秀(zh-CN) | ✅ 优秀 |
⚠️ **警告:**Chrome 的 SpeechRecognition 默认使用 Google 的在线语音识别服务,音频数据会发送到 Google 服务器。如果你的应用处理敏感信息,需要评估隐私合规风险。Safari 使用 Apple 的本地识别引擎,数据不会离开设备。
1.2 为什么选择原生 API 而非第三方 SDK
在决定技术方案之前,先看一组对比数据:
| 对比维度 | Web Speech API | 科大讯飞 SDK | 百度语音 SDK |
|---|---|---|---|
| 集成成本 | 零依赖,浏览器内置 | 需引入 JS SDK(~150KB) | 需引入 JS SDK(~200KB) |
| 费用 | 免费 | 免费额度有限,超出按量计费 | 免费额度有限,超出按量计费 |
| 延迟 | 极低(浏览器原生通道) | 中等(额外网络跳转) | 中等(额外网络跳转) |
| 中文识别准确率 | 约 92-95% | 约 96-98% | 约 95-97% |
| 离线能力 | Safari 支持,Chrome 不支持 | 不支持 | 不支持 |
| 适用场景 | 通用语音交互、辅助功能 | 高精度转写、专业术语 | 高精度转写、方言识别 |
⚡ **关键结论:**如果你的应用不需要 98% 以上的专业级识别精度(如医疗病历转写、法律文书口述),Web Speech API 是更优选择。零成本、零依赖、浏览器原生——对于 90% 的 Web 应用来说,这个精度已经足够。
🔊 二、SpeechRecognition 语音识别实战
2.1 基础用法:一次性语音识别
最常见的场景是「按住说话」——用户点击按钮开始录音,松开结束,系统返回识别结果:
// 一次性语音识别 — 点击按钮说话,获取识别结果
class SimpleSpeechRecognizer {
constructor() {
// 检测浏览器支持
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
throw new Error('此浏览器不支持 Web Speech API');
}
this.recognition = new SpeechRecognition();
this.recognition.lang = 'zh-CN'; // 设置中文识别
this.recognition.interimResults = false; // 只要最终结果
this.recognition.maxAlternatives = 1; // 只返回最可能的一个结果
}
async start() {
return new Promise((resolve, reject) => {
this.recognition.onresult = (event) => {
const transcript = event.results[0][0].transcript;
const confidence = event.results[0][0].confidence;
resolve({ transcript, confidence });
};
this.recognition.onerror = (event) => {
reject(new Error(`识别错误: ${event.error}`));
};
this.recognition.start();
});
}
stop() {
this.recognition.stop();
}
}
// 使用示例
const btn = document.getElementById('speak-btn');
const result = document.getElementById('result');
const recognizer = new SimpleSpeechRecognizer();
btn.addEventListener('mousedown', () => {
result.textContent = '🎤 正在听...';
recognizer.start().then(({ transcript, confidence }) => {
result.textContent = `识别结果: ${transcript} (置信度: ${(confidence * 100).toFixed(1)}%)`;
}).catch(err => {
result.textContent = `❌ ${err.message}`;
});
});
btn.addEventListener('mouseup', () => {
recognizer.stop();
});
💡 提示:
confidence值范围是 0 到 1,表示识别结果的置信度。在实际应用中,建议将置信度低于 0.7 的结果标记为「低置信度」,提示用户确认或重新输入。
2.2 进阶用法:连续实时转写
更复杂的场景是「持续转写」——像一个实时字幕系统,用户说话的同时文字不断更新:
// 连续实时语音转写 — 支持 interimResults 实时显示
class ContinuousTranscriber {
constructor(options = {}) {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
this.recognition = new SpeechRecognition();
this.recognition.lang = options.lang || 'zh-CN';
this.recognition.continuous = true; // 持续识别,不会自动停止
this.recognition.interimResults = true; // 返回中间结果(实时转写关键)
this.recognition.maxAlternatives = 1;
this.finalTranscript = '';
this.onUpdate = options.onUpdate || (() => {});
this.onError = options.onError || (() => {});
this.recognition.onresult = (event) => {
let interimTranscript = '';
for (let i = event.resultIndex; i < event.results.length; i++) {
const result = event.results[i];
if (result.isFinal) {
this.finalTranscript += result[0].transcript + '。';
} else {
interimTranscript += result[0].transcript;
}
}
// 回调返回:已完成的文字 + 正在识别的文字(通常用不同样式展示)
this.onUpdate({
final: this.finalTranscript,
interim: interimTranscript,
fullText: this.finalTranscript + interimTranscript
});
};
this.recognition.onerror = (event) => {
// 'no-speech' 是正常情况(用户沉默),不需要报错
if (event.error !== 'no-speech') {
this.onError(event.error);
}
};
// 连续模式下,识别结束后自动重新启动
this.recognition.onend = () => {
if (this._isRunning) {
this.recognition.start();
}
};
}
start() {
this._isRunning = true;
this.finalTranscript = '';
this.recognition.start();
}
stop() {
this._isRunning = false;
this.recognition.stop();
return this.finalTranscript;
}
}
// 使用示例 — 构建实时字幕
const transcriber = new ContinuousTranscriber({
lang: 'zh-CN',
onUpdate: ({ final, interim }) => {
document.getElementById('final-text').textContent = final;
document.getElementById('interim-text').textContent = interim;
// 自动滚动到底部
const container = document.getElementById('transcript-container');
container.scrollTop = container.scrollHeight;
},
onError: (error) => {
console.error('语音识别错误:', error);
}
});
document.getElementById('start-btn').onclick = () => transcriber.start();
document.getElementById('stop-btn').onclick = () => {
const fullText = transcriber.stop();
console.log('完整转写结果:', fullText);
};
⚠️ **警告:**Chrome 的
continuous模式有一个已知问题——在安静环境下,识别引擎会在约 30 秒后自动断开。上面代码中的onend重启逻辑就是为了解决这个问题。但在实际使用中,你还需要处理not-allowed(麦克风权限被拒绝)和network(网络中断)等错误。
2.3 多语言切换与方言支持
Web Speech API 支持超过 100 种语言和方言变体。以下是一些常用的语言代码:
// 多语言语音识别配置
const languagePresets = {
'中文(普通话)': 'zh-CN',
'中文(粤语)': 'zh-HK',
'中文(台湾)': 'zh-TW',
'英语(美国)': 'en-US',
'英语(英国)': 'en-GB',
'日语': 'ja-JP',
'韩语': 'ko-KR',
'法语': 'fr-FR',
'德语': 'de-DE',
'西班牙语': 'es-ES',
};
// 动态切换语言
function switchLanguage(recognition, langCode) {
const wasRunning = recognition._isRunning;
if (wasRunning) {
recognition.stop();
}
recognition.lang = langCode;
if (wasRunning) {
recognition.start();
}
}
💡 **提示:**语言切换需要在识别停止状态下进行。如果你在识别过程中修改
lang属性,变更会在下一次start()时生效。另外,粤语(zh-HK)的识别准确率通常低于普通话(zh-CN),因为它使用的是 Google 的粤语模型,训练数据相对较少。
🔈 三、SpeechSynthesis 语音合成实战
3.1 基础 TTS:文字转语音
SpeechSynthesis 的 API 设计非常简洁,核心就是一个 SpeechSynthesisUtterance 对象:
// 文字转语音基础用法
function speak(text, options = {}) {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = options.lang || 'zh-CN';
utterance.rate = options.rate || 1.0; // 语速:0.1 - 10,默认 1
utterance.pitch = options.pitch || 1.0; // 音调:0 - 2,默认 1
utterance.volume = options.volume || 1.0; // 音量:0 - 1,默认 1
// 选择语音(如果指定了)
if (options.voiceName) {
const voices = speechSynthesis.getVoices();
const targetVoice = voices.find(v => v.name === options.voiceName);
if (targetVoice) utterance.voice = targetVoice;
}
utterance.onstart = () => console.log('开始朗读');
utterance.onend = () => console.log('朗读结束');
utterance.onerror = (e) => console.error('朗读错误:', e.error);
speechSynthesis.speak(utterance);
}
// 使用
speak('你好,欢迎使用语音合成功能');
speak('Hello, welcome to use speech synthesis', { lang: 'en-US', rate: 1.2 });
3.2 可用语音列表与选择
浏览器内置的语音列表因操作系统而异。获取可用语音需要注意异步加载问题:
// 获取可用语音列表 — 处理异步加载
function getVoices() {
return new Promise((resolve) => {
let voices = speechSynthesis.getVoices();
if (voices.length > 0) {
resolve(voices);
return;
}
// 语音列表可能异步加载
speechSynthesis.onvoiceschanged = () => {
voices = speechSynthesis.getVoices();
resolve(voices);
};
});
}
// 获取中文语音
async function getChineseVoices() {
const allVoices = await getVoices();
const chineseVoices = allVoices.filter(v =>
v.lang.startsWith('zh') || v.lang.startsWith('cmn')
);
console.log('可用中文语音:');
chineseVoices.forEach(v => {
console.log(` ${v.name} (${v.lang}) - ${v.localService ? '本地' : '在线'}`);
});
return chineseVoices;
}
// 典型输出(Windows 11 + Chrome):
// Microsoft Xiaoxiao - Chinese, Simplified (zh-CN) - 在线
// Microsoft Yunxi - Chinese, Simplified (zh-CN) - 在线
// Google 普通话(中国大陆)(zh-CN) - 在线
// Microsoft Yaoyao - Chinese, Traditional (zh-TW) - 在线
📌 记住:
speechSynthesis.getVoices()在不同操作系统和浏览器上的返回结果完全不同。macOS 有 Apple 内置的高质量中文语音,Windows 有 Microsoft 的 Xiaoxiao 和 Yunxi,Chrome 则提供 Google 的在线语音。如果你需要一致的语音体验,建议使用 SSML(Speech Synthesis Markup Language)来精确控制发音。
3.3 长文本分段朗读
SpeechSynthesis 有一个鲜为人知的限制——当文本超过约 200 个字符时,部分浏览器(尤其是 Chrome)会截断或跳过后续内容。解决方案是将长文本分段:
// 长文本分段朗读 — 解决 Chrome 长文本截断问题
class LongTextSpeaker {
constructor(options = {}) {
this.lang = options.lang || 'zh-CN';
this.rate = options.rate || 1.0;
this.onProgress = options.onProgress || (() => {});
this.isSpeaking = false;
}
// 按句子分割文本,每段不超过 maxLength
splitText(text, maxLength = 150) {
const sentences = text.match(/[^。!?.!?\n]+[。!?.!?\n]*/g) || [text];
const chunks = [];
let currentChunk = '';
for (const sentence of sentences) {
if (currentChunk.length + sentence.length > maxLength && currentChunk.length > 0) {
chunks.push(currentChunk.trim());
currentChunk = '';
}
currentChunk += sentence;
}
if (currentChunk.trim()) chunks.push(currentChunk.trim());
return chunks;
}
async speak(text) {
if (this.isSpeaking) this.stop();
this.isSpeaking = true;
const chunks = this.splitText(text);
const totalChunks = chunks.length;
for (let i = 0; i < chunks.length; i++) {
if (!this.isSpeaking) break; // 支持中途停止
this.onProgress({ current: i + 1, total: totalChunks, text: chunks[i] });
await new Promise((resolve) => {
const utterance = new SpeechSynthesisUtterance(chunks[i]);
utterance.lang = this.lang;
utterance.rate = this.rate;
utterance.onend = () => resolve();
utterance.onerror = () => resolve(); // 出错也继续下一段
speechSynthesis.speak(utterance);
});
}
this.isSpeaking = false;
}
stop() {
this.isSpeaking = false;
speechSynthesis.cancel();
}
}
// 使用示例
const speaker = new LongTextSpeaker({
lang: 'zh-CN',
rate: 1.0,
onProgress: ({ current, total }) => {
document.getElementById('progress').textContent = `朗读进度: ${current}/${total}`;
}
});
// 播放长文本
speaker.speak(document.getElementById('article-content').textContent);
document.getElementById('stop-btn').onclick = () => speaker.stop();
⚠️ 警告:
speechSynthesis.cancel()会立即停止当前所有朗读队列。但有一个 Chrome 已知 Bug——调用cancel()后立即调用speak()可能不会生效。解决方案是在cancel()后加一个setTimeout(() => speechSynthesis.speak(utterance), 100)的延迟。
🛡️ 四、生产环境实战:完整语音助手
将语音识别和语音合成结合起来,我们可以构建一个完整的语音交互助手。以下是一个生产级的实现,包含错误处理、降级方案和性能优化:
// 生产级语音助手 — 语音识别 + 语音合成 + 错误处理
class VoiceAssistant {
constructor(options = {}) {
this.lang = options.lang || 'zh-CN';
this.onStateChange = options.onStateChange || (() => {});
this.onTranscript = options.onTranscript || (() => {});
this.onResponse = options.onResponse || (() => {});
this.state = 'idle'; // idle | listening | processing | speaking
this._initRecognition();
}
_initRecognition() {
const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SR) throw new Error('不支持语音识别');
this.recognition = new SR();
this.recognition.lang = this.lang;
this.recognition.interimResults = true;
this.recognition.continuous = false;
this.recognition.onresult = (event) => {
const result = event.results[event.results.length - 1];
const transcript = result[0].transcript;
if (result.isFinal) {
this._setState('processing');
this.onTranscript(transcript, true);
this._processCommand(transcript);
} else {
this.onTranscript(transcript, false);
}
};
this.recognition.onerror = (event) => {
if (event.error === 'no-speech') {
this._setState('idle');
return;
}
console.error('识别错误:', event.error);
this._setState('idle');
};
this.recognition.onend = () => {
if (this.state === 'listening') {
// Chrome 会自动停止,重置状态
this._setState('idle');
}
};
}
_setState(newState) {
this.state = newState;
this.onStateChange(newState);
}
startListening() {
if (this.state !== 'idle') return;
this._setState('listening');
this.recognition.start();
}
stopListening() {
this.recognition.stop();
this._setState('idle');
}
async speak(text) {
this._setState('speaking');
return new Promise((resolve) => {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = this.lang;
utterance.rate = 1.0;
utterance.onend = () => {
this._setState('idle');
resolve();
};
utterance.onerror = () => {
this._setState('idle');
resolve();
};
speechSynthesis.speak(utterance);
});
}
async _processCommand(text) {
// 这里可以接入你的业务逻辑或 AI API
const response = this._generateResponse(text);
this.onResponse(response);
await this.speak(response);
}
_generateResponse(text) {
// 简单的规则匹配示例 — 实际项目中替换为 AI API 调用
if (text.includes('时间') || text.includes('几点')) {
return `现在是 ${new Date().toLocaleTimeString('zh-CN')}`;
}
if (text.includes('日期') || text.includes('今天')) {
return `今天是 ${new Date().toLocaleDateString('zh-CN', {
year: 'numeric', month: 'long', day: 'numeric', weekday: 'long'
})}`;
}
return `你说的是:${text}。这是一个语音助手演示,你可以问我时间和日期。`;
}
destroy() {
this.stopListening();
speechSynthesis.cancel();
}
}
// 使用示例
const assistant = new VoiceAssistant({
lang: 'zh-CN',
onStateChange: (state) => {
const stateMap = { idle: '待命', listening: '正在听...', processing: '处理中...', speaking: '正在说...' };
document.getElementById('state').textContent = stateMap[state] || state;
},
onTranscript: (text, isFinal) => {
document.getElementById('transcript').textContent = text;
document.getElementById('transcript').style.opacity = isFinal ? 1 : 0.6;
},
onResponse: (text) => {
document.getElementById('response').textContent = text;
}
});
document.getElementById('mic-btn').onclick = () => {
if (assistant.state === 'idle') {
assistant.startListening();
} else {
assistant.stopListening();
}
};
📊 五、浏览器兼容性与降级方案
5.1 兼容性现状(2026 年 6 月)
| 特性 | Chrome | Safari | Firefox | Edge |
|---|---|---|---|---|
| SpeechRecognition | ✅ 33+ | ✅ 14.1+ | ❌ 不支持 | ✅ 79+ |
| SpeechSynthesis | ✅ 33+ | ✅ 7+ | ✅ 49+ | ✅ 14+ |
| continuous 模式 | ✅ | ✅ | ❌ | ✅ |
| interimResults | ✅ | ✅ | ❌ | ✅ |
| 中文识别 | ✅ 优秀 | ✅ 良好 | ❌ | ✅ 优秀 |
| 离线识别 | ❌ 需联网 | ✅ 本地引擎 | ❌ | ❌ 需联网 |
⚡ **关键结论:**SpeechSynthesis 的浏览器覆盖率接近 100%,可以放心使用。SpeechRecognition 在 Firefox 上完全不支持,这是最大的兼容性障碍。如果你的应用需要支持 Firefox 用户,必须提供降级方案。
5.2 检测与降级
// 语音能力检测与降级策略
function detectSpeechCapabilities() {
const capabilities = {
recognition: false,
synthesis: false,
recognitionOffline: false,
synthesisVoices: [],
};
// 检测语音识别
const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SR) {
capabilities.recognition = true;
// Safari 支持离线识别
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
capabilities.recognitionOffline = true;
}
}
// 检测语音合成
if ('speechSynthesis' in window) {
capabilities.synthesis = true;
capabilities.synthesisVoices = speechSynthesis.getVoices();
}
return capabilities;
}
// 降级方案:不支持时显示文字输入框
function setupWithFallback(container) {
const caps = detectSpeechCapabilities();
if (!caps.recognition) {
// 降级:显示文字输入 + 提示安装支持语音的浏览器
container.innerHTML = `
<div class="fallback-notice">
⚠️ 您的浏览器不支持语音识别,请使用 Chrome 或 Safari。
<input type="text" placeholder="请用键盘输入..." id="text-fallback" />
</div>
`;
return { useVoice: false };
}
return { useVoice: true, capabilities: caps };
}
💡 六、最佳实践与避坑指南
✅ 推荐做法
- 始终提供视觉反馈——用户需要知道麦克风是否在工作、识别是否在进行中
- 处理
onend事件——Chrome 在连续模式下会自动断开,需要重新启动 - 设置合理的
lang——不要默认用en-US,中文用户应该设置为zh-CN - 分段处理长文本 TTS——避免 Chrome 的长文本截断 Bug
- 在
unload事件中调用speechSynthesis.cancel()——防止页面关闭后仍在朗读
❌ 避免做法
- 不要在后台标签页中使用语音识别——浏览器会暂停后台页面的音频捕获
- 不要假设所有浏览器都支持——Firefox 完全不支持 SpeechRecognition
- 不要在没有用户手势的情况下调用
speak()——部分浏览器要求用户交互后才能播放音频 - 不要忽略
error事件中的not-allowed错误——这意味着用户拒绝了麦克风权限
⚠️ 性能注意事项
- Chrome 的在线识别会有 200-500ms 的网络延迟,不适合对延迟极度敏感的场景
getVoices()在首次调用时可能返回空数组,需要监听voiceschanged事件- 频繁调用
start()/stop()会导致浏览器创建过多的音频上下文,建议复用 Recognition 实例 - 移动端浏览器的语音识别会消耗较多电量,长时间使用时注意提示用户
🔗 七、相关工具与延伸阅读
Web Speech API 为浏览器端的语音交互提供了开箱即用的原生方案。对于大多数 Web 应用来说,它已经是最佳选择——零依赖、零费用、浏览器原生。但如果你需要更高的识别精度、方言支持或离线能力,以下工具值得了解:
- ✅ Web Speech API 规范 — W3C 官方规范文档
- ✅ MDN SpeechRecognition — 最权威的 API 参考
- 🔧 Whisper.cpp — OpenAI Whisper 的 C++ 移植,支持浏览器 WASM 运行,离线识别精度极高
- 🔧 Porcupine — 轻量级唤醒词引擎,适合语音助手场景
- 🔧 jsjson.com 在线工具 — JSON 处理、编码转换、哈希计算等开发者必备工具
如果你正在为 jsjson.com 这样的开发者工具站添加语音功能——比如语音输入搜索关键词、朗读代码注释、语音控制工具切换——Web Speech API 是最轻量、最实用的方案。不需要引入任何第三方 SDK,打开浏览器就能用。