Recursive AI

Run WebGPU compute in Blazor WebAssembly

OneGpu is a free, open-source GPU compute and rendering library for .NET from Recursive AI, on Vulkan, Metal and WebGPU. MIT or Apache-2.0, no bundled native binaries.

WebGPU lets a browser run compute work on the GPU. This example requires a browser with WebGPU and runs in a .NET 10 Blazor WebAssembly app, not in server-side Blazor. OneGpu 1.0.0 names Chrome as its supported browser.

Start a new app with the commands below. Or skip setup: unzip the example and run dotnet run.

Terminal
dotnet new blazorwasm -n browser
cd browser
dotnet add package RecursiveAI.OneGpu.Backends
dotnet add package RecursiveAI.OneGpu.Kernels

Requires .NET 10 (net10.0); .NET 9 and earlier aren’t supported.

In a Blazor WebAssembly project, replace Pages/Home.razor with:

Razor
@page "/"
@using RecursiveAI.OneGpu
@using RecursiveAI.OneGpu.Kernels
 
<h1>WebGPU matrix product</h1>
<pre id="result">@result</pre>
 
@code {
    private string result = "Running…";
 
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (!firstRender) return;
        try
        {
            using var device = await GpuBackends.CreateDeviceAsync();
            using var left = device.CreateBuffer<float>(new float[] { 1, 2, 3, 4 });
            using var right = device.CreateBuffer<float>(new float[] { 5, 6, 7, 8 });
            using var output = device.CreateBuffer<float>(4);
            using var gemm = new Gemm(device, GemmElementType.F32);
            var encoder = device.BeginCommands();
            gemm.Encode(encoder, new GemmProblem(2, 2, 2), left, right, output);
            device.Submit(encoder);
            var values = new float[4];
            await output.ReadAsync(values);
            result = string.Join(", ", values);
        }
        catch (Exception error)
        {
            result = error.ToString();
        }
        StateHasChanged();
    }
}

Unzip the example, then dotnet run.

The page displays:

Text
19, 22, 43, 50

After dotnet run, open the app URL shown in the terminal in Chrome. The download is a complete Blazor WebAssembly app; no input files or custom shader build step are needed.

The full program

The component at the top is the complete calculation. This Program.cs starts the downloaded Blazor app and mounts its root component. using browser; imports the project’s namespace, derived from its project name; use your own namespace if you name the project differently:

C#
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using browser;
 
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
await builder.Build().RunAsync();

Home.razor runs the calculation after the first render. Both matrices are row-major 2 × 2 arrays in the component. The Gemm kernel comes from RecursiveAI.OneGpu.Kernels; the app submits a product and awaits the four output values before releasing its buffers.

The component catches failures and displays the exception; device creation throws GpuUnsupportedException when WebGPU, an adapter or the package’s JavaScript module is unavailable.

Variations

Choose the browser backend explicitly

Replace the component’s device creation line with this. The Backends package already references the WebGpu package. This version displayed the same four values in Chrome:

C#
using var device = await
    RecursiveAI.OneGpu.WebGpu.WebGpu.CreateDeviceAsync();

The short answer

Yes: a Blazor WebAssembly component can run OneGpu compute kernels through the browser’s WebGPU API. Reference RecursiveAI.OneGpu.Backends and RecursiveAI.OneGpu.Kernels, create the device with await GpuBackends.CreateDeviceAsync(), submit work, then await ReadAsync to obtain the output. The example above is a complete component, and the download includes the project and browser host page.

OneGpu 1.0.0 publishes its JavaScript module as a static web asset at _content/RecursiveAI.OneGpu.WebGpu/onegpu.js. Device creation loads it automatically. The browser backend source at the release pin shows both module loading and asynchronous device creation. You do not need to write JavaScript glue for this matrix product.

Can Wasm access WebGPU?

Yes, through the browser backend. The .NET code runs as WebAssembly and OneGpu calls its JavaScript module to access WebGPU. The C# layer creates typed buffers and records commands; the browser executes the GPU submission.

The component waits until OnAfterRenderAsync and checks firstRender, so updating the result does not start another calculation. It submits once, waits for readback, then updates the displayed string. The two input arrays are included directly in the source, so the result can be checked without downloading a dataset: multiplying the matrices produces 19, 22, 43 and 50 in row-major order.

Use the asynchronous device and readback APIs in browser code. The release’s browser instructions state that the browser cannot block. Calling a synchronous read is not the equivalent of awaiting completion on this platform. Keep the buffers and kernel alive until the awaited readback finishes, as the component’s scope does.

Custom compute shaders also run in the browser: pass a ShaderSource containing WGSL to CreateComputeKernel. The browser backend at the release pin rejects SPIR-V-only shaders and names OneShaderTargets including Wgsl as the build requirement. The compute-shader recipe shows compilation, binding and dispatch on Vulkan; for browser use, also generate WGSL, include it in ShaderSource, and use asynchronous device creation and readback as above.

Which devices support WebGPU?

For this recipe, use Chrome with WebGPU and an available adapter. That is the browser platform named by the OneGpu 1.0.0 requirements. General browser support does not establish that every GPU capability is present: the backend requests shader-f16 and subgroups when the adapter offers them, while kernels needing unavailable features use a portable fallback or report the missing feature.

The output above was obtained in a headless Chrome WebGPU session on the NVIDIA RTX 4070 machine. It is an example run, not a guarantee for all browser and device combinations. Device creation can fail before any matrix work is submitted; the component displays that error instead of leaving “Running…” on screen.

The browser backend does not report GPU timestamps in this release. It also cannot be used by a server-side component merely because the UI is viewed in Chrome: the code creating the WebGPU device must execute inside the browser. Start with the downloadable WebAssembly project when separating server and client code would otherwise make the execution location unclear.

All onegpu recipes