www.xbdev.net
xbdev - software development
Friday June 27, 2025
Home | Contact | Support | WebGPU Graphics and Compute ... | WebGPU 'Compute'.. Compute, Algorithms, and Code.....
     
 

WebGPU 'Compute'..

Compute, Algorithms, and Code.....

 


Game of Life Simulation


The Game of Life is a grid-based simulation of cells thatevolves through discrete time steps based on simple rules, resulting in intricate and often unpredictable patterns. The emerging patterns from the interactions of live and dead cells showcases how complex structures and behaviors can arise from a simple set of rules and deterministic processes.


Game of Life
Game of Life


Functions Used: requestAdapter(), getPreferredCanvasFormat(), createCommandEncoder(), beginRenderPass(), setPipeline(), draw(), end(), submit(), getCurrentTexture(), createView(), createShaderModule()

Background Convay's Game Of Life


It consists of a 2D grid of cells - each cell has a value that represents 'life'. Value of 0 is dead (no life), but higher values indicate population in that cell.

To visualize the cells, you'd typically draw empty cells black which are considered 'dead', while non-zero cells are drawn (white or some other color) to indiciate they're 'alive'.

At each time step, all the cells reevaluate their state according to a set of rules.

Each cell has 3 choices:
1. Survives
2. Birth
3. Dies

These are the rules:

• Any live cell with fewer than two live neighbors dies (underpopulation) [9 neighbours around a cell]
• Any live cell with two or three live neighbors continues to live.
• Any live cell with more than three live neighbors dies (overpopulation).
• Any dead cell with exactly three live neighbors becomes a live cell (reproduction).

You iteratively calculate the state for each cell - causing the cells to evolve (live and die in a lifelike pattern) - hence the 'name'.

We can implement the game of life on the compute shader using two buffers (two textures buffers for the previous and next state).

Once it's implemented on the compute shader, it's just a matter of performing iterative updates, flipping the buffers for the input and output.


For the game to work, the initial texture needs to have some values - some noise - so it can be initialzied in the first frame. Pass a 'timer' counter - if the counter is '0' - then it's assumed that this is the first frame - and we set the output to a random value.


// Frame counter updated after each iteration
@binding(0) @group(0) var<uniformmytimer f32;
// Texture binding for rendering
@binding(1) @group(0) var myTexture0texture_storage_2d<rgba8unormread>;
@
binding(2) @group(0) var myTexture1texture_storage_2d<rgba8unormwrite>;

// Scene parameters
const imgWidth:  u32 = ${canvas.width};
const 
imgHeightu32 = ${canvas.width};
const 
gridSize:  i32 = ${canvas.width};

fn 
random(uvvec2<f32>) -> f32 {
    return 
fract(sin(dot(uvvec2<f32>(12.989878.233))) * 43758.5453);
}

// Main compute shader function
@compute @workgroup_size(88)
fn 
main(
    @
builtin(global_invocation_idglobalIdvec3<u32>,
    @
builtin(local_invocation_idlocalIdvec3<u32>,
    @
builtin(workgroup_idworkgroupIdvec3<u32>,
    @
builtin(num_workgroupsworkgroupSizevec3<u32>
) {
    if ( 
mytimer <= 0.0 )
    {
        var 
randomvec2<f32>( f32(globalId.x), f32(globalId.y) ) );
        if   ( 
0.94 ) { 0.0; }
        else              { 
1.0; }
        
textureStore(myTexture1vec2<i32>(globalId.xy), vec4<f32>( f32(r), 001.0));
        return;
    }
    
     
// Calculate the index of the cell
    
let index = ( globalId.u32(gridSize) ) + globalId.x;
    
    
// Read the current state of the cell
    
let currentState u32textureLoad(myTexture0vec2<i32>(globalId.xy) ).);
    
    
// Count the number of alive neighbors
    
var aliveNeighbors 0u;
    for (var 
dx i32 = -1dx <= 1dx++) {
        for (var 
dy i32 = -1dy <= 1dy++) {
            if (
dx == && dy == 0) {
                continue; 
// Skip the current cell
            
}

            
let neighborX i32(globalId.x) + dx;
            
let neighborY i32(globalId.y) + dy;

            if (
neighborX || neighborX >= gridSize || neighborY || neighborY >= gridSize) {
                continue; 
// Skip out-of-bounds neighbors
            
}

            
let neighborState u32textureLoad(myTexture0vec2<i32>(neighborXneighborY) ).);
            
aliveNeighbors += neighborState;
        }
    }
    
    
// Apply Conway's Game of Life rules
    // 3 options
    // 1 - survive
    // 2 - birth
    // 3 - death
    
var nextState 0u;
    if ( 
currentState == 1u && (aliveNeighbors == 2u || aliveNeighbors == 3u)) 
    {
        
nextState 1u// Cell survives
    

    else if ( 
u32(currentState) == 0u && aliveNeighbors == 3u
    {
        
nextState 1u// Cell is born
    
}
    
// death
    // Overcrowding: if a cell is alive and 4 or more of its neighbors are also alive - the cell will be dead
    // Exposure: If a live cell has only 1 live neighbor or no live neighbors, it will be dead 

    
    
var 0.0;
    var 
0.0;
    if ( 
aliveNeighbors 3        ) { 1.0; }
    if ( 
currentState != nextState ) { 1.0; }
    
     
// Write the next state to the output buffer
    
textureStore(myTexture1vec2<i32>(globalId.xy), vec4<f32>( f32(nextState), gb1.0));
}



Game of life output after a few minutes.
Game of life output after a few minutes.


Things to Try


• Starting value - instead of random noise all over the texture - focus it in a small circle in the middle (so that the life works outwards)
• Initialise the random starting texture pattern to be in the shape of 'text' using the canvas write to initize the texture before it's passed to the shader (e.g., words 'LIFE' written in random points)
• Try adding in some additional logic (random changing of states)
• Create a larger texture, e.g., 1024, split the texture into 'regions' different regions have different parameters (add some extra control logic/probabilities)



Resources and Links


• WebGPU Lab Compute Game of Life [LINK]























WebGPU by Example: Fractals, Image Effects, Ray-Tracing, Procedural Geometry, 2D/3D, Particles, Simulations WebGPU Compute graphics and animations using the webgpu api 12 week course kenwright learn webgpu api kenwright programming compute and graphics applications with html5 and webgpu api kenwright real-time 3d graphics with webgpu kenwright webgpu api develompent a quick start guide kenwright webgpu by example 2022 kenwright webgpu gems kenwright webgpu interactive compute and graphics visualization cookbook kenwright wgsl webgpu shading language cookbook kenwright wgsl webgpugems shading language cookbook kenwright



 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2025 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.