@degreesign/voice

中文文档
2.4.2发布于 2026/11/09MITTypeScriptCommonJS近 30 天下载 3294

Offline dictation, speech to text and text to speech (TTS) for web and Node.js

原始使用文档
文档目录20 节
  1. Table of contents
  2. Why
  3. Install
  4. CDN
  5. Quick start
  6. Browser microphone
  7. PCM input
  8. Speech to text
  9. Options
  10. Model loading
  11. Audio and timing
  12. Weights mirror
  13. Text to speech
  14. Voice cloning
  15. Dictation
  16. Demo
  17. Models
  18. Storage and performance
  19. Architecture
  20. Licensing

@degreesign/voice

Turn speech into text and text into speech, privately, on the user's own device. Use it for dictation, voice notes, transcription, speech to text, text to speech and TTS, with no cloud service, account or network connection after setup.

npm version license node types

Try the live demo to run speech to text and text to speech in the browser, with no install.

Table of contents

Why

  • Offline: weights download once, then every decode runs on the machine.
  • Streaming: transcribe returns a live handle and keeps updating while audio arrives.
  • One API: the same entry autodetects Node.js and the browser.
  • Any audio: 16 kHz mono PCM, a Blob or File, a URL, a filesystem path, or the browser microphone.
  • Typed: TypeScript declarations cover every export.
  • Two directions: speech to text and text to speech.
  • Self contained: the cache, storage and audio decoders are bundled at build time.

DoListy uses the package for task dictation and voice notes.

Install

npm install @degreesign/voice
yarn add @degreesign/voice
pnpm add @degreesign/voice

Node.js 24 or newer is required. The main entry exports recognition and the voice library. The dictation controller has its own entry.

The published files are:

  • dist/node/degreesign.node.min.js, the CommonJS entry for Node.
  • dist/browser/degreesign.min.js, the full UMD entry as window.dsVoice.
  • dist/browser/whisper.min.js, the Whisper only UMD entry as window.dsVoiceWhisper.
  • dist/browser/dictation.min.js, the dictation UMD entry.
  • dist/browser/dictation.worker.js, the prebuilt dictation worker.

Build from source with yarn build and typecheck with yarn typecheck.

CDN

<script src="https://cdn.jsdelivr.net/npm/@degreesign/voice@latest/dist/browser/degreesign.min.js"></script>

Try the live speech demo before installing. It runs speech to text and text to speech in the browser.

Quick start

Browser microphone

<script src="https://cdn.jsdelivr.net/npm/@degreesign/voice@latest/dist/browser/degreesign.min.js"></script>
<script>
  const { Language, transcribe } = window.dsVoice;
  const session = transcribe({ language: Language.auto });
  const timer = setInterval(() => {
    output.textContent = session.fullText().text;
  }, 500);
  stopButton.onclick = async () => {
    clearInterval(timer);
    const result = await session.stop();
    output.textContent = result.text;
  };
</script>

A browser session with no pcm, no audioFilePath and no manual feed asks for microphone access and records until stop.

PCM input

The input is 16 kHz mono Float32Array PCM.

import { Language, transcribe } from "@degreesign/voice";

const session = transcribe({ pcm, language: Language.auto });
console.log(session.fullText().text);

const result = await session.done;
console.log(result.text, result.duration, result.meta.rtf);

To warm up the model first, call preloadVoice. To feed audio as it arrives, pass microphone: false and append samples with push.

Speech to text

transcribe(options?) returns a TranscribeSession immediately.

MemberDescription
fullText()Latest text and decode time. Cheap and synchronous.
push(pcm)Append 16 kHz mono samples with microphone: false.
stop()Stop capture or input, flush the tail and resolve.
cancel()Drop pending audio and resolve without a final decode.
doneResolves after the input ends, stop or cancel.
errorFirst asynchronous failure, or null.

Options

FieldDescription
modelModelId.WhisperBase, the only model.
pcm16 kHz mono Float32Array, highest priority source.
audioFilePathURL string, Blob or File, or a Node filesystem path.
microphoneCapture the microphone. Defaults to true; false accepts push.
languageAudio language or Language.auto. Defaults to automatic detection.
qualityQuality.High (F16) by default, Medium (Q8_0) or Low (Q4_K_M).
timestampsTimestampMode.none, .segment or .word.
lowLatencySkip 30 second padding. Defaults to true.
allowedDiskSpaceMBCache cap in MB. Defaults to 512.
updateIntervalMsLive update cadence in ms. Defaults to 1000.
weightsBaseUrlWeights host, defaults to https://huggingface.co.
weightCacheMBF32 tensor cache cap in MB.
threadsThreadCount.Two, .Four or .Eight. Defaults to Four.
onProgressDownload and load progress across every file.

Model loading

import {
    Language,
    ModelId,
    Quality,
    isModelCached,
    loadModel,
    preloadVoice,
} from "@degreesign/voice";

await preloadVoice({
    model: ModelId.WhisperBase,
    language: Language.en,
    quality: Quality.Low,
});

