@fugood/whisper.node

中文文档
1.2.0-rc.2发布于 2026/11/09MITTypeScriptCommonJS零依赖近 30 天下载 3.6万

An another Node binding of whisper.cpp to make same API with whisper.rn as much as possible.

原始使用文档
文档目录11 节
  1. Platform Support
  2. Installation
  3. Usage
  4. Basic Transcription
  5. NVIDIA Parakeet TDT
  6. Voice Activity Detection (VAD)
  7. Native Logs
  8. Browser WASM
  9. Lib Variants
  10. Snapdragon (Hexagon NPU) benchmark
  11. License

whisper.node

CI NPM Version NPM Downloads

An another Node binding of whisper.cpp to make same API with whisper.rn as much as possible.

  • whisper.cpp: Automatic speech recognition with multi-platform support
  • whisper.rn: React Native binding of whisper.cpp

Platform Support

  • macOS
    • arm64: CPU and Metal GPU acceleration
    • x86_64: CPU only
  • Windows (x86_64 and arm64)
    • CPU
    • GPU acceleration via Vulkan
    • GPU acceleration via CUDA (x86_64)
  • Linux (x86_64 and arm64)
    • CPU
    • GPU acceleration via Vulkan
    • GPU acceleration via CUDA
    • NPU acceleration via Qualcomm Hexagon (arm64 Snapdragon, experimental)
  • Web
    • WASM
    • Optional WebGPU through ggml-webgpu when the WASM package is built with GGML_WEBGPU=ON

Installation

npm install @fugood/whisper.node

Usage

Basic Transcription

import { initWhisper } from '@fugood/whisper.node'

const context = await initWhisper({
  model: 'path/to/ggml-base.en.bin',
  useGpu: true,
}, libVariant)

// transcribeFile returns { stop, promise }
const { stop: stop1, promise: promise1 } = context.transcribeFile('audio1.wav', {
  language: 'en',
  temperature: 0.0,
  // ...
})

const result1 = await promise1

// transcribeData also returns { stop, promise }
let audioBuffer // PCM 16-bit, mono, 16kHz
const { stop: stop2, promise: promise2 } = context.transcribeData(audioBuffer, {
  language: 'en',
  temperature: 0.0,
  // ...
})

const result2 = await promise2

// You can also cancel transcription if needed
// await stop1() // Cancels the first transcription
// await stop2() // Cancels the second transcription
//
// stop() interrupts the decode; the job's promise then resolves with
// `isAborted: true` and the segments decoded so far (it does not reject).
// Native failures reject the promise. The context stays usable afterwards.

// Always release the context when done
await context.release()

NVIDIA Parakeet TDT

ParakeetContext runs NVIDIA's Parakeet TDT 0.6B v3 model through the Parakeet API included in whisper.cpp. The v3 model supports English plus 24 other European languages.

Download a GGUF model from ggml-org/parakeet-GGUF before initializing the context:

ModelApproximate size
ggml-parakeet-tdt-0.6b-v3-q4_0.bin356 MB
ggml-parakeet-tdt-0.6b-v3-q4_k.bin416 MB
ggml-parakeet-tdt-0.6b-v3-q8_0.bin669 MB
ggml-parakeet-tdt-0.6b-v3-f16.bin1.26 GB
import { initParakeet } from '@fugood/whisper.node'

const parakeetContext = await initParakeet({
  filePath: 'path/to/ggml-parakeet-tdt-0.6b-v3-q4_0.bin',
  useGpu: true,
}, libVariant)

// transcribeFile / transcribeData return { stop, promise } like WhisperContext
const { stop, promise } = parakeetContext.transcribeFile('audio.wav', {
  maxThreads: 4,
})
const { result, segments, isAborted } = await promise

// Cancel an in-flight transcription when needed:
// await stop()

await parakeetContext.release()

transcribeData() accepts raw signed 16-bit PCM as ArrayBuffer; raw audio must be mono at 16 kHz. Whisper-specific options (language, prompt, callbacks) are not supported, use maxThreads and audioCtx instead.

Voice Activity Detection (VAD)

import { initWhisperVad } from '@fugood/whisper.node'

// Speech model: whisper.cpp's ggml-silero-*.bin or a vad.cpp GGUF (Silero, FireRedVAD)
const vadContext = await initWhisperVad({
  filePath: 'path/to/ggml-silero-v6.2.0.bin',
  nThreads: 1, // Default: 1, best for the small per-hop graphs
}, libVariant)

