Halide has work-in-progress support for generating and running WebGPU shaders. This can be used in conjunction with the WebAssembly backend to bring GPU-accelerated Halide pipelines to the web.
As the first version of the WebGPU standard is itself still being developed, Halide's support has some limitations and may only work with certain browsers and versions of Emscripten.
The following is a non-comprehensive list of known limitations:
- Only 32-bit integers and floats have efficient support.
- 8-bit and 16-bit integers are implemented using emulation. Future extensions to WGSL will allow them to be implemented more efficiently.
- 64-bit integers and floats will likely remain unsupported until WGSL gains extensions to support them.
- Wrapping native device buffer handles is not yet implemented.
- You must use CMake/CTest to build/test Halide for WebGPU; using the Makefile is not supported for WebGPU testing (and probably never will be).
In addition to these functional limitations, the performance of the WebGPU backend has not yet been evaluated, and so optimizations in the runtime or device codegen may be required before it becomes profitable to use.
Tested with Emscripten 5.0.0 (a7c5deabd7c88ba1c38ebe988112256775f944c6) Tested with Node.js 25.5.0 Requires Node.js 25 or later
Halide can generate WebGPU code that can be integrated with WASM code using Emscripten.
When invoking emcc to link Halide-generated objects, include these flags:
--use-port=emdawnwebgpu -s JSPI.
Tests that use AOT compilation require a WebGPU implementation with Node.js bindings. There are two options:
webgpunpm package (recommended): Runnpm installin the Halide source root. The test runner will find and use the package automatically, with no additional environment variables needed. See below for setup instructions.- Custom
dawn.node: SetHL_WEBGPU_NODE_BINDINGS=/path/to/dawn.node. See Setting up Dawn for build instructions.
JIT compilation is not supported when using WebGPU with WASM.
Tested with Dawn release branch chromium/7698 (536c572aba)
For testing purposes, Halide can also target native WebGPU libraries, such as Dawn or wgpu. This is currently the only path that can run the JIT correctness tests. See below for instructions on building Dawn.
Note that as of 2026-02-17, wgpu is not supported due to lack of WaitAny timeout support.
When targeting WebGPU with a native target, Halide defaults to looking for a
build of Dawn (with several common names and suffixes); you can override this
by setting the HL_WEBGPU_NATIVE_LIB environment variable to the absolute path
to the library you want.
Note that it is explicitly legal to define both HL_WEBGPU_NATIVE_LIB and
HL_WEBGPU_NODE_BINDINGS at the same time; the correct executable environment
will be selected based on the Halide target specified.
Note that it is explicitly legal to specify both WEBGPU_NATIVE_LIB and WEBGPU_NODE_BINDINGS for the same build; the correct executable environment will be selected based on the Halide target specified.
Install nvm (Node Version Manager), then use it to install and activate Node.js 25:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
nvm install 25
nvm use 25
To activate Node.js 25 automatically in future shell sessions, add
nvm use 25 (or nvm alias default 25) to your shell profile.
Install Emscripten using the emsdk:
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
Add source /path/to/emsdk/emsdk_env.sh to your shell profile to activate
Emscripten automatically in future sessions.
The simplest way to provide a WebGPU implementation for Node.js testing is to
install the webgpu npm package, which
bundles pre-built Dawn Node.js bindings. From the Halide source root, run:
npm install
The package.json in the Halide source root lists webgpu as a dependency.
Once installed, the test runner (tools/launch_wasm_test.js) will find and use
it automatically — no HL_WEBGPU_NODE_BINDINGS environment variable is needed.
As an alternative to the webgpu npm package, you can build the Dawn Node.js
bindings from source. See Setting up Dawn for instructions.
After building, point the test runner at your build by setting:
HL_WEBGPU_NODE_BINDINGS=/path/to/dawn.node
Tested with Dawn vcpkg port version 20251202.213730 on macOS (arm64)
The Halide repository includes vcpkg support for automatically downloading and
building Dawn as part of the CMake configure step. This is the easiest path for
native WebGPU testing and does not require depot_tools or gclient.
Note: the vcpkg Dawn port builds only the native libwebgpu_dawn shared
library. It does not include Node.js bindings (dawn.node), so this path
only supports HL_TARGET=host-webgpu (native JIT). For the WASM+Node.js path,
see Setting up Dawn below.
Clone and bootstrap vcpkg locally (no system-wide installation needed):
git clone https://github.com/microsoft/vcpkg <vcpkg_dir>
<vcpkg_dir>/bootstrap-vcpkg.sh -disableMetrics
Use the provided arm64-osx-halide (or x64-osx-halide) overlay triplet,
which keeps static linkage for all packages except Dawn (which must be shared
so it can be loaded via dlopen at runtime):
cmake <halide_root_dir> -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=<vcpkg_dir>/scripts/buildsystems/vcpkg.cmake \
-DVCPKG_TARGET_TRIPLET=arm64-osx-halide \
-DVCPKG_MANIFEST_FEATURES=webgpu-native \
-DLLVM_DIR=<llvm_dir>/lib/cmake/llvm \
-DClang_DIR=<llvm_dir>/lib/cmake/clang \
-DHalide_TARGET=host-webgpu
cmake --build <build_dir>
vcpkg will download and build Dawn automatically during the configure step,
installing it into <build_dir>/vcpkg_installed/.
You may need to add -DWITH_SERIALIZATION=OFF, -DWITH_PYTHON_BINDINGS=OFF, and
-DHalide_WASM_BACKEND=OFF flags because vcpkg disables FetchContent, and those
features depend on packages (flatbuffers, pybind11, wabt) that are not included
in the webgpu-native manifest feature.
Set HL_WEBGPU_NATIVE_LIB to the vcpkg-installed Dawn library and run via
CTest:
HL_JIT_TARGET=host-webgpu \
HL_WEBGPU_NATIVE_LIB=<build_dir>/vcpkg_installed/arm64-osx-halide/lib/libwebgpu_dawn.dylib \
ctest --test-dir <build_dir> -R correctness_gpu
Building Dawn's Node.js bindings currently requires using CMake.
First, install depot_tools and add it to the
PATH environment variable.
Next, get Dawn and its dependencies:
# Clone the repo
git clone https://dawn.googlesource.com/dawn
cd dawn
# Bootstrap the gclient configuration with Node.js bindings enabled
cp scripts/standalone-with-node.gclient .gclient
# Fetch external dependencies and toolchains with gclient
gclient sync
# Other dependencies that must be installed manually:
# - golang
Finally, build Dawn, enabling both the Node.js bindings and shared libraries:
mkdir -p <build_dir>
cd <build_dir>
cmake <dawn_root_dir> -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DDAWN_BUILD_NODE_BINDINGS=1 \
-DDAWN_ENABLE_PIC=1 \
-DBUILD_SHARED_LIBS=ON
ninja dawn.node webgpu_dawn
This will produce the following artifacts:
- Node.js bindings:
<build_dir>/dawn.node - Native library:
<build_dir>/src/dawn/native/libwebgpu_dawn.{so,dylib,dll}
These paths can then be used for the HL_WEBGPU_NODE_BINDINGS and
HL_WEBGPU_NATIVE_LIB environment variables when using Halide.
The recommended method for updating mini_webgpu.h is to copy the
gen/include/dawn/webgpu.h file from the Dawn build directory, then:
- Restore the
// clang-format {off,on}lines. - Comment out the
#include <std*>lines. - Include the following block to define things that would normally be defined in system headers:
// BEGIN Halide-specific changes
//
// For the Halide runtime, we can't include these headers,
// so we define NULL, SIZE_MAX, and integer limit macros here.
// #include <stdint.h>
// #include <stddef.h>
// #include <math.h>
#ifndef NULL
#ifdef __cplusplus
#define NULL nullptr
#else
#define NULL ((void*)0)
#endif
#endif
#ifndef SIZE_MAX
#define SIZE_MAX (~(size_t)0)
#endif
#ifndef UINT32_MAX
#define UINT32_MAX (~(uint32_t)0)
#endif
#ifndef UINT64_MAX
#define UINT64_MAX (~(uint64_t)0)
#endif
// This _should_ be correct on all platforms we support, but needs checking.
#ifndef UINT32_C
#define UINT32_C(x) ((uint32_t)(x))
#endif
// END Halide-specific changes
This guarantees a version of the WebGPU header that is compatible with how Halide builds the runtime.