WebGPU Compute Backend

June 6, 2026 · View on GitHub

The WebGPU backend is the portable browser and edge execution path for UPDE Euler stepping. It complements Rust and JAX rather than replacing them:

  • Rust remains the f64 low-latency production path.
  • JAX remains the differentiable accelerator path.
  • WebGPU targets browsers, mobile GPUs, kiosk deployments, WebView shells, and edge runtimes where installing Rust, Julia, Go, or Mojo toolchains is not acceptable.

Contract

The backend package generated by scpn_phase_orchestrator.experimental.accelerators.upde._engine_webgpu.build_webgpu_upde_package() emits:

  • WGSL compute shader source for a dense Sakaguchi-Kuramoto Euler substep;
  • an ES-module WebGPUUPDEBackend runner;
  • explicit capability metadata through get_webgpu_backend_capabilities().

The shader implements:

dtheta_i = omega_i
         + sum_j K_ij sin(theta_j - theta_i - alpha_ij)
         + zeta sin(psi - theta_i)

with phase wrapping into [0, 2π) after each Euler substep. Host JavaScript loops over nSteps * nSubsteps dispatches and swaps two phase buffers after each dispatch. This avoids the race that would appear if multiple coupled time steps were attempted inside one shader invocation.

Precision

Portable WebGPU exposes WGSL f32 arithmetic for this compute path. The WebGPU backend is therefore a browser and edge portability backend, not the safety or certification reference path. Use Rust/JAX/NumPy for f64 parity, formal safety evidence, and publication-grade numerical comparisons.

Expected WebGPU parity checks should compare invariants and bounded drift:

  • phases remain finite and wrapped into [0, 2π);
  • synchronising examples increase the order parameter;
  • short Euler traces remain within an f32 tolerance of the NumPy reference.

Dispatcher Position

The stateless UPDE dispatcher declares the preference chain:

rust -> webgpu -> mojo -> julia -> go -> python

Plain CPython does not claim WebGPU availability. The loader only becomes available when a host provides an explicit bridge through SPO_WEBGPU_DISPATCH_BRIDGE=module:function. Browser and edge applications can also run the generated WGSL/ES-module package directly, without entering the Python dispatcher. Otherwise Python remains the final fallback.

Usage

Generate the package sources:

from scpn_phase_orchestrator.experimental.accelerators.upde._engine_webgpu import build_webgpu_upde_package

package = build_webgpu_upde_package(method="euler")
wgsl_source = package.wgsl
js_source = package.javascript

Load the JavaScript module in a browser or compatible edge runtime, then create the backend:

const backend = await WebGPUUPDEBackend.create(wgslSource);
const phasesOut = await backend.runEuler({
  phases,
  omegas,
  knm,
  alpha,
  zeta: 0.0,
  psi: 0.0,
  dt: 0.01,
  nSteps: 100,
  nSubsteps: 1,
});

All array inputs must be Float32Array. knm and alpha are dense flattened row-major N x N matrices matching the Python/Rust UPDE contract.

Current Limits

  • Supported integrator: Euler.
  • Supported coupling: dense K_nm with dense alpha_nm.
  • Supported scalar type: WGSL f32.
  • Browser execution requires navigator.gpu.
  • Large dense all-to-all matrices have O(N^2) memory and compute cost. Large particle-scale deployments should add a separate sparse, block, or mean-field WebGPU kernel with its own documented mathematical contract instead of silently changing this dense backend.

Verification

Local tests cover:

  • capability metadata;
  • generated WGSL bindings and compute entry point;
  • generated JavaScript runner structure, buffer usage, and dispatch loop;
  • explicit rejection of unsupported integrators;
  • CPython fallback behaviour when no browser WebGPU runtime exists;
  • dispatcher registration without false availability.

Run:

PYTHONPATH=src python -m pytest -q tests/test_upde_webgpu_backend.py

Deployment decision note

This backend is positioned as a portability-first path. It is useful for browser and edge validation, with explicit limits on kernel scale and floating-point precision.

Keep WebGPU in that posture by default:

  • use it for fast visual or distribution-level checks,
  • keep numeric parity checks visible when comparing against rust and jax,
  • route deterministic production control through lanes with established timing contracts.