// Offline detection: speech segments, t0/t1 in 10 ms units
const segments = await vadContext.detectSpeechFile('audio.wav')
const segments2 = await vadContext.detectSpeechData(audioBuffer, {
  threshold: 0.5,
  minSpeechDurationMs: 250,
  minSilenceDurationMs: 100,
  speechPadMs: 30,
})

// Stateful detection over live audio: the model state carries over between
// pushes, one speech probability per `hopSamples` (512 samples = 32 ms for Silero)
const stream = await vadContext.createStream()
const probs = await stream.push(pcmChunk) // any length
const lastProbs = await stream.flush() // end of audio
await stream.reset() // start a new audio stream
await stream.release()

await vadContext.release() // also frees its streams

Note: Audio data should be 16-bit PCM, mono, 16kHz format. The library expects ArrayBuffer containing raw audio data.

VAD runs on vad.cpp, always on the CPU (useGpu is ignored). Probabilities follow official Silero exactly; samplesOverlap is ignored and negThreshold is available.

Native Logs

import {
  addNativeLogListener,
  isNativeLogEnabled,
  toggleNativeLog,
} from '@fugood/whisper.node'

const logs = addNativeLogListener((level, text) => {
  console.log(`[whisper ${level}] ${text}`)
})

await toggleNativeLog(true)
console.log(isNativeLogEnabled())

// ...

await toggleNativeLog(false)
logs.remove()

Log levels are emitted as lowercase error, warn, info, or debug strings. The same helpers are available in Node.js and browser WASM builds.

Browser WASM

The browser package keeps the same promise-based initWhisper, initWhisperVad, and initParakeet entry points. In browsers, filePath is treated as a URL and the model is fetched into the WASM filesystem.

import { initWhisper, initWhisperVad } from '@fugood/whisper.node'

const whisper = await initWhisper({
  filePath: 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin',
  maxModelBytes: 1536 * 1024 * 1024,
  useGpu: false,
})

const { promise } = whisper.transcribeFile('https://raw.githubusercontent.com/ggml-org/whisper.cpp/master/samples/jfk.wav', {
  language: 'en',
  temperature: 0,
})

console.log(await promise)
await whisper.release()

const vad = await initWhisperVad({
  filePath: 'https://huggingface.co/ggml-org/whisper-vad/resolve/main/ggml-silero-v6.2.0.bin',
  useGpu: false,
})
console.log(await vad.detectSpeechFile('https://raw.githubusercontent.com/ggml-org/whisper.cpp/master/samples/jfk.wav'))
await vad.release()

The browser package ships both single-thread and pthread WASM artifacts. On cross-origin isolated pages (Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp), the loader uses the pthread artifact with SharedArrayBuffer; otherwise it falls back to the single-thread artifact automatically. Oversized model downloads fail before loading into MEMFS. Firefox is capped at 256 MiB by default; other browsers default to 75% of the configured WASM maximum memory. Pass maxModelBytes only when you know the target browser can allocate the model. Whisper transcription defaults to up to 8 threads based on browser hardware concurrency when pthreads are available; pass maxThreads to override it. Browser WASM clamps maxThreads to the compiled pool limit of 8, or 1 in the single-thread fallback. Browser pages run model loading, transcription, benchmarks, and VAD in a dedicated module worker by default so the UI thread can keep rendering. Use the main whisper.node package entrypoint in browser code too:

import { configureWasm, initWhisper } from '@fugood/whisper.node'

Use configureWasm({ worker: false }) only when you explicitly need the in-thread runtime, configureWasm({ threads: false }) to force the single-thread artifact, or pass workerPath, jsPath, and wasmPath when serving the package files from custom URLs. The older workerUrl and runtimeScriptUrl option names still work. Model downloads are cached in browser Cache Storage by default. Pass cacheModel: false to disable persistent caching, modelCacheName to isolate the cache namespace, or modelCacheKey when the fetch URL is a proxy or signed URL but should reuse the same cached model.

Build the browser package with:

npm run build-wasm

Or build with the Emscripten Docker image:

npm run build-wasm-docker

