Whistle API
    Preparing search index...

    Prepare static assets with whistle download --out public/whistle, then pass that directory's served URL. This example is typechecked against the package exports:

    import { createWhistle } from '@tiny-stt/whistle';
    import { decodeAudio } from '@tiny-stt/whistle/audio';

    /** Transcribe an application-selected file using a complete self-hosted asset directory. */
    export async function transcribeFile(file: File, assetsUrl: string | URL, signal?: AbortSignal) {
    const whistle = await createWhistle({ assetsUrl, signal });
    try {
    const pcm = await decodeAudio(file, { signal });
    return await whistle.transcribe(pcm, { signal, wordTimestamps: true });
    } finally {
    await whistle.dispose();
    }
    }

    Decoding averages channels and resamples through Web Audio. Supported codecs depend on the browser; PCM WAV is tested. Decode errors use AUDIO_DECODE_FAILED and files over 30 seconds use AUDIO_TOO_LONG. An application that handles many files should keep one engine across files and dispose it when the UI closes.

    Calling "@tiny-stt/whistle".createWhistle without options uses the packaged worker/runtime plus the immutable official model URL. modelUrl and wasmUrl accept mirrors of those exact bytes. For fully self-hosted deployment, use assetsUrl. The deployment guide covers CSP, worker paths and caching.

    Microphone access requires HTTPS or localhost. Call the helper from a user action, not on page load. These functions are examples, not actions executed by the docs:

    import type { Whistle } from '@tiny-stt/whistle';
    import { WhistleError } from '@tiny-stt/whistle';
    import { startRecording } from '@tiny-stt/whistle/microphone';

    /** Invoke from a user action; the application owns and later disposes the supplied engine. */
    export async function beginPushToTalk(whistle: Whistle) {
    const controller = new AbortController();
    const recording = await startRecording({ maxDurationSeconds: 10, signal: controller.signal });
    // Observe result immediately, so auto-stop and cancellation are both handled.
    const completed = recording.result
    .then((pcm) => whistle.transcribe(pcm, { signal: controller.signal }))
    .catch((error: unknown) => {
    if (error instanceof WhistleError && error.code === 'ABORTED') return null;
    throw error;
    });
    return {
    completed, // Observe this promise for transcript output and non-cancellation errors.
    stop: () => recording.stop(), // Bind to pointer/key release; repeated stop is harmless.
    cancel: () => controller.abort(), // Cancels capture, decoding or active transcription.
    };
    }

    Await completed and handle non-cancellation errors. Manual stop and automatic stop settle the same recording result. Cancelling discards captured audio; tracks, timers and decoder resources are released, including tracks from late permission grants. Permission dialogs cannot themselves be dismissed by the SDK.

    Default recording length is 30 seconds. The example explicitly limits capture to 10 seconds. This is completed-clip recording, not continuous transcription.