---
url: /examples/gpgpu/game-of-life.md
description: >-
  Run Conway's Game of Life entirely on the GPU and display the evolving cell
  state texture.
---

::: example-editor

```ts
import { glCanvas, glContext, loop, pingPongFBO } from "@radiancejs/gl";
import "./styles.css";

const { gl, canvas } = glContext({ canvas: "#glCanvas" });

const gridSize = 100;

const lifeUpdateFragment = /* glsl */ `
  uniform sampler2D tCurrentState;
  varying vec2 vUv;

  int getCellState(vec2 coord) {
    vec4 color = texture2D(tCurrentState, coord);
    return color.r > 0.5 ? 1 : 0;
  }

  void main() {
    int neighbors = 0;
    float dx = 1.0 / float(${gridSize});
    float dy = 1.0 / float(${gridSize});

    neighbors += getCellState(vUv + vec2(-dx, -dy));
    neighbors += getCellState(vUv + vec2(-dx, 0.0));
    neighbors += getCellState(vUv + vec2(-dx, dy));
    neighbors += getCellState(vUv + vec2(0.0, -dy));
    neighbors += getCellState(vUv + vec2(0.0, dy));
    neighbors += getCellState(vUv + vec2(dx, -dy));
    neighbors += getCellState(vUv + vec2(dx, 0.0));
    neighbors += getCellState(vUv + vec2(dx, dy));

    int currentState = getCellState(vUv);
    float newState = 0.0;

    if (currentState == 0 && neighbors == 3) {
      newState = 1.0; // Birth
    } else if (currentState == 1 && (neighbors == 2 || neighbors == 3)) {
      newState = 1.0; // Survival
    }

    gl_FragColor = vec4(newState, newState, newState, 1.0);
  }
`;

const initialData = new Float32Array(gridSize * gridSize * 4);
for (let i = 0; i < gridSize * gridSize; i++) {
  const alive = Math.random() < 0.5 ? 1 : 0;
  initialData.set([alive, alive, alive, 1], i * 4);
}

const gameState = pingPongFBO({
  gl,
  fragment: lifeUpdateFragment,
  dataTexture: {
    name: "tCurrentState",
    initialData,
  },
});

const renderPass = glCanvas({
  canvas,
  fragment: /* glsl */ `
    uniform sampler2D tCurrentState;
    attribute vec2 vUv;

    void main() {
      gl_FragColor = texture2D(tCurrentState, vUv);
    }
  `,
  uniforms: {
    tCurrentState: () => gameState.texture,
  },
});

let lastTime = 0;
loop(({ elapsedTime }) => {
  if (elapsedTime - lastTime < 50) return;
  gameState.render();
  renderPass.render();
  lastTime = elapsedTime;
});

```

```css
html {
  color-scheme: light dark;
}

body {
  margin: 0;
  height: 100svh;
  display: grid;
  place-items: center;
}

canvas {
  width: min(90svmin, 900px);
  aspect-ratio: 1;
  display: block;
  border-radius: 8px;
  border: 1px solid rgb(128 128 128 / 0.4);
}

```

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>RadianceJS Example</title>
  </head>
  <body>
    <canvas id="glCanvas"></canvas>
    <script src="/index.ts"></script>
  </body>
</html>

```

:::
