WGSL-aware buffer layout tooling and lightweight
Float32Arraymath helpers for WebGPU.
webgpu-forge is a small set of low-level utilities for WebGPU applications. The project currently focuses on:
- parsing WGSL buffer declarations
- computing binding-memory layouts accurately
- writing plain JavaScript data into correctly sized
ArrayBufferinstances - providing lightweight vector and matrix helpers for transform-oriented math
- Parse WGSL
@group(...) @binding(...) var<uniform|storage>declarations. - Compute alignment, offset, stride, size, and padding using WGSL-compatible layout rules.
- Serialize plain objects, arrays, and typed arrays into
ArrayBufferinstances. - Support runtime-sized arrays when the caller provides
byteLengthorruntimeArrayCount. - Visualize binding layouts in a Node.js terminal or as a browser canvas.
- Use lightweight
VecandMathelpers that keep data in plain typed arrays.
npm install webgpu-forgeThe package currently exposes three main entry points:
BufferBindingMemory— high-level WGSL-aware buffer allocation and serializationVec— vector math helpers built onFloat32ArrayMat— column-major matrix helpers built onFloat32Array
Example root import:
import { BufferBindingMemory, BindingMemoryLayout, Vec, Mat } from 'webgpu-forge';Use BindingMemoryLayout when you only need parsed layout metadata and validation. Use BufferBindingMemory when you also want to allocate buffers and write data.
import { BufferBindingMemory } from 'webgpu-forge';
const shaderCode = `
struct Material {
color: vec3f,
intensity: f32,
weights: array<f32, 2>,
transform: mat2x3f,
}
@group(0) @binding(0) var<storage> material: Material;
`;
const memory = new BufferBindingMemory(shaderCode);
const arrayBuffer = memory.set('material', {
color: [1, 2, 3],
intensity: 4,
weights: [5, 6],
transform: [7, 8, 9, 10, 11, 12],
});
console.log(Array.from(new Float32Array(arrayBuffer)));
// [
// 1, 2, 3, 4,
// 5, 6, 0, 0,
// 7, 8, 9, 0,
// 10, 11, 12, 0,
// ]BufferBindingMemory applies WGSL layout rules for you, so you do not need to hand-compute offsets, strides, or padding.
Source: lib/bindingMemory/BufferBindingMemory.ts
BufferBindingMemory treats a WGSL buffer declaration as a writable memory template. You provide WGSL source code and plain JavaScript data, and it produces an ArrayBuffer that matches WGSL layout requirements.
- parse uniform and storage buffer declarations from WGSL
- allocate buffers with the correct byte length
- support fixed-size layouts and runtime-sized arrays
- write scalar, vector, matrix, struct, and array data
- accept plain arrays and typed arrays as input
- inspect layouts through terminal or canvas visualization
new BufferBindingMemory(shaderCode)set(bindingName, data, options?)createBuffer(bindingName, options?)writeBuffer(bindingName, arrayBuffer, data)createLayoutSnapshot(bindingName)
import { BufferBindingMemory } from 'webgpu-forge';
const shaderCode = `
struct Camera {
exposure: f32,
offset: vec3f,
projection: mat4x4f,
}
@group(0) @binding(0) var<uniform> camera: Camera;
`;
const memory = new BufferBindingMemory(shaderCode);
const buffer = memory.set('camera', {
exposure: 1,
offset: [0, 2, 4],
projection: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
});
console.log(buffer.byteLength); // 96For runtime-sized arrays, you must tell the library how large the tail should be.
import { BufferBindingMemory } from 'webgpu-forge';
const shaderCode = `
struct PointLight {
position: vec3f,
color: vec3f,
}
struct LightStorage {
pointCount: u32,
point: array<PointLight>,
}
@group(0) @binding(1) var<storage> lights: LightStorage;
`;
const memory = new BufferBindingMemory(shaderCode);
const buffer = memory.set(
'lights',
{
pointCount: 2,
point: [
{
position: [1, 2, 3],
color: [4, 5, 6],
},
{
position: [7, 8, 9],
color: [10, 11, 12],
},
],
},
{ runtimeArrayCount: 2 },
);
console.log(buffer.byteLength); // 80createLayoutSnapshot() is useful when you want to inspect a parsed layout instead of writing data immediately.
import { BufferBindingMemory } from 'webgpu-forge';
const shaderCode = `
struct Camera {
exposure: f32,
offset: vec3f,
projection: mat4x4f,
}
@group(0) @binding(0) var<uniform> camera: Camera;
`;
const memory = new BufferBindingMemory(shaderCode);
const snapshot = memory.createLayoutSnapshot('camera');
console.log(snapshot.environment); // "node"Runtime-dependent behavior:
- Node.js — prints a colorized terminal visualization automatically
- Browser — stores a canvas visualization on
snapshot.image - Other runtimes — returns the structured snapshot without rendering
Example terminal output:
WGSL Binding Memory Snapshot
binding : camera
address-space : uniform
environment : node
size : 96B
align : 16
validation : ok
memory layout:
tree label kind offset end size align type details path
---- ---------- ------- ------ --- ---- ----- ------- ----------------- -----------------
root camera struct 0 96 96B 16 Camera — camera
├─ exposure builtin 0 4 4B 4 f32 — camera.exposure
├─ <padding> padding 4 16 12B — — reason=struct-gap —
├─ offset builtin 16 28 12B 16 vec3f — camera.offset
├─ <padding> padding 28 32 4B — — reason=struct-gap —
└─ projection builtin 32 96 64B 16 mat4x4f — camera.projectionExample browser canvas output:
Source: lib/vector.ts
Vec provides lightweight vector helpers that operate on plain Float32Array instances. The module intentionally avoids wrapper classes and focuses on direct 2D / 3D / 4D math.
- vectors are plain
Float32Array - supported dimensions are
vec2,vec3, andvec4 - most binary operations require equal dimensions
outparameters are optional; when omitted, a new vector is allocatednormalize()treats very small vectors as zero vectors
- constructors:
vec2,vec3,vec4 - component-wise operations:
copy,add,subtract,multiply,divide,scale,negate,min,max,clamp,lerp - scalar / geometric operations:
dot,lengthSq,length,distanceSq,distance,normalize,equals - 3D-only operation:
cross
import { Vec } from 'webgpu-forge';
const a = Vec.vec3(1, 2, 3);
const b = Vec.vec3(4, 5, 6);
const added = Vec.add(a, b);
const unit = Vec.normalize(a);
const normal = Vec.cross(a, b);Source: lib/mat.ts
Mat provides lightweight column-major matrix helpers backed by plain Float32Array. It currently focuses on 3x3 and 4x4 matrices that are convenient for transform-oriented graphics math.
- matrices are plain
Float32Array - storage order is column-major
mat3x3()creates length-9 matrices;mat4x4()creates length-16 matricesidentity()defaults to4x4whenoutis omittedtranslation()always produces4x4scaling(),rotationX(),rotationY(),rotationZ(), androtation()can write either3x3or4x4depending on theoutmatrix length
- creation:
mat3x3,mat4x4,identity - transforms:
translation,scaling,rotationX,rotationY,rotationZ,rotation - pure-transform inverses:
invertPureTranslation,invertPureScaling,invertPureRotation - matrix operations:
copy,add,subtract,scale,transpose,multiply - matrix-vector multiplication:
multiplyVector
rotation() accepts one of the following input objects:
- Euler rotation:
{ x, y, z, order? } - Quaternion rotation:
{ x, y, z, w }
Notes:
- Euler angles are in radians
- supported rotation orders are
xyz,xzy,yxz,yzx,zxy,zyx - quaternion input is normalized internally before conversion to a matrix
import { Mat, Vec } from 'webgpu-forge';
const model = Mat.rotation({ x: 0, y: Math.PI / 2, z: 0 });
const translated = Mat.translation(4, 5, 6);
const combined = Mat.multiply(translated, model);
const position = Vec.vec4(1, 2, 3, 1);
const worldPosition = Mat.multiplyVector(combined, position);npm install
npm run dev
npm run typecheck
npm run test:run
npm run build- Update
name/versioninpackage.json. - Run
npm run buildand confirm thatdist/contains.js,.cjs, and.d.tsoutputs. - Publish the package.
MIT
