npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tauri-plugin-tts-api

v0.2.0

Published

Native text-to-speech API for Tauri with multi-language and voice selection

Readme

Tauri Plugin TTS (Text-to-Speech)

Crates.io npm Documentation Tauri 2.0 License: MIT

Cross-platform native Text-to-Speech (TTS) plugin for Tauri 2.x using OS synthesizers: WinRT (Windows), AVSpeechSynthesizer (macOS/iOS), speech-dispatcher (Linux), and TextToSpeech (Android).


📑 Table of Contents


✨ Features

  • 🔊 Native Voice Synthesis: 100% offline, zero-latency speech synthesis powered by OS engines.
  • 🎛️ Normalized Controls: Unified rate, pitch, and volume scales across all platforms.
  • 🗣️ Voice Enumeration: Discover installed voices, filter by locale, and preview samples.
  • 📋 Utterance Queuing: Choose between flush (interrupt) and add (queue sequentially).
  • 📡 Lifecycle Events: Track speech:start, speech:finish, speech:cancel, and speech:error.
  • 📱 Mobile Background Audio: Continue speaking when the screen locks or app is minimized.

📊 Platform Matrix

| Platform | Native Engine | Pause/Resume | Background Audio | | :--- | :--- | :---: | :---: | | Windows | WinRT (SpeechSynthesis) | — | — | | macOS | AVSpeechSynthesizer | — | — | | Linux | speech-dispatcher | — | — | | iOS | AVSpeechSynthesizer | ✅ | ✅ | | Android | android.speech.tts.TextToSpeech | — | ✅ |


📦 Installation

# Rust
cargo add tauri-plugin-tts

# JavaScript / TypeScript
npm install tauri-plugin-tts-api
# or: pnpm add / yarn add / bun add tauri-plugin-tts-api

⚙️ Setup

1. Register Plugin in Rust

fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_tts::init())
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

2. Configure Capabilities

Add the plugin permission to src-tauri/capabilities/default.json:

{
  "permissions": [
    "core:default",
    "tts:default"
  ]
}

[!NOTE] Android <queries> declarations for TTS services are merged automatically by the plugin during build.


💻 Usage

import {
  speak,
  stop,
  getVoices,
  previewVoice,
  onSpeechEvent,
} from "tauri-plugin-tts-api";

// 1. Basic speech
await speak({ text: "Hello, world!" });

// 2. Custom rate, pitch, language, and queue mode
await speak({
  text: "Olá, mundo!",
  language: "pt-BR",
  rate: 1.0,   // 0.1 to 4.0 (1.0 = normal)
  pitch: 1.1,  // 0.5 to 2.0 (1.0 = normal)
  volume: 1.0, // 0.0 to 1.0
  queueMode: "flush", // 'flush' (interrupt) | 'add' (queue)
});

// 3. Discover and preview voices
const voices = await getVoices("en");
if (voices.length > 0) {
  const preview = await previewVoice({ voiceId: voices[0].id });
  // success is false when the voice is not installed - the preview is silent otherwise
  if (!preview.success) console.warn(preview.warning);
}

// 4. Listen for completion event
const unlisten = await onSpeechEvent("speech:finish", (e) => {
  console.log("Finished utterance:", e.id);
});

📚 API Reference

| Function | Parameters | Return Type | Description | | :--- | :--- | :--- | :--- | | speak(options) | SpeakOptions | Promise<SpeakResponse> | Synthesizes text. Resolves once the utterance is accepted by the engine. | | stop() | none | Promise<void> | Halts active speech synthesis. | | getVoices(language?) | string? | Promise<Voice[]> | Returns installed voices, filtered by locale prefix. | | previewVoice(options) | PreviewVoiceOptions | Promise<SpeakResponse> | Speaks a short sample using voice ID. | | isSpeaking() | none | Promise<boolean> | Returns true if synthesizer is speaking. | | isInitialized() | none | Promise<{ initialized, voiceCount }> | Checks if mobile synthesizer is initialized. | | pauseSpeaking() / resumeSpeaking() | none | Promise<PauseResumeResponse> | Pauses / resumes speech (iOS only). | | setBackgroundBehavior(opts) | { continueInBackground } | Promise<void> | Controls lock-screen behavior (Mobile). | | onSpeechEvent(type, handler) | SpeechEventType, Function | Promise<UnlistenFn> | Subscribes to speech events. |

SpeakResponse

speak() and previewVoice() both resolve with:

| Field | Type | Description | | :--- | :--- | :--- | | success | boolean | false when nothing will be spoken — currently only previewVoice() with a voice that is not installed. | | warning | string? | Set when the requested voiceId or language was unavailable and the system default was used instead. Speech still happens, so check this if the user picked a specific voice. | | utteranceId | string? | Matches the id on this utterance's lifecycle events. Read it to correlate a call with its events instead of racing speech:start. |

const { warning, utteranceId } = await speak({ text: "Hi", voiceId: savedId });
if (warning) console.warn(warning); // e.g. voice uninstalled since it was saved

Speech Events

Subscribe with onSpeechEvent(type, handler). Every payload carries eventType, and id whenever the platform can attribute the event to a specific utterance.

| Event | When | Platforms | | :--- | :--- | :---: | | speech:start | The engine began speaking this utterance. | All | | speech:finish | The utterance completed on its own. | All | | speech:cancel | The utterance was stopped — by stop(), or by a flush that replaced it. Carries interrupted on Android. | All | | speech:error | Synthesis failed. Carries error. | All | | speech:pause | Speech paused. Carries reason for automatic pauses (route_change, interruption_ended). | iOS | | speech:resume | Speech resumed after a pause. | iOS | | speech:interrupted | A phone call or another app took the audio session. Carries reason on Android. | iOS, Android | | speech:backgroundPause | The app backgrounded while continueInBackground was false. | iOS, Android |

[!NOTE] speech:cancel and speech:finish are mutually exclusive for a given id, so a state machine can treat either one as terminal.

Rate, Pitch and Volume

1.0 always means "this platform's normal". The plugin maps that onto each backend's own scale, which differ wildly (AVFoundation rate is 0.01.0 with 0.5 normal, WinRT is 0.56.0 with 1.0 normal, speech-dispatcher is -100100 with 0 normal), so the same value sounds equivalent everywhere.

| Parameter | Range | Normal | | :--- | :--- | :--- | | rate | 0.14.0 | 1.0 | | pitch | 0.52.0 | 1.0 | | volume | 0.01.0 | 1.0 |

Out-of-range values are clamped rather than rejected. text is limited to MAX_TEXT_LENGTH (10,000 UTF-8 bytes), exported from the JavaScript API.


🛠️ Troubleshooting

  • Linux "Speech Dispatcher not available": Install the daemon via sudo apt install speech-dispatcher or sudo dnf install speech-dispatcher.
  • Android Missing Voices: Download language packs in Settings → Accessibility → Text-to-Speech Output → Google TTS.
  • iOS Background Mode: To continue speaking with the screen locked, add UIBackgroundModes with audio in Info.plist.
  • A saved voiceId speaks in the wrong voice: the voice was uninstalled. speak() falls back to the system default and reports it in warning — re-run getVoices() and let the user pick again.
  • Android "TTS engine failed to initialize": no text-to-speech engine is installed. Install one from Settings → Accessibility → Text-to-Speech Output; isInitialized() reports false until then.

🌍 Used By

See USED_BY.md for projects and organizations using this plugin in production.


📄 License

MIT © Breno Gonzaga