One file, two languages
The shader stays plain WGSL. interstage turns each declaration into its TypeGPU counterpart: structs into schemas,
each @group into a bind group layout, functions into typed functions and entry points into pipeline
stages.
Because tsc reads the shader itself, there is nothing to generate and nothing to keep in sync. Rename a
field in the shader and the TypeScript that writes it stops compiling.
tsc sees itExamples
Each one runs in your browser on WebGPU. Open the contract drawer on any of them to see the shader next to the declarations tsc emits for it.
-
Slime
Hundreds of thousands of agents lay and follow trails in three competing species.
Five entry points in one WGSL module: scatter, clear, step, diffuse and a full-screen draw. The sliders write the
Paramsstruct field by field. -
Ink
Stable fluids with vorticity confinement. Drag through the water to stir it.
Nine passes share one bind group layout of eight storage buffers, and TypeScript checks the element type of every buffer bound to it.
-
Sculpture
A gyroid lattice and three orbiting drops, raymarched with soft shadows.
A single fragment shader driven by one uniform struct,
Scene, which TypeScript fills in by name. -
Atlas
An endless topographic map. Drag to travel, scroll to zoom.
The fragment shader is TypeScript and calls
elevation()fromatlas.wgsl, so a call with the wrong vector type fails to compile.
What tsc tells you
Real output from building these examples.
validate option, Tint, the WGSL compiler inside Chrome, checks every shader during the type check. One of the first drafts of the slime mold used a reserved word:$ tsc --noEmit --runExternalCode src/slime/slime.wgsl(116,9): error wgsl100: 'target' is a reserved keyword
speed: f32 to speed: vec2f in slime.wgsl breaks the TypeScript that writes it:$ tsc --noEmit --runExternalCode
src/slime/main.ts(40,66): error TS2345: Argument of type '{ decay: number; sensorAngle: number;
sensorDistance: number; speciesCount: number; speed: number; turnSpeed: number; }' is not
assignable to parameter of type 'Behaviour'.
Types of property 'speed' are incompatible.
Type 'number' is not assignable to type 'v2f'.
Set it up
-
Install interstage next to TypeScript 7.1 and TypeGPU.
pnpm add -D interstage typescript@beta pnpm add typegpu -
Register the content mapper in
tsconfig.json.validateis optional and needs thewebgpupackage (pnpm add -D webgpu).{ "compilerOptions": { "skipLibCheck": true }, "contentMappers": [ { "package": "interstage", "extensions": [".wgsl"], "options": { "validate": true } } ] } -
Let Vite load
.wgslfiles as modules.import interstage from 'interstage/vite' export default defineConfig({ plugins: [interstage()] }) -
Type-check with permission to run the mapper. In VS Code, the TypeScript 7 extension does the same in trusted workspaces.
tsc --noEmit --runExternalCode