toolgarden.xyz
EN
浏览器工具开发Whisper语音转文字Transformers.jsONNX WASM

怎么在浏览器运行 Whisper 实现语音转文字

使用 Transformers.js 和 ONNX WASM 实现浏览器语音识别,覆盖模型缓存、音频分块、麦克风快照转写和隐私边界。

ToolGarden 推荐的工具优先在浏览器本地运行,文件和文本不必上传到服务器,适合更注重安全隐私的日常处理。

发布于 2026年7月22日约 11 分钟阅读作者 ToolGarden

通过 Transformers.js 与 ONNX WebAssembly,Whisper 可以直接在浏览器中运行。推理期间音频留在当前标签页,模型则作为应用依赖下载并缓存。这形成了有价值的隐私边界,但不等于零网络流量,也不等于服务端级别的流式识别。

实用封装需要缓存一份 pipeline、展示模型下载进度、处理语言选择与长音频分块、释放 Object URL、管理麦克风权限,并准确解释“实时转写”其实是浏览器周期性重新识别累计录音。

区分模型交付与音频处理

模型、tokenizer、配置和 ONNX 运行时必须先到达浏览器。当前设计允许从远程模型仓库加载模型文件并使用浏览器缓存,ONNX WASM 则由站点提供;用户音频通过本地 Object URL 交给页面中的 pipeline。

第一次使用会承担模型下载和初始化成本。后续可能复用缓存,但无痕模式、清理站点数据、缓存淘汰或模型版本变化都可能重新下载。

数据典型路径缓存方式
Whisper 模型与 tokenizer远程模型站点到浏览器浏览器缓存
ONNX WASM 运行时站点到浏览器HTTP/浏览器缓存
上传音频File 到本地 Object URL推理封装不上传
转写文本pipeline 结果到 React state只有产品明确保存时才持久化

创建并缓存唯一 ASR Pipeline

pipeline 初始化成本很高,因此应缓存 Promise,而不是每个文件重新创建。初始化失败时重置 Promise,才能真正重试。progress callback 可以把模型下载、模型就绪、推理与完成分开显示。

示例使用单线程 WASM,以获得更可预测的兼容性。多线程通常依赖跨源隔离,必须在目标浏览器实测后再开启。

const transformers = await import('@xenova/transformers/dist/transformers.min.js');

transformers.env.allowLocalModels = false;
transformers.env.useBrowserCache = true;
transformers.env.backends.onnx.wasm.wasmPaths = '/models/transformers/';
transformers.env.backends.onnx.wasm.numThreads = 1;

const transcriber = await transformers.pipeline(
  'automatic-speech-recognition',
  'Xenova/whisper-tiny',
  { progress_callback: reportModelProgress },
);

const result = await transcriber(URL.createObjectURL(file), {
  chunk_length_s: 30,
  stride_length_s: 5,
  language: selectedLanguage === 'auto' ? undefined : selectedLanguage,
});

长录音使用带重叠的分块

Whisper 模型处理的是有限音频窗口。30 秒 chunk 加 5 秒 stride 会让相邻窗口重叠,降低词语刚好落在硬边界时被截断的概率,pipeline 再整合这些上下文。

长文件仍会增加总计算量与内存压力。应在加载模型前校验格式和大小,说明模型加载到 100% 后仍需继续识别,也不要承诺低性能设备可以实时完成。

  • 自动语言让模型封装自行判断语言。
  • 已知录音语言时明确传入 zh 或 en,可以减少歧义。
  • 无论成功或失败,都要在 finally revoke 输入 URL。
  • pipeline 可以缓存,但每个文件的 URL 和预览需要释放。

麦克风转写是周期性快照推理

MediaRecorder 可以每秒产生一块编码数据。应用按照固定间隔,把当前累计 chunks 合并成 File,再调用同一套文件转写函数,用最新完整结果替换预览。

这种方案比有状态流式解码器简单,但录音越长,每次快照重复处理的旧音频越多。必须阻止多个快照任务重叠,最终停止前等待正在运行的快照,并始终停止全部 MediaStream track。

const recorder = new MediaRecorder(stream, { mimeType });
const chunks: Blob[] = [];

recorder.ondataavailable = event => {
  if (event.data.size > 0) chunks.push(event.data);
};

recorder.start(1000);

const refreshId = window.setInterval(async () => {
  const snapshot = new File([new Blob(chunks, { type: mimeType })], 'live.webm');
  const result = await transcribeAudioFile(snapshot, { language: 'auto' });
  if (result.ok) setTranscript(result.text);
}, REFRESH_INTERVAL_MS);

认真管理麦克风权限和生命周期

显示功能可用前,先检查 MediaRecorder、getUserMedia 和浏览器支持的 MIME。可以请求回声消除与降噪,但它们只是浏览器约束,不是质量保证。

停止时请求最后一块数据、清理刷新 interval、停止 recorder、停止所有 track、完成最后一次快照,再让 UI 回到 idle。权限拒绝或异常时也要执行同样清理,然后展示本地化错误。

理解 Tiny 模型的能力边界

Whisper tiny 优先考虑下载体积和浏览器可运行性。噪声、口音、多人对话、技术词汇、音乐或远距离麦克风都会降低准确率,推理速度也取决于 CPU、内存与 WASM 支持。

如果必须保证准确率、说话人区分、时间戳、延迟或集中审计,应使用更大模型或受控服务。界面要允许用户编辑和复制结果,不能把模型输出当作权威文本。

总结

浏览器 Whisper 不只是一次推理调用,更是模型交付和生命周期问题。缓存一份 pipeline,区分模型流量与音频处理,使用重叠分块,串行执行麦克风快照,并清理权限与 Object URL,才能让转写过程既可信又可用。

常见问题

Q.浏览器 Whisper 可以完全离线吗?

应用、运行时和模型资源全部进入缓存后可能离线运行,但第一次通常需要下载。缓存被清理或使用无痕模式时可能再次下载,因此必须实测,不能直接承诺永久离线。

Q.麦克风模式是真正的流式语音识别吗?

这套实现不是。MediaRecorder 收集数据,应用周期性重新识别累计录音。真正的流式解码器会维护模型状态,并以不同方式处理增量音频。

Q.为什么音频分块要重叠?

词语和音素可能跨越固定边界。重叠让后一块获得前一块部分上下文,减少边界截断,但会增加一些重复计算。

Q.为什么选择 Whisper tiny 而不是大模型?

小模型下载和初始化更快,占用浏览器内存更少。大模型可能更准确,但首次使用、推理时间和移动端兼容性可能难以接受。