loading

Check support

IsSupported

Returns true when the runtime exposes a SpeechRecognition implementation - standard or webkit-prefixed. The Butil script resolves both, so you never have to feature-detect the prefix yourself.

C#
@inject Bit.Butil.SpeechRecognition speechRecognition

var isSupported = await speechRecognition.IsSupported();
Live sample
support output
Results will appear here when you interact with the samples.

Start a session

Start

Start begins listening and returns an IAsyncDisposable handle. Options select the language, whether recognition keeps running across pauses (Continuous), whether interim non-final transcripts are reported (InterimResults) and how many alternatives to surface. Each result arrives in the onResult callback as a SpeechRecognitionResult with Transcript, Confidence and IsFinal.

C#
await using var session = await speechRecognition.Start(
    new SpeechRecognitionOptions
    {
        Lang = "en-US",
        Continuous = true,
        InterimResults = true,
        MaxAlternatives = 1,
    },
    onResult: result =>
    {
        // result.Transcript, result.Confidence (0-1), result.IsFinal
    },
    onError: message => { /* mic denied, no speech, network, ... */ },
    onEnd: () => { /* engine stopped listening */ });
Live sample
Language (BCP-47)
Max alternatives
session output
Results will appear here when you interact with the samples.

Stop and clean up

Stop / DisposeAsync

Disposing the handle returned by Start stops that session and detaches its callbacks - an await using block covers the common case. The service itself is IAsyncDisposable too and tears down every live session when the scope ends, but pages that hold a handle across renders should dispose it explicitly in their own DisposeAsync.

Razor
@implements IAsyncDisposable

@code {
    private IAsyncDisposable? session;

    private async Task Stop()
    {
        if (session is null) return;
        await session.DisposeAsync(); // stops the engine and detaches callbacks
        session = null;
    }

    public async ValueTask DisposeAsync() => await Stop();
}
Warning:
Browser support is narrow Chromium-based browsers expose the engine as webkitSpeechRecognition (Butil handles the prefix) and may process audio on Google's servers; Safari supports it from 14.1 with on-device processing; Firefox ships it disabled by default. Always gate the UI behind IsSupported and design a typed-input fallback.
Note:
Microphone permission and secure context The first Start triggers the browser's microphone permission prompt and requires a secure context (https or localhost). A denied prompt surfaces through onError with a not-allowed message - handle it rather than assuming recognition began.

API reference

Member
Signature
Description
IsSupported
ValueTask<bool> IsSupported()
True when the runtime exposes a SpeechRecognition implementation (standard or webkit-prefixed).
Start
Task<IAsyncDisposable> Start(SpeechRecognitionOptions options, Action<SpeechRecognitionResult>? onResult = null, Action<string>? onError = null, Action? onEnd = null)
Starts recognition and returns a handle that stops the session when disposed. At least one callback must be provided.
Stop
ValueTask Stop(Guid id)
Stops the matching recognition session early. Equivalent to disposing the handle.
DisposeAsync
ValueTask DisposeAsync()
Stops every live session and releases the interop reference; called automatically when the scoped service is disposed.
SpeechRecognitionOptions
class { string? Lang; bool Continuous; bool InterimResults = true; int MaxAlternatives = 1; }
Session configuration: language tag, keep-listening mode, interim reporting and alternative count.
SpeechRecognitionResult
class { string Transcript; double Confidence; bool IsFinal; }
One transcript: recognized text, engine confidence in [0, 1] and whether it is final.
InvokeSpeechRecognitionResult / Error / End
void InvokeSpeechRecognition…(…)
JSInvokable interop plumbing dispatched from the Butil script - not intended for app code.
An unhandled error has occurred. Reload 🗙