loading
Warning:
Chromium, and only where the platform ships a recognizer Which in practice means Android and ChromeOS more than desktop, and behind the Experimental Web Platform features flag where it is present at all. IsSupported returning false is the common case; Detect answers with an empty array rather than throwing.

Support check

IsSupported

Returns true when the runtime exposes TextDetector. During prerender/SSR the check returns false rather than throwing, so defer it to OnAfterRenderAsync.

C#
@inject Bit.Butil.TextDetector textDetector

if (await textDetector.IsSupported() is false)
{
    // keep the manual entry field
}
Live sample
support check output
Results will appear here when you interact with the samples.

Read from the camera

Detect

Reads one frame of a video, image or canvas element. What counts as one block is the platform's decision - usually a line, sometimes a paragraph - and each block comes with the box it occupies, which is what you would draw a highlight over before asking the user to confirm.

Razor
@implements IAsyncDisposable
@inject Bit.Butil.MediaDevices mediaDevices
@inject Bit.Butil.TextDetector textDetector

<video @ref="_video" autoplay playsinline muted></video>

@code {
    private ElementReference _video;
    private MediaStreamHandle? _stream;

    private async Task Read()
    {
        _stream = await mediaDevices.GetUserMedia(
            audio: false,
            video: true,
            videoConstraints: new { facingMode = "environment" });   // rear camera on a phone

        await _stream!.AttachTo(_video);

        var blocks = await textDetector.Detect(_video);
    }

    // The camera light stays on until the stream is disposed, whatever the page does.
    public async ValueTask DisposeAsync()
    {
        if (_stream is not null) await _stream.DisposeAsync();
    }
}
Live sample
camera output
Results will appear here when you interact with the samples.

Read an image file

DetectImage

Takes an encoded image's bytes - a PNG or JPEG, not raw pixels. The decoded bitmap is released as soon as the scan finishes; it holds uncompressed pixels, which would be a real leak in a loop.

C#
var blocks = await textDetector.DetectImage(bytes, "image/jpeg");

var everything = string.Join("\n", blocks.Select(b => b.RawValue));
Live sample
file output
Results will appear here when you interact with the samples.

API reference

Member
Signature
Description
IsSupported
ValueTask<bool> IsSupported()
True when the runtime exposes TextDetector. Returns default (false) during prerender/SSR instead of throwing.
Detect
ValueTask<DetectedText[]> Detect(ElementReference element)
Reads one frame of a video, image or canvas element. Empty when there is nothing to read, or no frame yet.
DetectImage
ValueTask<DetectedText[]> DetectImage(byte[] imageBytes, string mimeType = &quot;image/png&quot;)
Reads an encoded image's bytes. Empty when the image couldn't be decoded.
DetectedText
RawValue | X | Y | Width | Height
One block of recognized text and the box it occupies, in source pixels.
An unhandled error has occurred. Reload 🗙