Will it run?
Hardware

Whistle: a 16.9 MB speech-to-text model that runs on CPU without GPU

By Nadia Ksiazek Clawpit staff
Whistle: a 16.9 MB speech-to-text model that runs on CPU without GPU

One model, three tasks, no external dependencies. Whistle ships as a single 16.9 MB file that handles transcription, word-level timestamps and speech embeddings entirely on device, with no GPU and no outside libraries. The engine is the same C++ runtime that powers Needle, packaged in the same `.cact` container with the same quantization (Cactus Quants), the same SIMD kernels and the same KV cache. Any device already running Needle gets Whistle in the same binary — no second install, no separate run.

The architecture reuses what already works. A log-mel frontend with a convolutional stem feeds an audio encoder; a Needle-style decoder reads it through gated cross-attention at every layer. The decoder is "laddered" like Needle: every depth from two layers up is a deployable model, selected at load time with the `--audio-depth` flag. Weights live in `whistle.cact`; the engine and platform folders sit in Cactus-Compute/needle3.

Seven languages, automatic detection, silence doesn't hallucinate. Transcription covers English, German, French, Spanish, Italian, Dutch and Polish — mono 16 kHz audio up to 30 seconds per pass. Language is auto-detected unless forced, and a silent segment returns an empty string instead of inventing a sentence. Timestamps come straight from the decoder's own attention, so an application can highlight, skip or cut at the single-word level with a probability score. Embeddings output one row every 80 ms from the encoder, enabling alignment and retrieval without ever decoding text. Keyword biasing pushes up the names, places and product terms users actually say.

Benchmarks: 86,174 samples, compared against Whisper and Moonshine. WER was measured on the full test splits (86,174 utterances) using Whisper's normalizers. Whisper and Moonshine numbers are quoted from their papers (Whisper: Tables 9, 10, 13; Moonshine: Table 3). The Whisper line is the multilingual checkpoint, not base.en. Moonshine is English-only; Whisper published no results on SPGISpeech, Earnings-22 or cleaned AMI. AMI includes empty references per the Open ASR Leaderboard convention (the Whisper figure is AMI-IHM). TED-LIUM excludes `ignore_time_segment_in_scoring` regions per the corpus protocol. FLEURS and MLS are averaged over Whistle's seven languages (MLS over six, excluding English), the same language set for every model. No test audio was found in training or validation data, verified by checksums and speaker IDs.

Speed and accuracy on an Apple M4 Pro, running in Python without heavy installs. Speed test: 10 seconds of audio on an M4 Pro, each model in its official release with defaults — Whistle at 5 beams, openai-whisper, moonshine-voice non-streaming. Time to first token measured from audio input; decode rate computed after the encoder finishes. Precision: Whistle at 2–4 bit, Whisper fp32 in memory on CPU, Moonshine int8. The package is `pip install cactus-needle` (code on GitHub). WAV at 16 kHz works with the base install; other sample rates and microphone capture need the `[mic]` extra. The API returns text, language, milliseconds to first token and tokens per second after; `word_timestamps=True` adds start, end and probability per word; `keywords=[...]` biases the search; `language="de"` forces the language. `needle.Whistle` exposes the same model as an object for `embed(audio)` or for holding a fine-tuned `.cact`. CLI: `needle whistle playground` from the mic, `needle whistle compare` puts Whistle side-by-side with Whisper and Moonshine.