const cached = await isModelCached({
    model: ModelId.WhisperBase,
    language: Language.en,
    quality: Quality.Low,
});
const model = await loadModel({
    model: ModelId.WhisperBase,
    language: Language.en,
    quality: Quality.Low,
});

preloadVoice downloads and loads a model ahead of time. loadModel returns the loaded model. isModelCached checks the store without using the network. An explicit Language.en selects the English only checkpoint; every other language, including Language.auto, uses the multilingual checkpoint.

Audio and timing

The first source that is present wins: pcm, then audioFilePath, then a manual feed with microphone: false, then the browser microphone.

In Node, paths are read from the filesystem and decoded as WAV, FLAC or MP3. In the browser, a URL is fetched, a Blob or File is read directly, and decoding uses the Web Audio API. Supported browser formats depend on the browser.

Audio below -50 dBFS is treated as silence and returns empty text without touching the model. Audible input shorter than one second is centred in silence, so a clipped word keeps context on both sides.

A session commits pending audio at a pause, after 12 seconds without one, or when stopped. Committed text no longer changes and its audio is released, so memory stays bounded during long recordings. msElapsed and meta.rtf cover decode work only, not download, load, audio decoding or waiting for audio.

Weights mirror

Set weightsBaseUrl to serve checkpoints from your own host:

await preloadVoice({
    weightsBaseUrl: "https://models.example.com",
});

The mirror keeps the Hugging Face layout: <base>/<repo>/resolve/main/<file>. Whisper reads GGUF weights from the handy-computer repositories and text files from the openai repositories. Support Range requests to enable resumable, parallel downloads.

Text to speech

The voice library has two 48 kHz voices. Both use MOSS-TTS-Nano 100M and clone bundled reference codes, so no reference recording is downloaded. Each voice takes about 133 MB on first use. A 16 kHz preview clip is already bundled.

VoiceIdModelOutput
FemaleVoiceId.FemaleMOSS-TTS-Nano 100M48 kHz
MaleVoiceId.MaleMOSS-TTS-Nano 100M48 kHz

The text itself selects one of the 20 MOSS languages: Arabic, Chinese, Czech, Danish, English, French, German, Greek, Hebrew, Hungarian, Italian, Japanese, Korean, Persian, Polish, Portuguese, Russian, Spanish, Swedish and Turkish.

import {
    VoiceId,
    getVoiceSample,
    isVoiceCached,
    listVoices,
    loadVoice,
    removeVoice,
    synthesize,
} from "@degreesign/voice";

const voices = listVoices();
const sample = getVoiceSample(VoiceId.Female);
console.log(voices.map((voice) => voice.label), sample.sampleRate);

if (!(await isVoiceCached(VoiceId.Female))) {
    await loadVoice({
        voice: VoiceId.Female,
        onProgress: ({ phase, ratio }) => console.log(phase, ratio),
    });
}

const audio = await synthesize({
    voice: VoiceId.Female,
    text: "Hello from @degreesign/voice.",
});
// audio.pcm is mono Float32 at audio.sampleRate.

await removeVoice(VoiceId.Female);
FunctionDescription
listVoices()Every voice spec, in library order.
getVoiceSpec(voice)One spec with files, license and preview line.
getVoiceSample(voice)Bundled WAV bytes and clip rate.
isVoiceCached(voice)True when every voice file is stored.
loadVoice(options)Download the voice files. Accepts weightsBaseUrl.
prepareVoice(options)Download and parse the voice without speaking.
synthesize(options)Render text and return VoiceAudio.
removeVoice(voice)Remove the cached voice files.

synthesize accepts optional pitch in semitones, rate and linear gain. Pitch and rate apply after synthesis for every voice. The MOSS voices also accept seed and temperature. Gain defaults to two, pitch to zero and rate to one. The parsed weights stay resident until another voice replaces them.

Voice cloning

cloneVoice records the browser microphone for three seconds and encodes the recording with the MOSS audio tokenizer. The first call downloads the 45 MB encoder, then keeps it resident.

import { cloneVoice, synthesize, VoiceId } from "@degreesign/voice";

const clone = await cloneVoice({ durationMs: 3000 });
const audio = await synthesize({
    voice: VoiceId.Female,
    text: "This is the recorded voice.",
    reference: clone.codes,
});

A VoiceClone holds codes, the frame count and the native 48 kHz rate. Store the codes on the page to reuse the same voice without recording again:

const audio = await synthesize({
    voice: VoiceId.Male,
    text: "The stored reference still speaks.",
    reference: clone.codes,
});

Pass pcm with sampleRate to encode a recording you already have. The encoder resamples to 48 kHz and keeps at most four seconds. A reference shorter than one second is rejected. onProgress follows the encoder download.

Dictation

@degreesign/voice/dictation is a headless controller for live microphone dictation. It owns the worker, capture, pause state, model progress and partial text. The page keeps its own markup and copy.

Copy the prebuilt worker next to the page, or serve it from a same origin path:

