Game of life - JavaScript I would like to kindly ask others to review my code. I'd like suggestions, criticisms, and discussions on what is good and what could be done better.



const canvas = document.querySelector('.gameOfLife');
const ctx = canvas.getContext('2d');

// create an array
const canvasWidth = 100;
const canvasHeight = 100;
canvas.width = 3 * canvasWidth;
canvas.height = 3 * canvasHeight;
let gameBoard = createBoard(canvasWidth, canvasHeight);
let gameBoard2 = createBoard(canvasWidth, canvasHeight);
let gameBoard3 = createBoard(canvasWidth, canvasHeight);

function createBoard(width, height) {
  let board = new Array(0);
  for (let i = 0; i &lt; width; i++) {
    board [i] = [];
    for (let j = 0; j &lt; height; j++){
      board[i][j] = 0;
    }
  }
  return board;
}

function randomNumber(max) {
  return Math.floor(Math.random() * max);
}

function randomPos() {
  const x = randomNumber(canvasWidth);
  const y = randomNumber(canvasHeight);
  return [x, y];
}

function randomBoard(arr) {
  for (let i = 0; i &lt;= 0.2 * canvasHeight * canvasWidth; i++) {
    const [x, y] = randomPos();
    arr[x][y] = 1;
  }
}

function drawBoard(arr) {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  for (let i = 0; i &lt; canvasWidth; i++) {
    for (let j = 0; j &lt; canvasHeight; j++){
      if (arr[i][j] === 1) {
        ctx.fillStyle = "#012345";
        ctx.fillRect(i*3, j*3, 3, 3);
      }
    }
  }
}

// calculate new cycle

function checkSurrounding(x, y) {
  let sum = 0;

  startX = x-1 &lt; 0 ? x : x-1;
  stopX = x+1 &gt;= canvasWidth ? x : x+1;
  startY = y-1 &lt; 0 ? y : y-1;
  stopY = y+1 &gt;= canvasHeight ? y : y+1;

  for (let i = startX ; i &lt;= stopX; i++) {
    for (let j = startY ; j &lt;= stopY; j++) {
      if (i !== x || j !== y) {
        sum += gameBoard[i][j];
      }
    }
  }

  return sum;
}

function deadOrAlive(x, y) {
  const surround = checkSurrounding(x, y);

  if (gameBoard[x][y] === 1 &amp;&amp; (surround === 2 || surround === 3)) return 1;
  else if (gameBoard[x][y] === 0 &amp;&amp; surround === 3) return 1;
  else return 0;
}

function nextGene() {
  let newBoard = createBoard(canvasWidth, canvasHeight);

  for (let i = 0; i &lt; canvasWidth; i++) {
    for (let j = 0; j &lt; canvasHeight; j++) {
      newBoard[i][j] = deadOrAlive(i, j);
    }
  }

  gameBoard = newBoard;
  drawBoard(newBoard);

}


randomBoard(gameBoard);
drawBoard(gameBoard);

setInterval(nextGene, 150);
&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
  &lt;meta charset="UTF-8"&gt;
  &lt;title&gt;Game of Life - Canvas&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;canvas class="gameOfLife" width="400" height="400"&gt;&lt;/canvas&gt;
  &lt;script type="text/javascript" src="scripts.js"&gt;&lt;/script&gt;
&lt;/body&gt;

&lt;style&gt;
  body {
    background-color: #ffc600;
  }
  .gameOfLife{
    margin: 15px auto;
    background-color: #ff6c00;
    display: block;
  }
&lt;/style&gt;
&lt;/html&gt;
I have gone over the code briefly and looked at a couple optimizations, but haven't come up with any drastic changes. I will try to go over it again when I find time.
What is good
I like how the various functions are generally short. And the CSS/HTML is very simple too! It is a nice use of the canvas.
Suggestions
Iterations
Anytime you iterate over an array using a for loop, consider using a functional approach like .forEach(), .map(). That way, you won't have to handle the bookkeeping of iterating the variable (e.g. var i) and indexing into the array. For more information, I recommend going through these exercises. Though be aware that functional approaches are not always optimal/faster.
Creating board:
Inspired by the comment from Longfei Wu on this question, the function to create the board could take on a functional approach using Array.fill() and Array.map()
function createBoard(width, height) {
  return Array(width).fill(0).map(function() {
    return Array(height).fill(0);
  });
}

And if you are open to using ES-6 arrow functions, that can be even shorter:
function createBoard(width, height) {
  let board = Array(width).fill(0).map(() =&gt; Array(height).fill(0));
  return board;
}

