Stream data to a shader
Push realtime data to a GPU visual each block.To feed a shader a stream of live data (a spectrogram, a waterfall, a scope history),
declare a bucket, fill it each block in process, and read it by name in the shader.
Declare a bucket in describe
A ring is a scrolling history: frames rows of width texels, newest row each block.
FMT_R16F is one half-float per texel, a quarter the bytes of full RGBA.
m.ring("spectro", 256, 128, FMT_R16F); // 256 freq columns x 128 time rowsm.texture(name, w, h, format?) and m.array(name, len, format?) are the other shapes.
Fill it each block
Write the newest column. For FMT_*32F pass a Float32Array to ctx.writeBucket; for a
packed format, pack the bytes yourself (f32ToF16) and use ctx.writeBucketBytes.
for (let i = 0; i < WIDTH; i++) {
const h = f32ToF16(level[i]); // map magnitude to 0..1, then to f16
this.row[i * 2] = <u8>(h & 0xff);
this.row[i * 2 + 1] = <u8>((h >> 8) & 0xff);
}
ctx.writeBucketBytes(0, this.row); // appends one ring rowRead it in the shader
Declare the name in opts.images and sample it with image("name", uv).
export default defineShader(
(f, _g) => {
"use gpu";
const level = image("spectro", f.uv).x; // R16F lands in .x
return vec4f(level, level * 0.4, 1.0 - level, 1.0);
},
{ images: ["spectro"] },
);The host rotates the ring and uploads only changed buckets, and only while the canvas is on screen. See the buckets reference for shapes and formats, and the GPU shader canvas for the reading side.