This is the first version of my first HTML5 game. Beyond the standard features, I've added a high scores board which uses local storage to keep track of the scores.You can move the snake with the arrow keys.  Unlike the normal game, running into the wall doesn't kill the snake, only when it runs into itself. This will display the Game Over screen, at which point you can either enter your name or be anonymous as 'Noname'. You can view the high scores at any time by pressing the 'Highscores' button.  From there you are also able to go back to the main menu and start a new game.I'd like feedback to focus on the JavaScript, especially with regards to cleanliness and readability, as well as overall best practices.As a side note, I'm not sure if the stack snippets will run properly because of the use of local storage, but it works fine if run normally.use strict;(function (){let canvas = document.getElementById('gameboard');let scoreBoard = document.querySelector('.scoreboard');let highscoreLabel = document.querySelector('.highscore');let restartBtn = document.getElementById('restart');let gameOverScreen = document.querySelector('.gameover-screen');let finalScoreLabel = document.querySelector('.final-score');let playerNameInput = document.querySelector('.player-name');let highscoreBoardBtn = document.getElementById('highscores-btn');let highscoresBoard = document.querySelector('.highscores-board');let highscoresList = document.querySelector('.highscores-list');let highscoresReturnBtn = document.getElementById('return-from-highscores');canvas.width = 800;canvas.height = 600;let ctx = canvas.getContext('2d');let isPlayerSaved,    state,    points,    playerName,    snakeBody,    direction,    blockSize,    snakeHead,    apple;let init = function() {  canvas.style.display = block;  gameOverScreen.style.display = none;  isPlayerSaved = false;//this flag helps eliminate duplicates from highscore board;if it's value's true,same player can't be saved again  state = 'game';  points = 0;  playerName = '';  snakeBody = [{    x: 60,    y: 60,    color:'red',    lastPosX: null,    lastPosY: null  }];  for(let i = 0; i < 5; i++) {    snakeBody.push({color:'blue'});  }  direction = 'right';  blockSize = 20;  snakeHead = snakeBody[0];  apple = {    x: Math.floor(Math.random() * canvas.width),    y: Math.floor(Math.random() * canvas.height),    color: 'red',  };}init();let draw = function(o) {  if(o.x != undefined || o.x != null) {    ctx.beginPath();    ctx.fillStyle = o.color;    ctx.fillRect(o.x, o.y, blockSize, blockSize);  }}let inputHandler = function(e) {  if(direction === 'right' && e.keyCode === 37) {    return;  }  else if(direction === 'left' && e.keyCode === 39) {    return;  }  else if(direction === 'up' && e.keyCode === 40) {    return;  }  else if(direction === 'down' && e.keyCode === 38) {    return;  }  switch (e.keyCode) {    case 38:  /* Up arrow was pressed */      direction = 'up';    break;    case 40:  /* Down arrow was pressed */      direction = 'down';    break;    case 37:  /* Left arrow was pressed */      direction = 'left';    break;    case 39:  /* Right arrow was pressed */      direction = 'right';    break;    }}let move = function() {  switch (direction) {    case 'left':      snakeHead.x -= blockSize;      break;    case 'right':      snakeHead.x += blockSize;      break;    case 'down':      snakeHead.y += blockSize;      break;    case 'up':      snakeHead.y -= blockSize;      break;  }}let update = function() {  scoreBoard.innerHTML = Points:  + points;  //Calculate the highscore and display it  if(localStorage.getItem('highscores')) {    let storageHighscoresItemsArr = [];    let storageHighscoresItems = JSON.parse(localStorage.getItem('highscores'));    for(let i = 0; i < storageHighscoresItems.length; i++) {       storageHighscoresItemsArr.push(storageHighscoresItems[i].score);    }    let highscore = Math.max(...storageHighscoresItemsArr);    highscoreLabel.innerHTML = Highscore:  + highscore;  }  //Snake's body movement  for(let i = 1; i < snakeBody.length; i++) {    snakeBody[i].x = snakeBody[i - 1].lastPosX;    snakeBody[i].y = snakeBody[i - 1].lastPosY;    snakeBody[i - 1].lastPosX = snakeBody[i - 1].x;    snakeBody[i - 1].lastPosY = snakeBody[i - 1].y;    } }let generateApple = function() {  apple = {    x: Math.floor(Math.random() * canvas.width),    y: Math.floor(Math.random() * canvas.height),    color: 'red',  };  let isAppleReachable = false;  let isAppleOutsideBody = function() {    for(let i = 0; i < snakeBody.length; i++ ) {      if((snakeBody[i].x === apple.x) && (snakeBody[i].y === apple.y)) {        return false;      }    }    return true;  }  while(isAppleReachable === false){    if(((apple.x % blockSize === 0) && (apple.y % blockSize === 0)) && isAppleOutsideBody()) {      isAppleReachable = true;    }    else {      apple.x =  Math.floor(Math.random() * canvas.width);      apple.y = Math.floor(Math.random() * canvas.height);   }  }}let checkForColliions = function() {  if(snakeHead.x >= canvas.width) {    snakeHead.x = 0;  }  else if(snakeHead.x <= -10) {    snakeHead.x = canvas.width;  }  else if(snakeHead.y <= -10) {    snakeHead.y = canvas.height;  }  else if(snakeHead.y >= canvas.height) {    snakeHead.y = 0;  }  if((snakeHead.x === apple.x) && (snakeHead.y === apple.y)) {      points += 10;      generateApple();      snakeBody.push({color:'blue'});  }  for(let i = 1; i < snakeBody.length; i++) {    if((snakeHead.x === snakeBody[i].x) && (snakeHead.y === snakeBody[i].y)) {        state = 'gameOver';    }  }}let savePlayerToLocalStorage = function() {  if(isPlayerSaved === false) {    isPlayerSaved = true;    playerName = playerNameInput.value;    let highscores = [];    if(localStorage.getItem('highscores')) {      highscores = highscores.concat(JSON.parse(localStorage.getItem('highscores')));    }    highscores.push({name: playerName, score: points});    localStorage.setItem('highscores', JSON.stringify(highscores));  }}let loop = function() {  if(state === 'game') {    update();    ctx.clearRect(0, 0, canvas.width, canvas.height);    snakeBody.forEach( (s) => {      draw(s);    })    draw(apple);    for(let y = blockSize; y < 600; y += blockSize) {      ctx.beginPath();      ctx.moveTo(0,y);      ctx.lineTo(800, y);      ctx.stroke();    }    for(let x = blockSize; x < 800; x += blockSize) {      ctx.beginPath();      ctx.moveTo(x,0);      ctx.lineTo(x, 600);      ctx.stroke();    }    move();    checkForColliions();    setTimeout(loop,50);  }  else if(state === 'gameOver') {    canvas.style.display = none;    gameOverScreen.style.display = block;    highscoresBoard.style.display = none;    finalScoreLabel.innerHTML = Your score:  + points;  }  else if(state === 'highscores') {    gameOverScreen.style.display = 'none';    highscoresBoard.style.display = 'flex';    highscoresList.innerHTML = '';    let storageHighscoresItems = JSON.parse(localStorage.getItem('highscores'));    let storageHighscoresItemsArr = [];    for(let i = 0; i < storageHighscoresItems.length; i++) {         storageHighscoresItemsArr.push(storageHighscoresItems[i]);      }      storageHighscoresItemsArr.sort(function (a, b) {        if (a.score > b.score) {          return 1;        }        if (a.score < b.score) {          return -1;        }        return 0;    });    storageHighscoresItemsArr.reverse();      for(let i = 0; i < 10; i++) {          if(storageHighscoresItemsArr[i]) {            let li = document.createElement('li');            highscoresList.appendChild(li);            li.innerHTML = (storageHighscoresItemsArr[i].name || 'Noname') +  -  + storageHighscoresItemsArr[i].score;        }      }  }}loop();generateApple()addEventListener('keydown', e =>  inputHandler(e), true);restartBtn.addEventListener('click', () => {  savePlayerToLocalStorage();  init();  generateApple();  loop();})highscoreBoardBtn.addEventListener('click', () => {  savePlayerToLocalStorage();  state = 'highscores';  loop();})highscoresReturnBtn.addEventListener('click', () => {  state = 'gameOver';  loop();})} ())html,body {  margin: 0;  padding: 0;  overflow: hidden;  height: 100%;}.game-wrap {  height: 100%;  width: 100%;  display: flex;  flex-direction: column;  align-items: center;  justify-content: center;}.board {  background: white;  border: 2px black solid;}.label {  width: 800px;  display: flex;  flex-direction: row;  justify-content: space-between;}.gameover-screen {    height: 600px;    width: 800px;    background: red;    /*display: flex;    flex-direction: column;    justify-content: center;    align-items: center;*/    display: none;}.gameover-screen ul {  list-style: none;  height: 100%;  display: flex;  flex-direction: column;  align-items: center;  justify-content: center;}.gameover-screen ul li {  margin-bottom: 50px;}.final-score {  font-size: 32px;}.highscores-board {  display: flex;  flex-direction: column;  align-items: center;  height: 600px;  width: 800px;  color: white;  background: black;  display: none;}.highscores-board ol  {  margin-top: 35px;}.highscores-board ol li {  font-size: 36px;  margin-bottom: 10px;}#return-from-highscores {  width: 200px;  height: 100px;}<!DOCTYPE html><html><head lang=en-Us>  <meta charset=utf-8>  <title>Snake</title>  <link rel=stylesheet href=css/main.css></head><body></div>  <div class=game-wrap>    <div class=label>      <p class=scoreboard>Points: 0</p>      <p class=highscore>Highscore: 0</p>    </div>    <div class=gameover-screen>      <ul>        <li><p class=final-score>Your score: </p></li>        <li><label>Your name: </label>        <input class=player-name type=text maxlength=6></li>        <li><button id=restart>Restart</button></li>        <li><button id=highscores-btn>Highscores</button></li>      </ul>    </div>    <div class=highscores-board>      <ol class=highscores-list></ol>      <button id=return-from-highscores>Back</button>    </div>    <canvas id=gameboard class=board></canvas>  </div>  <script src=js/game.js></script></body></html>
HTML5 Snake Game
javascript;css;html5;canvas;snake game
In general this is how would a JS game architecture look like.You certainly can get it working with another architecture but this one will give good performance and responsiveness. You have to careful with calls from within the event handlers.You want to release the event handler ASAP - so call only quick core methods.Everything else (like save to disks, network io) try to do it in the core thread/timer where the process should be triggered by some indicator in the shared data for example.Comments on specific moments:1) Global funcitonslet generateApple = function() { .. }when you put that code in the global scope it is the same like function generateApple() { .. }Where the latter is a bit more readable in my opinion.2) Global functions 2You are using global functions most of the time - that allows you to call everything from everywhere and makes your program look like a bow of spaghetti. No offense I love it .. however in the long term makes your code hard to read and maintain.I'd suggest you to keep your code separated in namespaces classes at least.var Core={};   Core.prototype.generateApple = function() { .. }  Core.prototype.checkForCollisions = function() { .. }var UI = {};   UI.prototype.draw = function() { .. }This will allow you to have separation of concerns and will help you understand and maintain your own code better.3) Naming conventions function loop() { .. } // Is expected to have a loop insideTry to find name to represent what is the code inside doing.If the code is handling user input, renders the content to screen and updates the high score table .. call it like that.handleUserInputRenderScreeContentAndUpdateHighScoreTable() This should hint you to break code in purposeful functions separated by concern.4) Event handlersMind you - these function are called on mouse or button click. if you want responsive UI and happy users keep them short and fast. If it would take time the user would expect at a please wait indicator.I would love to see it again after you apply these principals.