Skip to content

Core API

Use these APIs to build a custom framework integration or control audio capture and text editing directly. Most React apps should use @voiceinput/react instead.

The package provides a recording session, a browser audio source, and a text engine that inserts speech while preserving unrelated edits.

Terminal window
npm install @voiceinput/core

createVoiceInputSession coordinates option validation, two-phase audio startup, provider streaming, duration limits, normalized state, and cleanup.

import {
createBrowserAudioSource,
createVoiceInputSession,
} from "@voiceinput/core";
const session = createVoiceInputSession({
provider,
audioSource: createBrowserAudioSource(),
language: "en-CA",
vocabulary: ["VoiceInput"],
endpointing: { silenceMs: 650 },
connectionTimeoutMs: 15_000,
finalizationTimeoutMs: 15_000,
stopWhenHidden: true,
maxDurationMs: 5 * 60 * 1_000,
});
const unsubscribe = session.subscribe((event) => {
console.log(event.type);
});
await session.start();
await session.stop();
unsubscribe();

Actions are start, stop, cancel, and toggle. stop is graceful and preserves visible dictated text; cancel aborts immediately. The default maximum duration is five minutes, with one warning 30 seconds before cutoff.

Once microphone audio is acquired, provider connection and audio activation must complete within connectionTimeoutMs (15 seconds by default). Expiry aborts the full run, releases acquired audio, reports a retryable network-error, and permits a fresh start().

Audio/provider shutdown has a finalizationTimeoutMs budget (15 seconds by default), including the final audio flush. If finalization times out, the session releases resources, promotes the last interim text, runs any configured text transform under its separate transformTimeoutMs budget, returns to idle, and emits stop with reason finalization-timeout. The snapshot finalTranscript includes that preserved fallback; it is not a guarantee that the provider finalized every phrase. Errors while flushing audio or finalizing after Stop preserve text in the same way: the session completes the text engine, reports an error event, keeps the error in its snapshot, and returns to idle with the original stop reason. Errors during recording still terminate the run.

stopWhenHidden defaults to true: switching tabs or apps stops recording. Set it to false for desktop workflows that need background dictation. Page hide/freeze still stops the session, and browsers may interrupt microphone capture regardless of this option.

The immutable snapshot exposes status, transcript, interimTranscript, finalTranscript, and error. Status values are idle, requesting-permission, connecting, listening, stopping, processing, and error. Stop reasons are user, max-duration, replaced, max-length, target-unavailable, backgrounded, and finalization-timeout. A text-limit event reports a constrained insertion.

Final parts use the same boundary policy as field insertion: outer provider whitespace is normalized, word boundaries are added when needed, punctuation is kept adjacent, empty parts are ignored, and consecutive Han, Hiragana, and Katakana parts are not separated. finalTranscript is cumulative; transcript adds the current normalized interim part. Session final events expose the raw provider part as text and the cumulative normalized value as transcript.

VoiceAudioSource.prepare({ sampleRate, abortSignal, onAcquired }) returns a PreparedVoiceAudioSource with a PCM16 stream plus start, stop, and abort. Preparation can request permission. Once acquired, audio is buffered while the provider connects; delivery to the provider starts after connection. Custom sources must call the optional onAcquired() callback as soon as they hold live audio resources; this starts the connection deadline even if later preparation is still pending.

createBrowserAudioSource supplies the production browser implementation:

const audioSource = createBrowserAudioSource({
constraints: { echoCancellation: true },
frameDurationMs: 20,
// Optional: use a self-hosted module under strict CSP.
workletModuleUrl: "/voiceinput-worklet.js",
});

It captures mono audio through an AudioWorklet, resamples to the adapter’s declared rate, emits Int16Array frames, resumes suspended Safari contexts, and tears down tracks, nodes, and contexts on every terminal path.

The default worklet uses a temporary Blob URL. For a policy without blob:, write VOICE_INPUT_AUDIO_WORKLET_SOURCE to a same-origin JavaScript asset at build time and pass its URL as workletModuleUrl. See the Content Security Policy guide for the copy script and exact directives.

Use getBrowserVoiceInputSupport() for a capability report. It checks secure context, media devices, getUserMedia, AudioContext, and AudioWorklet. normalizeBrowserAudioError(error) converts browser failures into VoiceInputError.

createVoiceInputTextEngine inserts transcript text without taking ownership of unrelated user content:

let value = textarea.value;
const engine = createVoiceInputTextEngine({
controlled: {
getValue: () => value,
onValueChange: (nextValue) => {
value = nextValue;
},
},
interimBehavior: "inline",
transformTranscript: async (text) => text.trim(),
transformTimeoutMs: 10_000,
});
engine.setTarget(textarea);
engine.captureSelection();
engine.begin();
engine.applyInterim("draft");
engine.applyFinal("final text");
const completion = engine.complete();
await completion.result;

Supported targets are <textarea> and <input> types text, search, url, and tel. The engine tracks provisional, finalized, frozen, and transformed spans. If a user edits or moves the caret, it freezes text it can no longer prove ownership of and re-anchors later speech. Uncontrolled targets receive a bubbling native input event.

For a controlled target, pass each committed application value to reconcileControlledValue.

interimBehavior: "inline" inserts replaceable interim text. "expose" keeps interim text out of the field while still reporting it in snapshots.

Session and errors:

  • createVoiceInputSession
  • getVoiceInputErrorMessage
  • VoiceInputSession, CreateVoiceInputSessionOptions
  • VoiceInputSnapshot, VoiceInputStatus, VoiceInputSessionEvent
  • VoiceInputStopReason
  • VoiceAudioSource, VoiceAudioSourcePrepareOptions
  • PreparedVoiceAudioSource
  • VoiceInputError, VoiceInputErrorCode, VoiceInputErrorOptions

Browser audio:

  • VOICE_INPUT_AUDIO_WORKLET_SOURCE
  • createBrowserAudioSource, CreateBrowserAudioSourceOptions
  • getBrowserVoiceInputSupport
  • BrowserVoiceInputSupport, BrowserVoiceInputCapability
  • normalizeBrowserAudioError

Text ownership:

  • createVoiceInputTextEngine, CreateVoiceInputTextEngineOptions
  • VoiceInputTextEngine, VoiceInputTextEngineSnapshot
  • VoiceInputTextTarget
  • VoiceInputTextSelection
  • VoiceInputTextSpan, VoiceInputTextSpanState
  • VoiceInputControlledTextBinding
  • VoiceInputInterimBehavior
  • VoiceInputTransformTranscript
  • VoiceInputTextCompletion, VoiceInputTextLimit
  • VoiceInputTextEngineEvent, VoiceInputTextWritableChange

The session accepts any VoiceInputProviderV1. Provider-specific models, tokens, and settings belong in adapter factories, not core options. See the @voiceinput/provider guide to implement an adapter.

The text engine exposes undo(), redo(), isWritable(), and subscribe(). Subscribers receive writable-change, text-limit, target-unavailable, and reset events. See editing and undo for the field behavior and @voiceinput/provider for segment identity and audio backpressure.