npm run build-wasm enables GGML_WEBGPU=ON by default and emits wasm/whisper-node.js, wasm/whisper-node.wasm, wasm/whisper-node.threads.js, and wasm/whisper-node.threads.wasm. Use bash scripts/build-wasm.sh --no-webgpu for a CPU-only WASM build, or --no-threads / --threads to build only one CPU threading variant. Pass --single-file only when you want the WASM binary embedded into each generated JS file. Modern Emscripten embeds the pthread worker bootstrap in the main JS file, so a separate whisper-node.worker.js is not expected. The browser package also ships its own module worker.js wrapper for non-blocking model load and inference. npm run build-wasm-docker uses emscripten/emsdk:4.0.14-arm64 on arm64 hosts such as Apple Silicon Macs, and emscripten/emsdk:4.0.13 on amd64 hosts. Override with EMSCRIPTEN_IMAGE or EMSCRIPTEN_PLATFORM when needed. A local smoke page is available after building:

node examples/wasm/server.mjs

In the WASM package, useGpu: true enables WebGPU for whisper transcription when the browser supports navigator.gpu. VAD always runs on the CPU (vad.cpp's own kernels, with WASM SIMD).

Lib Variants

  • default: General usage, not support GPU except macOS (Metal)
  • vulkan: Support GPU Vulkan (Windows/Linux), but some scenario might unstable
  • cuda: Support GPU CUDA (Windows/Linux), but only for limited capability

    Linux: (x86_64: 8.9, arm64: 8.7) Windows: x86_64 - 12.0

  • [x] snapdragon: Qualcomm Snapdragon Hexagon NPU via ggml-hexagon (Linux arm64). The HTP skels ship in the platform package and ADSP_LIBRARY_PATH is set automatically; GGML_HEXAGON_NDEV defaults to 16

Snapdragon (Hexagon NPU) benchmark

Pass 'snapdragon' as the libVariant argument (e.g. initWhisper({ model, useGpu: true }, 'snapdragon')). The whisper model runs on the Hexagon NPU with flash attention always on.

Benchmark on Qualcomm IQ-9075 (HTP = snapdragon variant, CPU = default variant):

ModelHTP encodeCPU encodeHTP decodeCPU decodeHTP transcribeCPU transcribe
tiny.en95 ms361 ms6.7 ms3.4 ms316 ms524 ms
base143 ms787 ms6.3 ms5.5 ms430 ms846 ms
base-q8_0154 ms557 ms6.9 ms4.2 ms397 ms582 ms
small360 ms2824 ms16.3 ms14.0 ms915 ms2789 ms
distil-small.en-q8_0320 ms1743 ms5.3 ms4.8 ms510 ms1530 ms
medium812 ms8799 ms35.1 ms38.3 ms1915 ms8006 ms
large-v3-turbo-q8_02479 ms9400 ms9.8 ms8.2 ms2838 ms7616 ms

The same backend is available on Android through whisper.rn.

License

MIT


Built and maintained by BRICKS.

版本与发布记录

@fugood/whisper.node 共发布 28 个版本,首次发布于 2025/07/11;过去 12 个月发布了 20 个版本。

版本发布日期距今
1.2.0-rc.2latest2026/11/096 天前
1.2.0-rc.12026/10/071 个月前
1.2.0-rc.02026/09/171 个月前
1.1.32026/08/272 个月前
1.1.22026/08/242 个月前
1.1.12026/07/193 个月前

关于 @fugood/whisper.node 的常见问题

@fugood/whisper.node 是什么?

An another Node binding of whisper.cpp to make same API with whisper.rn as much as possible.

如何安装 @fugood/whisper.node?

在项目目录运行 npm install @fugood/whisper.node,或使用 pnpm add / yarn add / bun add。

@fugood/whisper.node 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@fugood/whisper.node 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@fugood/whisper.node 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=16.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@fugood/whisper.node 可以商用吗?

@fugood/whisper.node 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@fugood/whisper.node 的下载量有多少?

根据 npm 官方下载统计,@fugood/whisper.node 在 2026/10/16 – 2026/11/14 这 30 天内被下载 35,553 次,日均约 1,185 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@fugood/whisper.node 还在维护吗?

最新版本 1.2.0-rc.2 发布于 2026/11/09(6 天前);至今共发布 28 个版本,其中过去 12 个月发布了 20 个;首次发布于 2025/07/11。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。