vgpu: a typed WebGPU library that runs the same code in the browser and headless Node
Modular cross-runtime WebGPU library for shaders, 3D scenes, GPU tensors, neural networks, and math viz
At a glance
- What is it?
- vgpu is a Vercel Labs TypeScript library for WebGPU with typed WGSL imports, one Gpu context, and a mock adapter for tests. It suits shader authors who want one API across browser, Node and CI, and it is the wrong tool if you want a scene graph.
- Who is it for?
- Adopt vgpu if you write WGSL by hand, want uniform names checked at build time, and need the same shader code to run in the browser, in headless Node and in CI without a GPU. Skip it if you want a scene graph, a material system or a renderer that manages object hierarchies for you, because vgpu's frames are explicit pass calls and nothing else.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 17, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What vgpu solves for shader authors
WebGPU gives you a device, buffers, pipelines and bind group layouts. It does not give you a way to import a .wgsl file, and it does not give you a way to run that shader in a test process without a GPU. Those two gaps are where most of the friction sits in a small WebGPU project, and they are what vgpu targets.
The README states the goal directly: typed shader imports, a small gpu-first API, and the same code running in the browser, headless Node, and your test suite. The audience is therefore narrow and specific. It is for people who already know what a bind group is and would rather write WGSL than fight a node editor. It is not a general 3D engine, and the repository does not present it as one.
The design decision that shapes everything else is the single context. init() returns one handle, and every entry point takes it as the first argument. The README calls this out as a deliberate absence of hidden global state. In practice that means two devices in one process are possible without monkey-patching a module, and it means your functions have to thread the gpu value through, which is a real cost in code verbosity.
Typed WGSL imports and how reflection keeps bindings honest
The mechanism is a build-time import resolver for .wgsl files. A shader can export a fn, struct or const, and another shader can import it by name. The README gives a grain.wgsl example that imports hash2 from @vgpu/wgsl-std/hash and exports a grain function. Imports resolve at build time through typed WGSL reflection, and the README states there is no codegen step and no manual binding declarations to keep in sync.
That last sentence is the whole pitch. In a plain WebGPU project you write a bind group layout, a pipeline layout, and a uniform buffer whose byte offsets you compute by hand. Rename a field in the shader and the mismatch shows up as a validation error at runtime, or worse, as garbage pixels. vgpu moves that check earlier. Because the .wgsl file is a module with types, a wrong name or a wrong type is a build error.
The second half of the mechanism is set(). The README describes effect() as compiling a shader into a fullscreen effect whose uniforms are addressed by their WGSL names through set(), and notes that writes land immediately, so the frame loop only sets what changes. That is a different model from a retained uniform buffer you rewrite each frame. It is convenient for a handful of scalars and it means you cannot batch a large uniform upload through the same path.
@vgpu/wgsl-std ships reusable declarations such as hash, noise, color and sampling as named exports. The README does not enumerate the full set, so treat the list as partial.
Installing vgpu and drawing a first fullscreen effect
The README's Quick Start uses pnpm and adds the WebGPU type definitions as a dev dependency. The types package is separate from vgpu itself, so a TypeScript project needs both.
pnpm add vgpu
pnpm add -D @webgpu/typesWith those installed, the README's first example wires a canvas, an effect and a frame loop. Note that waveShader is imported from a .wgsl file, which is the typed import path described earlier; your bundler needs the vgpu plugin or equivalent resolution for that import to work.
import { clock, init, effect, frameLoop, surface } from "vgpu";
import waveShader from "./wave.wgsl";
const gpu = await init();
const canvasSurface = surface(gpu, canvas, { dpr: [1, 2] });
const wave = effect(gpu, waveShader, { set: { speed: 2 } });
const time = clock(gpu);
frameLoop(gpu, (frame) => {
wave.set({ time: time.time });
frame.pass(canvasSurface, wave);
});What you should see: init() acquires an adapter and device and returns the Gpu context. surface() wraps the canvas as a render target and keeps its size current, clamping the device-pixel ratio between 1 and 2. effect() compiles the shader, and the initial set option writes speed before the first frame. clock() provides frame time, and frameLoop() runs the callback once per frame, where frame.pass draws into the surface.
The Node path is the same shape with different imports. This is the version to copy when you want a rendered image in a test or a CI job rather than a live canvas.
import { draw, frame, init, target } from "vgpu/node";
import triangleShader from "./triangle.wgsl";
const gpu = await init();
const colorTarget = target(gpu, { size: [256, 256], format: "rgba8unorm" });
const triangle = draw(gpu, { shader: triangleShader });
frame(gpu, (f) => f.pass(colorTarget, triangle));
const pixels = await colorTarget.read();
gpu.dispose();colorTarget.read() returns the pixels, which is what makes this useful in an assertion. The README states the Node build is Dawn-backed. For tests that must not touch a GPU at all, vgpu/mock swaps in a deterministic software adapter for the same code. The README does not describe how faithful the mock's output is to a real device, so do not assume pixel-identical results between vgpu/node and vgpu/mock.
Where vgpu is the wrong choice
There is no scene graph. The README is explicit that passes, clears and draws are explicit calls, never implicit scene-graph state, and the example frame(gpu, (f) => f.pass(target, effect)) shows the entire frame description. If your project needs parent-child transforms, frustum culling, a material system or a glTF loader, none of that is in the API surface listed in the README, and you would be rebuilding it. A renderer like three.js already has those things, and the repository even ships an examples/three-tsl directory, which suggests the two are meant to coexist rather than compete.
The second limitation is the budget. The README states a complete fullscreen effect ships in 25 KB gzipped and that this budget is enforced in CI. That is a genuine constraint on what can be added to the core, and it means anything sizable belongs in a separate package rather than in the main entry point. If you were hoping the library would grow to include the feature you need, the CI gate is the reason it probably will not.
The third is the runtime range. The workspace package.json pins engines to node >=22 <23, and the README describes the Node path as Dawn-backed. Node 20 or 24 is outside the declared range. That is a supported-configuration question you should settle before writing code, not after.
Finally, the repository is a Vercel Labs project on the canary default branch. Labs projects are exploratory by nature. The last push was on 2026-09-17 and v0.5.0 shipped on 2026-09-14, so the code is moving, but a pre-1.0 version number means the API can change between releases.
How vgpu differs from three.js and from raw WebGPU
Against raw WebGPU, the difference is the shader module system and the mock adapter. Raw WebGPU gives you createShaderModule with a WGSL string and a device that only exists where a real adapter exists. vgpu gives you an import graph across .wgsl files, name-addressed uniforms, and a second adapter that runs the same code without a GPU. The cost is a build step and a dependency.
Against three.js, the difference is where the abstraction sits. three.js abstracts the scene: you add meshes to a graph, and the renderer decides what to draw and in what order. vgpu abstracts the shader and the frame: you decide what to draw, and the library handles module resolution, uniform addressing and target management. The two are not interchangeable, and the presence of an examples/three-tsl directory in the repository is consistent with using three.js for scene management and vgpu for the compute or shader work underneath.
A third comparison worth making is against writing your own thin wrapper. If your project has one shader and one canvas, the typed import system is overhead. The value appears when you have several shaders sharing declarations, or when you need the same shader to run in a test process, which is exactly the case the mock adapter exists for.
Maintenance, licence and what an upgrade costs
The repository is MIT licensed, and the README carries the licence badge pointing at the LICENSE file on the canary branch. MIT is permissive: it allows use, modification and redistribution provided the copyright notice and permission notice are retained. That is a summary of the licence text, not legal advice, and if you redistribute vgpu inside a product you should read the LICENSE file yourself.
Upgrade cost is shaped by the version number. v0.5.0 was released on 2026-09-14, preceded by v0.5.0-rc.1 on 2026-09-11, which the release notes label a native beta. A pre-1.0 library with a native component still in beta means you should read the changelog between versions rather than assuming a drop-in upgrade. The repository has a .changeset directory, so releases are managed through changesets, and CHANGELOG.md is the file to check.
The CLI is part of the same package, which helps here. The README shows npx vgpu docs cat getting-started.md and npx vgpu docs find effect, and states that the guides and API reference ship inside the package and run offline. That means the documentation matching your installed version is available locally instead of only on vgpu.sh, which is the right arrangement for a library whose API is still moving. There is also a doctor command listed in the packages table for Dawn and software-renderer setup, which is the first thing to run when the Node path fails on a new machine.
Editorial conclusion
Adopt vgpu if you write WGSL by hand, want uniform names checked at build time, and need the same shader code to run in the browser, in headless Node and in CI without a GPU. Skip it if you want a scene graph, a material system or a renderer that manages object hierarchies for you, because vgpu's frames are explicit pass calls and nothing else. Before committing, run npx vgpu doctor on each target machine, confirm the Node engine range in package.json matches your runtime, and read the licence file in the repository if you plan to redistribute the package.
Frequently asked questions
Does vgpu run in Node without a GPU?
The README states that vgpu/node is Dawn-backed and that vgpu/mock swaps in a deterministic software adapter for the same code, so tests never need a GPU. The README does not describe how closely the mock's output matches a real device.
What is the vgpu CLI for?
The README lists the vgpu command-line binary as covering docs, shader check, doctor, and Dawn or software-renderer setup. It also shows npx vgpu examples search and npx vgpu examples pull for copying an example's source locally.
Can I use vgpu with three.js?
The repository contains an examples/three-tsl directory, and the README does not present vgpu as a replacement for a scene graph, since passes and draws are explicit calls. The README does not document a specific integration API for three.js.
Which Node version does vgpu require?
The workspace package.json sets engines to node >=22 <23. The README does not state a supported range beyond that, so a runtime outside it is untested territory.
Is vgpu a full 3D engine?
No. The README describes explicit frames where passes, clears and draws are calls you make, with no implicit scene-graph state. The listed API surface covers shaders, effects, targets and compute rather than scene management.
Community notes