Skip to content

BJJINS/webgpu-forge

Repository files navigation

webgpu-forge

WGSL-aware buffer layout tooling and lightweight Float32Array math 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 ArrayBuffer instances
  • providing lightweight vector and matrix helpers for transform-oriented math

Highlights

  • 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 ArrayBuffer instances.
  • Support runtime-sized arrays when the caller provides byteLength or runtimeArrayCount.
  • Visualize binding layouts in a Node.js terminal or as a browser canvas.
  • Use lightweight Vec and Mat helpers that keep data in plain typed arrays.

Installation

npm install webgpu-forge

Package Overview

The package currently exposes three main entry points:

  • BufferBindingMemory — high-level WGSL-aware buffer allocation and serialization
  • Vec — vector math helpers built on Float32Array
  • Mat — column-major matrix helpers built on Float32Array

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.

Quick Start

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.


BufferBindingMemory

Source: lib/bindingMemory/BufferBindingMemory.ts

What it does

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.

Core capabilities

  • 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

Primary APIs

  • new BufferBindingMemory(shaderCode)
  • set(bindingName, data, options?)
  • createBuffer(bindingName, options?)
  • writeBuffer(bindingName, arrayBuffer, data)
  • createLayoutSnapshot(bindingName)

Fixed-size buffer example

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); // 96

Runtime-sized array example

For 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); // 80

Layout visualization

createLayoutSnapshot() 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.projection

Example browser canvas output:

Web canvas binding-memory visualization


Vec

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.

Conventions

  • vectors are plain Float32Array
  • supported dimensions are vec2, vec3, and vec4
  • most binary operations require equal dimensions
  • out parameters are optional; when omitted, a new vector is allocated
  • normalize() treats very small vectors as zero vectors

Main APIs

  • 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

Example

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);

Mat

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.

Conventions

  • matrices are plain Float32Array
  • storage order is column-major
  • mat3x3() creates length-9 matrices; mat4x4() creates length-16 matrices
  • identity() defaults to 4x4 when out is omitted
  • translation() always produces 4x4
  • scaling(), rotationX(), rotationY(), rotationZ(), and rotation() can write either 3x3 or 4x4 depending on the out matrix length

Main APIs

  • 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() input shapes

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

Example

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);

Development

npm install
npm run dev
npm run typecheck
npm run test:run
npm run build

Publish Checklist

  1. Update name / version in package.json.
  2. Run npm run build and confirm that dist/ contains .js, .cjs, and .d.ts outputs.
  3. Publish the package.

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages