用浏览器原生 Web Speech API 做一个不需要后端服务的语音转文字工具,记录了权限处理和实时识别的一些实践细节。

这个需求其实挺常见的——用户对着麦克风说话,页面上实时出文字。

通常的做法是调后端接口或者接第三方语音识别服务,但如果你不需要那么高的准确率,只是想快速跑起来,浏览器自带的 Web Speech API 就能搞定,而且完全是前端的事,不用搭服务、不用申请 key。

先说一下兼容性问题,这个容易忽略。

SpeechRecognition 接口在 Chrome 和 Edge 上叫 webkitSpeechRecognition,Firefox 直接用 SpeechRecognition。所以初始化的时候得两个都检查一下:

if (!('webkitSpeechRecognition' in window) && !('SpeechRecognition' in window)) {
    // 提示用户换浏览器
    return;
}
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;

这个检查不做的后果就是,用户点开页面发现按钮灰的或者直接报错,体验很差。

我在 Safari 上测试过,目前还不支持,所以移动端的覆盖是个问题,这个后面再说。

拿到 SpeechRecognition 实例之后,有几个配置项会影响识别效果。

lang 设成 'zh-CN' 走中文识别,continuoustrue 会让识别持续进行而不是说一句停一下,interimResultstrue 可以在说话过程中拿到临时的识别结果——这个对实时展示很重要,用户能看到自己说的话正在被转成文字,不会觉得卡住了。

关于权限,有个地方一开始没注意到:navigator.mediaDevices.getUserMedia 需要手动调一次。

虽然 SpeechRecognition 自己也会触发浏览器的麦克风权限请求,但如果你在 start() 之前显式调用 getUserMedia,能提前捕获用户拒绝授权的情况,方便给个友好的提示,而不是等 onerror 里收到 not-allowed 错误再处理。代码大概是这样的:

try {
    await navigator.mediaDevices.getUserMedia({ audio: true });
} catch (err) {
    showError('麦克风访问被拒绝,请允许使用麦克风');
    return;
}
recognition.start();

结果处理的部分,onresult 回调里 event.results 是一个列表,每个结果上有个 isFinal 标记。持续识别场景下,用户说完一句话停顿之后,那一段结果会标成 final,还没说完的就是 interim。

拼接的时候把 final 的内容固定下来,interim 的部分用灰色或者虚线下划线展示,能做到那种“边说边出字”的效果。代码不复杂:

recognition.onresult = (event) => {
    let interimTranscript = '';
    for (let i = event.resultIndex; i < event.results.length; i++) {
        const transcript = event.results[i][0].transcript;
        if (event.results[i].isFinal) {
            finalTranscript += transcript + '\n';
        } else {
            interimTranscript += transcript;
        }
    }
    resultDiv.innerHTML = finalTranscript + '<span class="text-gray-400">' + interimTranscript + '</span>';
};

错误处理这块,onerrorevent.error 会返回几个常见的值:no-speech 是没检测到语音,audio-capture 是音频设备有问题,not-allowed 是权限被拒。针对这几个做下分支处理就行,其余的错误统一兜底。我这里加了个 5 秒自动隐藏的错误提示,避免一直占着页面。

还有一个需要注意的点:onend 回调会在识别停止时触发。如果你设了 continuous: true,正常情况下用户不主动停止时识别会一直跑,但某些情况下(比如网络波动或者浏览器自己中断了)也会走到 onend。所以在这个回调里判断一下 isListening 状态,如果是 true 说明是非预期的中断,手动调一次 start() 恢复识别:

recognition.onend = () => {
    if (isListening) {
        recognition.start();
    }
};

按钮状态的切换我就没细展开了,基本就是点击时根据 isListening 状态调 startRecognitionstopRecognition,同时换一下按钮样式和文字。

来此加密的SSL证书有效期为90天(域名类证书)和7天(IP类证书),系统会在证书到期前自动进行重新申请,免去运维人员的负担。用户无需手动干预,自动续期功能确保了网站的持续安全。

加了个脉冲动画的 CSS,录音时按钮会呼吸闪烁,给用户一个明确的“正在录音”的视觉反馈,比纯文字状态提示直观。

整套代码我放在了一个 HTML 文件里,样式用的 Tailwind CSS CDN,图标用的 Font Awesome,都是外链引入,不用构建工具,直接打开就能跑。适合快速验证或者内部小工具的场景。

不过实话实说,Web Speech API 的识别准确率跟商业 API 比还是有差距的,尤其在有背景噪音或者说话人带口音的情况下。

它的优势是零成本、零延迟部署,不需要任何后端资源。如果你的场景对准确率要求很高,比如会议纪要、采访稿整理,那还是得上专业的语音识别服务。但如果只是想做一个语音输入框、搜索框的语音输入,或者内部用的标注工具,这个方案完全够用。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