JSPM

webgpu-forge

1.1.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 20
  • Score
    100M100P100Q73562F
  • License MIT

WGSL-aware WebGPU binding memory layout and ArrayBuffer writing utilities.

Package Exports

  • webgpu-forge

Readme

webgpu-forge

WGSL-aware buffer layout parsing and ArrayBuffer writing for WebGPU.

Reusable WebGPU utilities with a current focus on WGSL-compatible binding memory layout and ArrayBuffer authoring.

Status

BufferBindingMemory is one of the main APIs currently under active development. It already covers the core workflow of:

  • parsing WGSL @group(...) @binding(...) var<uniform|storage> declarations
  • computing the correct layout, alignment, offsets, and padding
  • creating ArrayBuffer instances with the correct byte length
  • writing plain JavaScript objects / arrays / typed arrays into the buffer

The examples below focus on current API behavior. As more features are added later, the public API surface and documentation will continue to grow.

Install

npm install webgpu-forge

BufferBindingMemory

Source: lib/bindingMemory/BufferBindingMemory.ts

What this API is for

BufferBindingMemory lets you treat a WGSL buffer declaration as a writable memory template. You provide WGSL source code, then write normal JavaScript data into a correctly aligned ArrayBuffer without manually calculating offsets, strides, or padding.

Main capabilities

  • parse uniform/storage buffer declarations from WGSL
  • create buffers with the correct size
  • support both fixed-size layouts and runtime-sized arrays
  • write scalar / vector / matrix / struct / array data
  • accept plain arrays and typed arrays as input
  • visualize binding memory layouts in the Node terminal or as a browser canvas

API Overview

new BufferBindingMemory(shaderCode)

Creates a memory helper from WGSL source. The constructor parses @group/@binding buffer declarations and builds the layout information later used when you call set.


set(groupBindingName, data, options?)

set() is the primary write API. It takes a parsed WGSL binding, allocates an ArrayBuffer with the correct size, writes your JavaScript data into it according to the computed layout, and returns the finished buffer.

Use set() when you want a ready-to-use ArrayBuffer directly from WGSL + JS data.

For runtime-sized arrays, provide one of the following in options:

  • runtimeArrayCount
  • byteLength

createLayoutSnapshot(groupBindingName)

createLayoutSnapshot() is the inspection / debugging API for binding memory visualization. It builds a structured snapshot of the parsed binding layout and then renders it according to the current runtime:

  • Node.js: prints a colorized, width-aware terminal visualization automatically
  • Web / browser: creates a canvas-based memory board and stores it on snapshot.image
  • Unknown runtime: returns the structured snapshot without rendering

The returned snapshot includes information such as:

  • binding name
  • address space
  • total size and alignment
  • validation result and issues
  • the recursive layout tree (snapshot.root)
  • the detected runtime (snapshot.environment)
  • an optional canvas image (snapshot.image) in web environments

Use this API when you want to inspect offsets, padding, stride, array expansion, or validation errors without manually reading the layout tree.

Example 1: fixed-size storage buffer

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],
});

const float32 = new Float32Array(arrayBuffer);
console.log(Array.from(float32));
// [
//   1, 2, 3, 4,
//   5, 6, 0, 0,
//   7, 8, 9, 0,
//   10, 11, 12, 0,
// ]

Why this is useful

You write normal JS data, and the API inserts the required WGSL padding automatically.

Example 2: runtime-sized array

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 arrayBuffer = 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(arrayBuffer.byteLength); // 80

Important note

For runtime-sized arrays, BufferBindingMemory cannot guess the final tail size by itself. You must tell it the target size using runtimeArrayCount or byteLength.

Example 3: Node terminal visualization

import {BufferBindingMemory} from "webgpu-forge";

const shaderCode = `
    struct Camera {
        scale: 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"
// The terminal visualization is printed automatically when the snapshot is created.

This is useful on the server side or in CLI tooling when you want to quickly inspect byte ranges, padding, and validation issues.

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
├─    scale       builtin       0    4    4B      4  f32      —                  camera.scale
├─    <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 4: browser canvas visualization

import {BufferBindingMemory} from "webgpu-forge";

const shaderCode = `
    struct Camera {
        scale: f32,
        offset: vec3f,
        projection: mat4x4f,
    }

    @group(0) @binding(0) var<uniform> camera: Camera;
`;

const memory = new BufferBindingMemory(shaderCode);
const snapshot = memory.createLayoutSnapshot("camera");

if (snapshot.image instanceof HTMLCanvasElement) {
    document.body.appendChild(snapshot.image);
}

In the browser, snapshot.image contains a memory-board style canvas that helps visualize field bands, offsets, padding, and expanded array/matrix layout.

Example canvas output:

Web canvas binding memory visualization

Typical workflow

const memory = new BufferBindingMemory(shaderCode);
const arrayBuffer = memory.set(groupBindingName, data, options);

In most cases, this is the main workflow you need.

Supported input shapes

Current input support includes:

  • plain objects for WGSL structs
  • arrays / typed arrays for WGSL arrays
  • arrays / typed arrays for vectors and matrices
  • scalars such as i32, u32, f32, f16
  • atomic scalar writes such as atomic<u32>

Vec

Source: lib/vector.ts

Vec is a lightweight Float32Array vector helper module. It focuses on direct 2D / 3D / 4D operations and avoids extra wrapper classes.

Import

import {Vec} from "webgpu-forge";

Conventions

  • vectors are plain Float32Array
  • only vec2, vec3, and vec4 are supported
  • output parameters are optional; when omitted, a new vector is allocated
  • most binary operations require both inputs to have the same dimension
  • normalize() treats very small vectors as zero vectors

Main APIs

  • constructors: vec2, vec3, vec4
  • component-wise ops: copy, add, sub, mul, div, scale, negate, min, max, clamp, lerp
  • scalar / geometric ops: dot, lengthSq, length, distanceSq, distance, normalize, equals
  • 3D-only op: 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 built on plain Float32Array. It currently focuses on 3x3 and 4x4 matrices that work well with transform-style math.

Import

import {Mat} from "webgpu-forge";

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() is 4x4 only
  • scaling(), rotationX(), rotationY(), rotationZ(), and rotation() can write either 3x3 or 4x4 depending on whether out is a length-9 or length-16 matrix

Main APIs

  • creation: mat3x3, mat4x4, identity
  • transforms: translation, scaling, rotationX, rotationY, rotationZ, rotation
  • matrix ops: copy, add, sub, scale, transpose, multiply
  • matrix-vector op: multiplyVector

rotation() input shapes

rotation() accepts one of these 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);

Future expansion

This README intentionally treats BufferBindingMemory as a foundational API. As the project grows, more capabilities may be added around it, for example:

  • more binding-memory helpers
  • more serialization / update utilities
  • richer TypeScript typing for input data
  • higher-level WebGPU integration examples

Chinese documentation

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 dist/ contains .js, .cjs, and .d.ts outputs
  3. Publish the package