Skip to content

Sign up to be notified when the ShaderHunt platform is available, along with interactive examples teaching TypeGPU from the ground up.

WESL Interoperability

We are working with WESL, a community standard for enhanced WGSL, to enable hybrid programs that mix-and-match between shader-centric and host-centric approaches. Shaders written in WGSL or WESL can be reflected into JS/TS as TypeGPU definitions, with proper types generated on the fly.

  • ✨ Take advantage of type-safe buffers while keeping your shaders in WESL
  • ⚔️ Eliminate manual byte alignment and padding

This functionality is provided as an extension to wesl-plugin. Consult their documentation on what to install, and how to use it with your bundler of choice.

Our official wesl-ext-typepgu package extends the capabilities of wesl-plugin. Install it before proceeding.

npm add --save-dev wesl-ext-typegpu

Next up, reference the extension in your bundler’s configuration. Below is an example using Vite.

import { defineConfig } from "vite";
import weslPlugin from "wesl-plugin/vite";
import { linkBuildExtension } from "wesl-plugin";
import { typegpuExtension } from "wesl-ext-typegpu";
export default defineConfig({
plugins: [weslPlugin({ extensions: [linkBuildExtension, typegpuExtension] })],
});

And finally, to let the TypeScript language server know where to look for typing of the .wgsl/.wesl you’re importing, change the following in your tsconfig.json:

{
// ...
"include": [/* all other files you're including */, ".wesl/**/*"]
// ...
}

Let’s say we have to following shader program, split across two files.

shaders/shared.wesl
struct BoidState {
position: vec3f,
velocity: vec3f,
}
struct Fish {
kind: u32,
state: BoidState,
}
shaders/main.wesl
use package::shared::Fish;
@group(0) @binding(0) var<storage, read_write> fish: array<Fish>;
@compute @workgroup_size(32)
fn main() {
// ...
}

Given a shader written in WGSL/WESL, we can use the ?typegpu query parameter to import reified references to any struct definition.

main.ts
// Importing a WGSL struct into JS
import {
import Fish
Fish
} from './shaders/shared.wesl?typegpu';
const
const FishArray: (elementCount: number) => d.WgslArray<d.WgslStruct<{
kind: d.U32;
state: d.WgslStruct<{
position: d.Vec3f;
velocity: d.Vec3f;
}>;
}>>
FishArray
=
import d
d
.
arrayOf<d.WgslStruct<{
kind: d.U32;
state: d.WgslStruct<{
position: d.Vec3f;
velocity: d.Vec3f;
}>;
}>>(elementType: d.WgslStruct<{
kind: d.U32;
state: d.WgslStruct<{
position: d.Vec3f;
velocity: d.Vec3f;
}>;
}>, elementCount?: undefined): (elementCount: number) => d.WgslArray<...> (+1 overload)
export arrayOf

Creates an array schema that can be used to construct gpu buffers. Describes arrays with fixed-size length, storing elements of the same type.

@example

const LENGTH = 3; const array = d.arrayOf(d.u32, LENGTH);

If elementCount is not specified, a partially applied function is returned.

@example const array = d.arrayOf(d.vec3f); // ^? (n: number) => WgslArray<d.Vec3f>

@paramelementType The type of elements in the array.

@paramelementCount The number of elements in the array.

arrayOf
(
const Fish: d.WgslStruct<{
kind: d.U32;
state: d.WgslStruct<{
position: d.Vec3f;
velocity: d.Vec3f;
}>;
}>
Fish
);
const buffer =
const root: TgpuRoot
root
.
TgpuRoot.createBuffer<d.WgslArray<d.WgslStruct<{
kind: d.U32;
state: d.WgslStruct<{
position: d.Vec3f;
velocity: d.Vec3f;
}>;
}>>>(typeSchema: d.WgslArray<d.WgslStruct<{
kind: d.U32;
state: d.WgslStruct<{
position: d.Vec3f;
velocity: d.Vec3f;
}>;
}>>, initial?: {
...;
}[] | undefined): TgpuBuffer<...> (+1 overload)

Allocates memory on the GPU, allows passing data between host and shader.

@paramtypeSchema The type of data that this buffer will hold.

@paraminitial The initial value of the buffer. (optional)

createBuffer
(
const FishArray: (elementCount: number) => d.WgslArray<d.WgslStruct<{
kind: d.U32;
state: d.WgslStruct<{
position: d.Vec3f;
velocity: d.Vec3f;
}>;
}>>
FishArray
(512)).
TgpuBuffer<WgslArray<WgslStruct<{ kind: U32; state: WgslStruct<{ position: Vec3f; velocity: Vec3f; }>; }>>>.$usage<["storage"]>(usages_0: "storage"): TgpuBuffer<d.WgslArray<d.WgslStruct<{
kind: d.U32;
state: d.WgslStruct<{
position: d.Vec3f;
velocity: d.Vec3f;
}>;
}>>> & StorageFlag
$usage
('storage');
const buffer: TgpuBuffer<d.WgslArray<d.WgslStruct<{
kind: d.U32;
state: d.WgslStruct<{
position: d.Vec3f;
velocity: d.Vec3f;
}>;
}>>> & StorageFlag
// Updating the 123rd fish's position
const buffer: TgpuBuffer<d.WgslArray<d.WgslStruct<{
kind: d.U32;
state: d.WgslStruct<{
position: d.Vec3f;
velocity: d.Vec3f;
}>;
}>>> & StorageFlag
buffer
.
TgpuBuffer<WgslArray<WgslStruct<{ kind: U32; state: WgslStruct<{ position: Vec3f; velocity: Vec3f; }>; }>>>.writePartial(data: {
idx: number;
value: {
kind?: number | undefined;
state?: {
position?: d.v3f | undefined;
velocity?: d.v3f | undefined;
} | undefined;
} | undefined;
}[] | undefined): void
writePartial
([
{
idx: number
idx
: 123,
value: {
kind?: number | undefined;
state?: {
position?: d.v3f | undefined;
velocity?: d.v3f | undefined;
} | undefined;
} | undefined
value
: {
state?: {
position?: d.v3f | undefined;
velocity?: d.v3f | undefined;
} | undefined
state
: {
posit
posit: any
position
},
}
}
]);