Storage buffers are GPU-side data arrays that compute shaders can read and write, and fragment shaders can read. They are the primary mechanism for passing structured data between the CPU, compute pipeline, and rendering pipeline.
Declaring storage buffers
Storage buffers are declared in defineMaterial({ storageBuffers }):
StorageBufferDefinition
Supported types
Access modes
Fragment shaders always access storage buffers as var<storage, read> regardless of the declared access mode. WGSL does not support write-only storage buffers.
Writing storage buffers from CPU
Use state.writeStorageBuffer() inside useFrame to upload data from the CPU to a GPU storage buffer:
Validation
- The buffer name must be declared in
material.storageBuffers. offset + data.byteLengthmust not exceed the buffersize.offsetmust be>= 0.
Writes are batched as pending storage writes and flushed to the GPU at the start of the next render call.
Reading storage buffers back to CPU
Use state.readStorageBuffer() to asynchronously read GPU buffer data back to the CPU:
How readback works
- A staging buffer is created with
MAP_READ | COPY_DSTusage. - The GPU copies the storage buffer contents to the staging buffer.
- The staging buffer is mapped for CPU read access.
- The mapped data is copied and returned as an
ArrayBuffer. - The staging buffer is unmapped and destroyed.
This is an async operation — readStorageBuffer returns a Promise<ArrayBuffer>.
Caveats
- Readback requires the renderer to be initialized. If called before the first render, the promise rejects.
- Readback involves a GPU → CPU transfer and is inherently slower than writes. Use sparingly.
- The returned
ArrayBufferis a copy — modifying it does not affect the GPU buffer.
WGSL access in shaders
Storage buffers are automatically bound at group(1) in both compute and fragment shaders. The library generates the bindings for you — just reference the buffer by its declared name:
In compute shaders
In fragment shaders
You do not need to write the @group and @binding attributes yourself — the library injects them automatically. Simply use the buffer name in your shader code.
Initial data
When initialData is provided, the buffer is populated on GPU creation:
The initialData must fit within the declared size. Supported typed arrays: Float32Array, Uint32Array, Int32Array.
Pipeline signature
Adding, removing, or changing storage buffer definitions (name, size, type, access) changes the material signature and triggers a full renderer rebuild. Writing data to an existing buffer at runtime does not trigger a rebuild.