diff --git a/life.html b/life.html
new file mode 100644
index 0000000..4611315
--- /dev/null
+++ b/life.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Hello typescript
+
+
+
+
+
+
diff --git a/life.ts b/life.ts
new file mode 100644
index 0000000..aafefd3
--- /dev/null
+++ b/life.ts
@@ -0,0 +1,81 @@
+import { Point, resizeCanvas } from "./common.js"
+
+const WIDTH = 800;
+const HEIGHT = 600;
+const SCALING = 4;
+const DIRECTIONS: Point[] = [
+ { x: 0, y: -1}, // N
+ { x: -1, y: -1}, // NW
+ { x: 1, y: -1}, // NE
+ { x: -1, y: 0}, // W
+ { x: 1, y: 0}, // E
+ { x: 0, y: 1}, // S
+ { x: -1, y: 1}, // SW
+ { x: 1, y: 1}, // SE
+]
+
+function add2(p1: Point, p2: Point) {
+ return { x: p1.x + p2.x, y: p1.y + p2.y };
+}
+
+function init() {
+ const canvas = document.getElementById("canvas") as HTMLCanvasElement | null;
+ if (!canvas) throw new Error("unable to get canvas HTML element");
+
+ const ctx = canvas.getContext("2d", { alpha: false }) as CanvasRenderingContext2D | null;
+ if (!ctx) throw new Error("unable to get canvas 2D context");
+
+ ctx.canvas.width = WIDTH
+ ctx.canvas.height = HEIGHT
+
+ const cells = new Array(WIDTH * HEIGHT / SCALING)
+ cells.fill(0)
+
+ const state = new Array(WIDTH * HEIGHT / SCALING)
+ state.fill(0)
+
+ for (let i=0; i < WIDTH * HEIGHT / SCALING; i++) state[i] = +(Math.random() * 4 < 1)
+
+ console.log(state.length)
+
+ //window.addEventListener('resize', () => resizeCanvas(ctx));
+ window.requestAnimationFrame(t => update(ctx, cells, state));
+}
+
+// update loop, called every frame
+function update(ctx: CanvasRenderingContext2D, cells: number[], state: number[]) {
+ ctx.clearRect(0, 0, WIDTH, HEIGHT);
+ for (let i=0; i < WIDTH * HEIGHT / SCALING; i++) cells[i] = state[i];
+
+ const cell = (p: Point) => cells[p.y*WIDTH/SCALING + p.x]
+
+ ctx.beginPath(); // batch draw calls, only once per frame
+ ctx.fillStyle = "white";
+ for (let y=1; y < HEIGHT - 1; y++) {
+ for (let x=1; x < WIDTH - 1; x++) {
+ const p = { x, y };
+ // count neighbors
+ let neighbors = 0;
+ for (const d of DIRECTIONS) {
+ const pos = add2(p, d);
+ neighbors += cell(pos);
+ }
+
+ if (cell(p) == 1) {
+ state[p.y*WIDTH/SCALING+p.x] = +(neighbors == 2 || neighbors == 3)
+ } else {
+ state[p.y*WIDTH/SCALING+p.x] = +(neighbors == 3)
+ }
+
+ if (cell(p) == 1) {
+ ctx.moveTo(p.x, p.y);
+ ctx.rect(p.x, p.y, SCALING, SCALING);
+ }
+ }
+ }
+ ctx.fill(); // draw only once per frame
+
+ window.requestAnimationFrame(t => update(ctx, cells, state));
+}
+
+init();