Shiny.Speech 2.1.0

Prefix Reserved
dotnet add package Shiny.Speech --version 2.1.0
                    
NuGet\Install-Package Shiny.Speech -Version 2.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Shiny.Speech" Version="2.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Shiny.Speech" Version="2.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Shiny.Speech" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Shiny.Speech --version 2.1.0
                    
#r "nuget: Shiny.Speech, 2.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Shiny.Speech@2.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Shiny.Speech&version=2.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Shiny.Speech&version=2.1.0
                    
Install as a Cake Tool

Shiny.Speech

Cross-platform speech services for .NET MAUI and Blazor WebAssembly — speech-to-text, text-to-speech, audio capture, and audio playback with pluggable cloud providers.

Libraries

Package Description Targets
Shiny.Speech Core interfaces + native platform implementations (STT, TTS, audio capture, audio playback) net10.0-ios, net10.0-android, net10.0-windows, net10.0 (Browser/WASM)
Shiny.Speech.Cloud Cloud provider abstractions + CloudSpeechToText / CloudTextToSpeech implementations net10.0
Shiny.Speech.Azure Azure AI Speech provider (STT + TTS) net10.0
Shiny.Speech.ElevenLabs ElevenLabs provider (STT + TTS) net10.0

Getting Started

Native Platform Speech

Use the built-in OS speech engines — no cloud account needed. Works on MAUI (iOS, Android, Windows) and Blazor WebAssembly (via Web Speech API).

builder.Services.AddSpeechServices();
// Registers: ISpeechToTextService, ITextToSpeechService, IAudioSource, IAudioPlayer
// On Browser/WASM: auto-detected via OperatingSystem.IsBrowser()

Azure AI Speech (Cloud)

builder.Services.AddAzureSpeech("your-subscription-key", "your-region");

ElevenLabs (Cloud)

// Register both STT (Scribe) and TTS:
builder.Services.AddElevenLabsSpeech("your-api-key");

// Or pick one:
builder.Services.AddElevenLabsSpeechToText("your-api-key");
builder.Services.AddElevenLabsTextToSpeech("your-api-key");

Usage

Text-to-Speech

public class MyService(ITextToSpeechService tts)
{
    public async Task SpeakAsync()
    {
        await tts.SpeakAsync("Hello world!", new TextToSpeechOptions
        {
            SpeechRate = 1.2f,
            Pitch = 1.0f,
            Volume = 0.8f
        });
    }
}

VU Meter (Audio Level)

ITextToSpeechService and IAudioPlayer expose an AudioLevelChanged event that fires periodically while audio is playing with a normalized 0.01.0 RMS level. Check IsPlayerAnalysisSupported before binding UI.

if (tts.IsPlayerAnalysisSupported)
    tts.AudioLevelChanged += (s, level) =>
        MainThread.BeginInvokeOnMainThread(() => MyVuBar.Progress = level);
Surface iOS / macOS Android Windows Browser
Native ITextToSpeechService
Cloud ITextToSpeechService (Azure / OpenAI / ElevenLabs / custom)
IAudioPlayer (generic playback)

On Apple platforms, native TTS routes AVSpeechSynthesizer through AVAudioEngine + AVAudioPlayerNode so audio levels can be tapped. The engine is created lazily on first speak and kept warm across utterances — only the first utterance pays ~50–150 ms additional startup.

Speech-to-Text

The ISpeechToTextService uses a Start/Stop model with events, allowing multiple consumers to observe recognition results simultaneously.

public class MyService(ISpeechToTextService stt) : IDisposable
{
    public async Task StartListeningAsync()
    {
        var access = await stt.RequestAccess();
        if (access != AccessState.Available)
            return;

        // Subscribe to events
        stt.ResultReceived += OnResult;
        stt.KeywordHeard += OnKeyword;
        stt.Error += OnError;

        // Start listening (throws if already listening)
        await stt.Start(new SpeechRecognitionOptions
        {
            Culture = CultureInfo.GetCultureInfo("en-US"),
            SilenceTimeout = TimeSpan.FromSeconds(3),
            Keywords = ["Yes", "No", "Maybe"]
        });
    }

    public async Task StopListeningAsync()
    {
        await stt.Stop(); // no-op if not listening
        stt.ResultReceived -= OnResult;
        stt.KeywordHeard -= OnKeyword;
        stt.Error -= OnError;
    }

    void OnResult(object? sender, SpeechRecognitionResult result)
        => Console.WriteLine($"[{(result.IsFinal ? "FINAL" : "partial")}] {result.Text}");

    void OnKeyword(object? sender, string keyword)
        => Console.WriteLine($"Keyword detected: {keyword}");

    void OnError(object? sender, SpeechRecognitionError error)
        => Console.WriteLine($"Error: {error.Message}");

    public void Dispose() => StopListeningAsync().GetAwaiter().GetResult();
}

Extension Methods (Convenience)