cp node_modules/@degreesign/voice/dist/browser/dictation.worker.js \
  public/dictation.worker.js
import {
    createDictation,
    DictationState,
    Language,
} from "@degreesign/voice/dictation";

const dictation = createDictation({
    workerUrl: "/dictation.worker.js",
    language: Language.en,
    onState: (state, { modelReady, ratio }) => {
        status.textContent = state === DictationState.recording && modelReady
            ? "Listening"
            : `${state} ${Math.round(ratio * 100)}%`;
    },
    onText: (text) => {
        input.value = text;
    },
    onError: ({ source, message }) => console.error(source, message),
});

micButton.onclick = () => void dictation.start();
pauseButton.onclick = () => dictation.togglePause();
doneButton.onclick = () => void dictation.finish();
cancelButton.onclick = () => dictation.cancel();

Main options are workerUrl or workerFactory, model, language, quality, threads, timestamps, updateIntervalMs, partialIntervalMs, onState, onText, onFrequency, onError and an advanced capture override. One controller belongs to one input or component. Controllers on a page share one worker, and starting one cancels the active one. A cancel emits onText("", true) so the page can restore its base text.

The states are idle, requesting, recording, paused, loading and working. The controller methods are start, pause, resume, togglePause, finish, cancel, reset, active, state and destroy.

Demo

Open the live demo to try both directions in a browser before installing. It loads @degreesign/voice from jsDelivr and lets you:

  • Record from the microphone or pick an audio file and read the transcript.
  • Pick a voice, tune pitch, rate, gain, temperature and seed, then listen.
  • Record a reference and speak the text with the cloned voice.

The demo for speech to text uses Quality.Low, a 59 MB download. The demo source is docs/index.html. Run node test/example/serve.cjs, then open http://127.0.0.1:17544/docs/index.html to serve it locally.

Models

IdCheckpointLanguages
ModelId.WhisperBaseWhisper basemultilingual
ModelId.WhisperBase + Language.enWhisper base.enEnglish

Whisper base uses handy-computer/whisper-base-gguf with the openai/whisper-base text files. The English checkpoint uses handy-computer/whisper-base.en-gguf with openai/whisper-base.en. Both share one ModelId, one usage record and one meta.model.

Weights are published as F16, Q8_0 and Q4_K_M. Quality.High is the default.

QualityWeightsWhisper baseWhisper base.en
HighF16151 MB151 MB
MediumQ8_085 MB85 MB
LowQ4_K_M59 MB59 MB

Quantized files are dequantized to F32 on first access. The kernels are the same at every quality, so quality changes download size and first access cost, not the steady state decode path.

Storage and performance

Node.js stores model files through @degreesign/cache under ~/.cache/decoder. Browsers store them in IndexedDB through @degreesign/storage.

Weights are stored as 4 MiB chunks with a manifest, text files as JSON, and usage.json tracks size and last use for the disk budget. A download uses four parallel Range requests and resumes from stored chunks. A failed chunk is retried with backoff before the whole file falls back to one stream.

allowedDiskSpaceMB caps stored model bytes and defaults to 512. A model that is already cached is never pruned.

The graph runs on the CPU with WebAssembly SIMD kernels. threads defaults to ThreadCount.Four. Browsers need cross origin isolation with Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp to use the worker pool; without it the engine uses the single thread path. The F32 tensor cache holds the checkpoint once by default; weightCacheMB lowers the cap.

Architecture

The committed map at docs/architecture.html shows the files and functions as bands, from the public entry through the model family, the tensor runtime, the platform stores and the audio input. It is one offline HTML file. Open it directly and use the panel to trace a call.

Licensing

The runtime code in this repository is MIT licensed. Whisper weights and token data are Apache-2.0. The MOSS TTS weights, tokenizer and reference codes are Apache-2.0. The bundled MP3 decoder is CC0 and the minimp3-wasm bindings are Apache-2.0. See THIRD_PARTIES.md and LICENSE.

版本与发布记录

@degreesign/voice 共发布 22 个版本,首次发布于 2026/10/20;过去 12 个月发布了 22 个版本。

版本发布日期距今
2.4.2latest2026/11/092 天前
2.4.12026/11/092 天前
2.4.02026/11/092 天前
2.3.22026/11/092 天前
2.3.12026/11/092 天前
2.3.02026/11/092 天前

关于 @degreesign/voice 的常见问题

@degreesign/voice 是什么?

Offline dictation, speech to text and text to speech (TTS) for web and Node.js

如何安装 @degreesign/voice?

在项目目录运行 npm install @degreesign/voice,或使用 pnpm add / yarn add / bun add。

@degreesign/voice 支持 TypeScript 吗?

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

@degreesign/voice 支持 ESM 还是 CommonJS?

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

@degreesign/voice 需要什么版本的 Node.js?

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

@degreesign/voice 可以商用吗?

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

@degreesign/voice 的下载量有多少?

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

@degreesign/voice 还在维护吗?

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