loading
Warning:
Early - behind a flag even in Chromium WebNN is not shipped anywhere by default and its shape is still changing. Treat a supported result as an opportunity, never as a requirement.
Note:
What is wrapped, and what isn't This service wraps the part that answers "can this device run a model, and on what". Building and running a graph is not wrapped: an MLGraphBuilder program is dozens of chained operator calls over tensor handles that cannot cross interop, and marshalling each one through JSON would be slower than the inference it is meant to accelerate. Build the graph in a JS module and call into it - and use this service to decide whether loading that module is worth it at all.

Support check

IsSupported

True when the runtime exposes navigator.ml.createContext. During prerender/SSR the check returns false rather than throwing, so defer it to OnAfterRenderAsync.

C#
@inject Bit.Butil.WebNN webNN

if (await webNN.IsSupported() is false) UseWasmInferenceInstead();
Live sample
support check output
Results will appear here when you interact with the samples.

Create a context

CreateContext

The device type is a hint: the runtime may hand back another one, which is why the returned context reports what was actually created. Dispose it - a context can hold a real accelerator backend open.

C#
await using var context = await webNN.CreateContext("gpu", "high-performance");

if (context?.Info.CanBuildGraph is true)
{
    // MLGraphBuilder exists - a model could be built and run here
}
Live sample
context output
Results will appear here when you interact with the samples.

What can this backend do?

WebNNContext.GetOpSupportLimits

Which operators the backend implements, and within what limits. The same model can run on one backend and be rejected by another, so a page offering a choice of device wants to know before it builds a graph. The detail differs per operator and per backend, so it is reported as JSON rather than modelled.

Razor
@code {
    private WebNNContext? context;   // from webNN.CreateContext

    // Support is per backend, not per browser: the same runtime answers differently for "gpu" and
    // "cpu", so a graph has to be checked against the context it will actually run on.
    private async Task Check()
    {
        var limits = await context!.GetOpSupportLimits();
        var hasConv = limits.Any(op => op.Name == "conv2d");
    }
}
Live sample
operator support 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 navigator.ml.createContext.
CreateContext
ValueTask<WebNNContext?> CreateContext(string deviceType = '', string powerPreference = '')
Creates an execution context on a device. Null when the runtime has no WebNN or no backend for the request.
WebNNContext.Info
WebNNContextInfo Info
Which backend this context actually got, and whether MLGraphBuilder exists.
WebNNContext.GetOpSupportLimits
ValueTask<WebNNOpSupport[]> GetOpSupportLimits()
Which operators this backend implements, and within what limits.
WebNNContext.DisposeAsync
ValueTask DisposeAsync()
Releases the context.
DisposeAsync
ValueTask DisposeAsync()
Releases every context created through this instance that is still open.
An unhandled error has occurred. Reload 🗙