// Simple: wait for silence (starts and stops automatically)
var text = await stt.ListenUntilSilence(cancellationToken: ct);

// Wake word: "Hey Computer, do something" → returns "do something"
var command = await stt.StatementAfterKeyword(["Hey Computer"], cancellationToken: ct);

// Wait for a specific keyword (with optional timeout)
var answer = await stt.WaitListenForKeywords(["Yes", "No"], timeout: TimeSpan.FromSeconds(30), cancellationToken: ct);

// Continuous keyword stream
await foreach (var keyword in stt.ListenForKeywords(["Up", "Down", "Left", "Right"], cancellationToken: ct))
{
    Console.WriteLine($"Direction: {keyword}");
}

Custom Cloud Provider

Implement ISpeechToTextProvider and/or ITextToSpeechProvider from Shiny.Speech.Cloud:

public class MyCloudSttProvider : ISpeechToTextProvider
{
    public event EventHandler<SpeechRecognitionError>? Error;

    public async IAsyncEnumerable<SpeechRecognitionResult> RecognizeAsync(
        Stream audioStream,
        SpeechRecognitionOptions? options = null,
        CancellationToken cancellationToken = default)
    {
        // Read PCM audio from audioStream (16kHz, 16-bit, mono)
        // Yield recognition results...

        // For continuous recognition, surface non-fatal errors (e.g. a transient
        // network blip between chunked requests) without aborting the session:
        // Error?.Invoke(this, new SpeechRecognitionError("network blip", ex));
    }
}

// Register:
builder.Services.AddCloudSpeechToText<MyCloudSttProvider>();

CloudSpeechToText subscribes to the provider's Error event and forwards it to the service-level ISpeechToTextService.Error, so app code only needs to wire one handler.

Platform Requirements

Platform STT TTS Audio Capture Audio Playback
iOS 15+ (incl. CarPlay) SFSpeechRecognizer AVSpeechSynthesizer AVAudioEngine AVAudioPlayer
Android 26+ SpeechRecognizer Android TTS AudioRecord MediaPlayer
Windows 10 19041+ Windows.Media.SpeechRecognition Windows.Media.SpeechSynthesis AudioGraph MediaPlayer
Browser (WASM) Web Speech API (SpeechRecognition) Web Speech API (SpeechSynthesis) Web Audio API (getUserMedia + ScriptProcessorNode) HTML5 Audio

Browser (Blazor WebAssembly)

No manifest changes needed — the browser prompts the user for microphone access automatically. Include the JS interop module in your index.html:

<script src="shiny-speech.js"></script>

Note: IAudioSource captures raw PCM audio in the browser using the Web Audio API (getUserMedia + ScriptProcessorNode), downsampled to 16kHz 16-bit mono. Audio playback (IAudioPlayer) accepts any browser-supported format via a base64 data URL.

iOS/macOS

Add to Info.plist:

<key>NSSpeechRecognitionUsageDescription</key>
<string>Speech recognition description</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone description</string>

Android

Add to AndroidManifest.xml:

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

MODIFY_AUDIO_SETTINGS is required for the TTS audio-level Visualizer and for the native STT beep suppression.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-android36.0 is compatible.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-ios26.0 is compatible.  net10.0-maccatalyst was computed.  net10.0-maccatalyst26.0 is compatible.  net10.0-macos was computed.  net10.0-macos26.0 is compatible.  net10.0-tvos was computed.  net10.0-windows was computed.  net10.0-windows10.0.19041 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (4)

Showing the top 4 NuGet packages that depend on Shiny.Speech:

Package Downloads
Shiny.Speech.Cloud

Shiny Speech - Cross-platform speech-to-text and text-to-speech for .NET MAUI

Shiny.AiConversation

Shiny AI Conversation - Voice chat with your AI in your .NET apps

Shiny.Blazor.Controls.SpeechAddins

Shiny Controls for Blazor and .NET MAUI - Makes your UI shine beautifully with complex controls for free

Shiny.Maui.Controls.SpeechAddins

Shiny Controls for Blazor and .NET MAUI - Makes your UI shine beautifully with complex controls for free

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.1.0 407 5/22/2026
2.1.0-beta-0004 140 5/22/2026
2.1.0-beta-0003 162 5/21/2026
2.1.0-beta-0002 134 5/21/2026
2.0.0 331 5/13/2026
2.0.0-beta-0002 184 5/13/2026
1.2.1 145 5/12/2026
1.2.1-beta-0002 141 5/11/2026
1.2.0 362 5/6/2026
1.2.0-beta-0002 118 5/6/2026
1.1.2 123 5/6/2026
1.1.2-beta-0002 121 5/6/2026
1.1.1 140 5/5/2026
1.1.1-beta-0002 123 5/5/2026
1.1.0 176 5/4/2026
1.0.0 134 5/3/2026
1.0.0-beta-0007 126 5/3/2026
1.0.0-beta-0006 124 5/3/2026