Skip to content
SPEECH-TO-TEXT, NO STRINGS ATTACHED

Whisper, rebuilt
in pure Rust.

A real in-process Whisper engine — ggml parser, log-mel frontend, encoder, decoder, word-level timestamps — with native four-speaker diarization. No FFI. No Python. No subprocess. One binary that answers to agents first.

Install in one line Try it in your browser Source on GitHub
100% Rust · zero FFI 2.99× faster than whisper.cpp* 4-speaker native diarization NDJSON robot mode 13 exact error codes
franken_whisper illustration
THE NATIVE ENGINE

The model runs in your process.

Most "Rust Whisper" projects wrap whisper.cpp over FFI or shell out to Python. franken_whisper implements the entire forward pass in safe Rust on FrankenTorch CPU kernels.

FRONTEND

ggml model parser, log-mel spectrogram, and the exact BPE tokenizer — byte-for-byte compatible with whisper.cpp's model files. Drop a ggml-*.bin in place and transcribe.

TRANSFORMER

Encoder and decoder with whisper.cpp's full timestamp-rule suite, greedy and beam decode, int8 quantization under a calibrated quality policy, and token-merging (ToMe) on the encoder — the lever behind the 2.99× row.

WORD TIMESTAMPS

Cross-attention DTW word alignment — real per-word times from the model's own attention, not linear interpolation. Metal acceleration engages automatically on macOS.

MEASURED, NOT MARKETED

Faster, with receipts.

Every row below ran the real whisper-cli incumbent side-by-side in the same harness invocation, at matched greedy decode settings, with paired A/A null controls.

Model / workloadModeResultTranscript
large-v3-turbo, 124.5 s / 5 windowswhole job, no timestamps2.99× fasterWER 0.011
tiny.en, 124.5 s / 5 windowstranscribe only1.52× fasterWER 0.0000
tiny.en, 300 s / 10 windowstranscribe only1.51× fasterWER 0.0000

*Matched-greedy CPU comparisons (whisper-cli -bs 1 -bo 1), 32 threads, quiet x86-64 Threadripper host, order-alternating pairs, per-engine A/A controls, CI95 gates. A result is admitted only when both same-invocation A/A null medians sit in [0.98, 1.02] and the effect clears twice the widest null-CI edge. The full measurement record, including every rejected optimization, lives in docs/PERF_LEDGER.md.

WHO SAID WHAT

Diarization that reaches the transcript.

A memory-safe Rust port of NVIDIA's Sortformer runs in-process by default, tracking up to four overlapping speakers. Turn evidence is fused into every segment, and speaker changes snap to the transcript's own punctuation.

ONE PAYLOAD, ALREADY JOINED

Every segment carries its speaker, and speaker_segments gives you merged same-speaker runs with byte-faithful text and per-run confidence — no client-side join required.

{ "speaker": "SPEAKER_00", "start_sec": 45.8, "end_sec": 51.2, "text": "Hey Jeff. How are you doing?", "segment_count": 6, "speaker_confidence": 0.93 }
HONEST ABOUT ITS LIMITS

Four lanes are a hard capacity boundary, not proof a recording has four speakers — the output says so explicitly (development_uncertified). Untimed audio stays unknown rather than guessed. Recordings past the boundary fall back to a bounded acoustic engine, and the report names which engine ran and why.

BUILT FOR MACHINES

Agents are the primary interface.

Robot mode streams sequenced, schema-versioned NDJSON. Errors are thirteen exact codes, not prose. Even argument-parsing failures come back as one parseable JSON object.

# one JSON object per line, schema 1.0.0 $ fw robot run --input meeting.mp3 --backend auto {"event":"run_start","schema_version":"1.0.0", ...} {"event":"stage","stage":"normalize","code":"normalize.ok", ...} {"event":"stage","stage":"backend","code":"backend.ok", ...} {"event":"stage","stage":"diarize","code":"diarize.ok", ...} {"event":"run_complete","transcript":"...","segments":[...],"warnings":[]}
fw robot triagelive machine-readable starting point
fw capabilities --jsonexact error codes, exit codes, schemas
fw doctor --jsonmodel cache and readiness preflight

Dropped decode windows, ignored flags, and every non-fatal anomaly surface in warnings and structured events — nothing important is stderr-only. Runs persist to SQLite with replay envelopes; identical input and engine must produce an identical output hash.

TEN COMPOSABLE STAGES

A pipeline, not a monolith.

Each stage has an independent time budget and emits paired start/ok events. Skipped stages say so — agents can tell "not needed" from "failed".

ingestfile · stdin · microphone
normalizein-process decode: MP3/AAC/FLAC/OGG
vadoptional silence skip
separateoptional vocal isolation
backendnative engine or bridges
accelerateconfidence normalization
alignforced alignment
punctuateopt-in text normalization
diarizenative Sortformer default
persistSQLite + JSONL replay
EVIDENCE OVER VIBES

Every claim has a paper trail.

THE GRAVEYARD

A 26,000-line negative-evidence ledger records every optimization that didn't work — with the measurement that killed it and the predicate under which it may be retried. Failed ideas are data, not embarrassments.

BYTE-EXACT OR LEDGERED

Performance levers must prove bit-identical output before their speed is even measured. The few accepted divergences live in DISCREPANCIES.md with kill switches and review dates.

REPLAY PACKS

Every run produces SHA-256 replay envelopes; a four-artifact replay pack reproduces any run byte-for-byte on another machine. Drift between engine versions is detected, never assumed away.

SIXTY SECONDS TO FIRST TRANSCRIPT

Install it.

SHA-256-verified binaries for Linux (x86_64/arm64), macOS (Intel/Apple Silicon), and Windows. The installer also provisions both pinned model packages (~2.1 GB) so the default pipeline works offline.

ONE LINE
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/franken_whisper/main/install.sh" | bash
HOMEBREW
brew install dicklesworthstone/tap/franken-whisper fw pull all # one-time model download
CARGO
cargo install franken_whisper fw pull all
THEN
# transcribe + diarize a meeting, full JSON report fw transcribe --input meeting.mp3 --json # transcript only fw transcribe --input meeting.mp3 --no-diarize # stream NDJSON events for your agent fw robot run --input meeting.mp3 --backend auto