Game of Life

Conway's cellular automaton running entirely on GPU compute shaders. Compare two implementations: raw WebGPU vs TypeGPU's higher-level API.

Loading demo...

TypeGPU Implementation

This version uses TypeGPU's higher-level APIs. The code is more declarative and type-safe, with less boilerplate.

Key Differences

1. Typed Buffers

Instead of manually calculating sizes and handling serialization:

// Raw WebGPU
const buffer = device.createBuffer({
  size: cellCount * 4,  // Manual size calculation
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(buffer, 0, new Uint32Array(data));

// TypeGPU
const CellArray = d.arrayOf(d.u32, cellCount);
const buffer = root.createBuffer(CellArray, data).$usage("storage");

2. Declarative Bind Group Layouts

Named keys instead of numeric binding indices:

// Raw WebGPU
const layout = device.createBindGroupLayout({
  entries: [
    { binding: 0, buffer: { type: "uniform" } },
    { binding: 1, buffer: { type: "read-only-storage" } },
    { binding: 2, buffer: { type: "storage" } },
  ],
});

// TypeGPU
const layout = tgpu.bindGroupLayout({
  uniforms: { uniform: GridUniforms },
  cellStateIn: { storage: CellStateArray, access: "readonly" },
  cellStateOut: { storage: CellStateArray, access: "mutable" },
});

3. Cleaner Pipeline Execution

Fluent API instead of command encoder boilerplate:

// Raw WebGPU
const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(32, 32);
pass.end();
device.queue.submit([encoder.finish()]);

// TypeGPU
computePipeline
  .with(layout, bindGroup)
  .dispatchWorkgroups(32, 32);

4. Shader Functions with $uses()

The $uses() method injects bind group members into shaders, making dependencies explicit and type-checked.

Tech Stack

  • TypeGPU - Type-safe WebGPU toolkit
  • WGSL - WebGPU Shading Language for compute and render shaders
  • Storage buffers - GPU memory for cell state (read/write from shaders)
  • Uniform buffers - GPU memory for constants like grid dimensions