Life is finite

Source code

~(async () => {
    const canvas = document.querySelector('canvas')
    const context = canvas.getContext('2d')

    const [WIDTH, HEIGHT] = [canvas.width / 16, canvas.height / 16]

    const grid = Array.from(Array(HEIGHT), row => Array(WIDTH).fill(false))
        .map(row => row.map(cell => Math.random() > .75 ? true : false))
    const counts = Array.from(Array(HEIGHT), row => Array(WIDTH).fill(0))

    const render = () => {
        grid.forEach((row, rowIndex) => {
            row.forEach((cell, cellIndex) => {
                context.fillStyle = cell ? 'black' : 'white'
                context.fillRect(cellIndex * 16, rowIndex * 16, 15, 15)
            })
        })
    }

    const judge = () => {
        grid.forEach((row, rowIndex) => {
            row.forEach((_, cellIndex) => {
                let count = 0
                for (let y = -1; y <= 1; y++) {
                    for (let x = -1; x <= 1; x++) {
                        if (x != 0 || y != 0) {
                            if (grid[rowIndex + y] && grid[rowIndex + y][cellIndex + x]) {
                                count += grid[rowIndex + y][cellIndex + x] ? 1 : 0
                            }
                        }
                    }
                }
                counts[rowIndex][cellIndex] = count
            })
        })
    }

    const evolve = () => {
        grid.forEach((row, rowIndex) => {
            row.forEach((cell, cellIndex) => {
                const count = counts[rowIndex][cellIndex]
                row[cellIndex] = cell && (count == 2 || count == 3) || !cell && count == 3
            })
        })
    }

    for (;;) {
        render()
        judge()
        evolve()
        await new Promise(resolve => setTimeout(resolve, 250))
    }
})()
game-of-life.js