See comparison in this jsPerf. When I ran it in Chrome the array fill-map approach was faster, though the opposite was true in Firefox, Mobile Safari and emphasized textEdge.
Drawboard:
.forEach() can be used here instead of the for statements:
function drawBoard(arr) {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  arr.forEach(function(innerArray, i) {//  for (let i = 0; i &lt; canvasWidth; i++) {
    innerArray.forEach(function(cell, j) {//for (let j = 0; j &lt; canvasHeight; j++){
      if (cell === 1) {
        ctx.fillStyle = &quot;#012345&quot;;
        ctx.fillRect(i*3, j*3, 3, 3);
      }
    });
  });
}

And the same is true for nextGene().
deadOrAlive()
Because checkSurrounding() checks 3-9 spaces around the given cell, I see cases where it returns values greater than 3. Should deadOrAlive() have logic in those cases? It appears that it only has logic for return values of 2 and 3.
Updated snippet:


const canvas = document.querySelector('.gameOfLife');
const ctx = canvas.getContext('2d');

// create an array
const canvasWidth = 100;
const canvasHeight = 100;
canvas.width = 3 * canvasWidth;
canvas.height = 3 * canvasHeight;
let gameBoard = createBoard(canvasWidth, canvasHeight);
let gameBoard2 = createBoard(canvasWidth, canvasHeight);
let gameBoard3 = createBoard(canvasWidth, canvasHeight);

function createBoard(width, height) {
  return Array(width).fill(0).map(function() {
    return Array(height).fill(0);
  });
}

function randomNumber(max) {
  return Math.floor(Math.random() * max);
}

function randomPos() {
  const x = randomNumber(canvasWidth);
  const y = randomNumber(canvasHeight);
  return [x, y];
}

function randomBoard(arr) {
  for (let i = 0; i &lt;= 0.2 * canvasHeight * canvasWidth; i++) {
    const [x, y] = randomPos();
    arr[x][y] = 1;
  }
}

function drawBoard(arr) {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  arr.forEach(function(innerArray, i) {//  for (let i = 0; i &lt; canvasWidth; i++) {
    innerArray.forEach(function(cell, j) {//for (let j = 0; j &lt; canvasHeight; j++){
      if (cell === 1) {
        ctx.fillStyle = "#012345";
        ctx.fillRect(i*3, j*3, 3, 3);
      }
    });
  });
}

// calculate new cycle

function checkSurrounding(x, y) {
  let sum = 0;

  startX = x-1 &lt; 0 ? x : x-1;
  stopX = x+1 &gt;= canvasWidth ? x : x+1;
  startY = y-1 &lt; 0 ? y : y-1;
  stopY = y+1 &gt;= canvasHeight ? y : y+1;

  for (let i = startX ; i &lt;= stopX; i++) {
    for (let j = startY ; j &lt;= stopY; j++) {
      if (i !== x || j !== y) {
        sum += gameBoard[i][j];
      }
    }
  }

  return sum;
}

function deadOrAlive(x, y) {
  const surround = checkSurrounding(x, y);

  if (gameBoard[x][y] === 1 &amp;&amp; (surround === 2 || surround === 3)) return 1;
  else if (gameBoard[x][y] === 0 &amp;&amp; surround === 3) return 1;
  else return 0;
}

function nextGene() {
  let newBoard = createBoard(canvasWidth, canvasHeight);

  newBoard.forEach(function(innerArray, i) {//  for (let i = 0; i &lt; canvasWidth; i++) {
    innerArray.forEach(function(cell, j) {//for (let j = 0; j &lt; canvasHeight; j++){
      newBoard[i][j] = deadOrAlive(i, j);
    });
  });

  gameBoard = newBoard;
  drawBoard(newBoard);

}


randomBoard(gameBoard);
drawBoard(gameBoard);

setInterval(nextGene, 150);
&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
  &lt;meta charset="UTF-8"&gt;
  &lt;title&gt;Game of Life - Canvas&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;canvas class="gameOfLife" width="400" height="400"&gt;&lt;/canvas&gt;
  &lt;script type="text/javascript" src="scripts.js"&gt;&lt;/script&gt;
&lt;/body&gt;

&lt;style&gt;
  body {
    background-color: #ffc600;
  }
  .gameOfLife{
    margin: 15px auto;
    background-color: #ff6c00;
    display: block;
  }
&lt;/style&gt;
&lt;/html&gt;