tauri-plugin-stt-api
v0.2.2
Published
Speech-to-text recognition API for Tauri with multi-language support
Maintainers
Readme
Tauri Plugin STT (Speech-to-Text)
Cross-platform speech recognition for Tauri 2.x. Desktop targets use whisper.cpp via whisper-rs; mobile delegates to the native OS engine (SFSpeechRecognizer on iOS, SpeechRecognizer on Android).
Highlights
- One model, 99 languages — Whisper is multilingual; a single GGML model file handles English, Portuguese, Mandarin, and more
- No separate runtime to install —
whisper-rsbuilds whisper.cpp statically; there is no.so/.dylibto ship - Explicit model lifecycle — the host app controls when a model is downloaded;
start_listeningreturnsModelNotInstalledinstead of pulling hundreds of MB silently - Hardware acceleration — opt-in
metal/cuda/vulkanfeatures map to the matching whisper.cpp backend
Platform Matrix
| Platform | Engine | Model |
| -------- | ------------------------------------------ | ----- |
| iOS | SFSpeechRecognizer (Speech.framework) | OS |
| Android | SpeechRecognizer | OS |
| macOS | whisper.cpp via whisper-rs (Metal opt.) | GGML |
| Windows | whisper.cpp via whisper-rs (CUDA opt.) | GGML |
| Linux | whisper.cpp via whisper-rs (Vulkan opt.) | GGML |
Installation
Rust
[dependencies]
tauri-plugin-stt = { version = "0.2", features = ["metal"] } # macOS
# "cuda" for NVIDIA GPU, "vulkan" for cross-vendor GPU, omit for CPUTypeScript
npm install tauri-plugin-stt-apiRegister the plugin:
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_stt::init())
.run(tauri::generate_context!())
.unwrap();
}Permissions
{ "permissions": ["stt:default"] }Model Catalogue
| id | Size | Notes |
| ---------- | ------ | ------------- |
| tiny | 75 MB | fastest |
| base | 142 MB | balanced ⭐ |
| small | 466 MB | accurate |
| medium | 1.5 GB | very accurate |
| large-v3-turbo | 1.6 GB | fast & accurate (advanced) |
| large-v3 | 3.0 GB | most accurate |
Files are fetched from HuggingFace (ggerganov/whisper.cpp) and stored under <app_data_dir>/whisper-models/. The active model is persisted to whisper-models/active.txt.
Commands
list_models()→{ models, active, total_disk_bytes }install_model(id)— downloads and emitsstt://download-progresseventsremove_model(id)— deletes file; clears active marker if neededset_active_model(id)— sets which installed modelstart_listeningloadsunload_model()— drops the loaded Whisper context from memory; fails while listening or transcribingstart_listening({ language?, max_duration? })— begins a push-to-talk sessionstop_listening()— runs Whisper over captured audio and emits a final resultis_available()—trueonly when a model is installed and readyget_supported_languages()— curated list of UI-facing localescheck_permission()/request_permission()— microphone permission helpers
Events
stt://download-progress—{ status, modelId, model, progress, downloaded?, total? }stt://result—{ transcript, isFinal, confidence }stt://error/plugin:stt:error—{ code, message, details? }(codes follow theSttErrorCodeunion, e.g.NO_SPEECH,AUDIO_ERROR)plugin:stt:stateChange—{ state, isAvailable, language }(idleis emitted only after transcription finishes)
Behaviour Notes
- Whisper is not a streaming recogniser. The plugin buffers audio during recording and runs a single inference pass on
stop_listening. The UX is push-to-talk, not live transcription. - Audio is captured at the device default rate, downmixed to mono, then decimated to 16 kHz with nearest-neighbour. Whisper is robust enough that a higher-quality resampler makes no measurable difference.
- Inference uses
min(available_parallelism(), 4)threads — beyond that whisper.cpp shows diminishing returns, and we want headroom for the UI.
Mobile
The mobile bridges expose the same JS API surface, but list_models returns an empty list and install_model / remove_model / set_active_model / unload_model are no-ops: the OS engine has no downloadable model concept. Use is_available to gate UI — on iOS/Android it reflects actual recogniser availability.
License
MIT
