Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

            
              <!-- time and score -->
<div>
  <div class="numbers">
    <span>time</span>
    <span id="timer">00000</span>
  </div>
  <div class="numbers multiplier">
    <span>x2</span>
  </div>
  <div class="numbers">
    <span>score</span>
    <span id="score">00000</span>
  </div>
</div>

<!-- game area -->
<main>
  <section id="snake">
  </section>
  <span id="gameOver">GAME OVER</span>
</main>
<input type="button" value="PLAY" />

<!-- play button and options -->
<div id="options">
  <div>
    <div>
      <input type="checkbox" name="smallArea" /><span>Small game area</span>
    </div>
    <div>
      <input type="checkbox" name="fastSnake" /><span>Faster Chain</span>
    </div>   
  </div>
</div>
            
          
!
            
              *{
  box-sizing: border-box;
}

body, html{
  height: 100vh;
}

body{
  display: flex;
  flex-direction: column;
  align-items: center;
  min-height: 44rem;
  min-width: 30rem;
  background: url("https://cryptocannabis.app/wp-content/uploads/2019/06/ezgif.com-video-to-gif.gif");
  background-size: cover;
  background-position: right;
}

body>div:first-of-type{
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  width: 30rem;
  margin-top: 2rem;
}

.numbers span{
  display: block;
  color: white;
  font-size: 2rem;
  font-family: "Nova Mono", monospace;
}

.numbers:nth-of-type(2){
  display: none;
  align-self: flex-end;
  width: 100%;
  margin-right: 1rem;
  text-align: right;
}

.numbers:nth-of-type(3){
  align-self: flex-end;
  span{
    text-align: right;
  }
}

main{
  position: relative;
  width: 30rem;
  height: 30rem;
  margin-top: 0.5rem;
  border: 1rem solid rgba(0,0,0,0.5);
  background-color: rgba(0,0,0,0.6);
  transition: all 0.5s;
  &>div{
    position: absolute;
    height: 5rem;
    width: 5rem;
  }
  .normalApple{
    background: url("https://cryptocannabis.app/wp-content/uploads/2019/06/TRC10-CCGX.png");
    background-size: 100%;
  }
  .specialApple{
    background: url("http://weknownyourdreamz.com/images/star/star-01.jpg");
    background-size: 100%;
  }
}

#snake{
  div{
    position: absolute;
    top: 0;
    left: 0;
    height: 1rem;
    width: 1rem;
    &:last-of-type::after{
      content: ":";
      display: block;
      position: absolute;
      font-size: 1.5rem;
    }
    &:last-of-type::before{
      content: "y";
      display: block;
      position: absolute;
      color: pink;
      font-family: sans-serif;
      font-size: 0.6rem;
    }
  }
}

.normalSnake{
  background-color: green;
  &::after{
    color: white;
  }
}

.specialSnake{
  background-color: #E6AF2E;
  &::after{
    color: blue;
  }
}

/*Head positions*/
.left{
  border-top-left-radius: 50%;
  border-bottom-left-radius: 50%;
  &::after{
    top: -0.4rem;
  }
  &::before{
    top: 0.25rem;
    left: -0.4rem;
    transform: rotate(-100deg);
  }
}
.up{
  border-top-left-radius: 50%;
  border-top-right-radius: 50%;
  &::after{
    top: -0.5rem;
    left: 0.45rem;
    transform: rotate(90deg);
  }
  &::before{
    top: -0.55rem;
    left: 0.4rem;
    transform: rotate(-5deg);
  }
}
.right{
  border-top-right-radius: 50%;
  border-bottom-right-radius: 50%;
  &::after{
    top: -0.4rem;
    left: 0.45rem;
  }
  &::before{
    top: 0.2rem;
    left: 1.15rem;
    transform: rotate(80deg);
  }
}
.down{
  border-bottom-right-radius: 50%;
  border-bottom-left-radius: 50%;
  &::after{
    top: 0;
    left: 0.45rem;
    transform: rotate(90deg);
  }
  &::before{
    top: 1.05rem;
    left: 0.4rem;
    transform: rotate(170deg);
  }
}

#gameOver{
  z-index: 2;
  display: none;
  position: absolute;
  top: 50%;
  width: 100%;
  transform: translateY(-50%);
  text-align: center;
  color: rgb(150,0,0);
  font-size: 4rem;
  font-family: sans-serif;
  font-weight: bold;
  text-shadow: 0.1rem 0 0 white, -0.1rem 0 0 white, 0 0.1rem 0 white, 0 -0.1rem 0 white, 0.1rem 0.1rem 0 white, -0.1rem -0.1rem 0 white, 0.1rem -0.1rem 0 white, -0.1rem 0.1rem 0 white;
}

#options{
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  align-items: center;
  &>div{
    width: 10rem;
    display: flex;
    flex-direction: column;
    padding: 0.25rem 1.25rem;
    background-color: rgba(0,0,0,0.75);
    div{
      input{
        cursor: pointer;
        &:hover+span{
          color: green;
        }
      }
      span{
        position: relative;
        top: -0.1rem;
        color: white;
        font-family: sans-serif;
        font-size: 0.75rem;
      }
    }
  }
}

[type="button"]{
  width: 10rem;
  height: 3rem;
  border: none;
  margin: 2rem 0 2rem;
  color: white;
  font-size: 1.5rem;
  font-family: "Nova Mono", monospace;
  background-color: rgba(0,0,0,0.75);
  cursor: pointer;
  transition: color 0.5s, background-color 0.5s;
  &:hover{
    color: green;
    background-color: rgba(0,0,0,0.85);
  }
}
            
          
!
            
              $(function(){ //Start of jQuery
  
  
    /////////////
   //VARIABLES//
  /////////////
  
  //keyCode correspondent directions, relative to top and left
  var keyTable = {
    37: ["left", -1], //Go left
    38: ["top", -1], //Go up
    39: ["left", 1], //Go right
    40: ["top", 1] //Go down
  };
  
  //Sounds
  var appleSound = new Audio("http://www.freesound.org/people/jeremysykes/sounds/341227/download/341227__jeremysykes__powerup.wav");
  var gameOverSound = new Audio("http://www.freesound.org/people/myfox14/sounds/382310/download/382310__myfox14__game-over-arcade.wav");
  var specialAppleSound = new Audio("http://163.172.159.230/star.mp3");
  appleSound.volume = 0.25;
  gameOverSound.volume = 1;
  specialAppleSound.volume = 0.5;
  
  //Other variables
  var appleTime,
      currentKey = [39],
      gameAreaX = 30,
      gameAreaY = 30,
      loopCounter,
      loopSpeed = 100,
      mainLoopInterval,
      oldPosition,
      score,
      scoreMultiplier = 1,
      specialApplePossible,
      specialClass,
      specialSnakeTimeout,
      time,
      timerLoopInterval;
      

    /////////////
   //FUNCTIONS//
  /////////////

  //rem to px
  function remToPx(rem){    
    return rem * parseFloat(getComputedStyle(document.documentElement).fontSize);
  }

  //Move snake
  function moveSnake(){
    //Get new position of head from key pressed buffer (currentKey)
    var newDirection = keyTable[currentKey[0]][0];
    var newDistance = keyTable[currentKey[0]][1];
    //As long as key pressed buffer has more than one entry, remove the one previously used
    if(currentKey.length > 1){
      currentKey.shift();
    }
    //Add a head part to the snake and remove the last tail part
    $("#snake").append("<div class='" + specialClass + "'></div>");
    $("#snake div:first-of-type").remove();
    //Position the newly created head part where the previous head was
    $("#snake div:last-of-type").css({
      "top": oldPosition["top"] + "rem",
      "left": oldPosition["left"] + "rem"
    });
    //Move the newly created head part in the direction given by the key press
    $("#snake div:last-of-type").css(newDirection, "+=" + newDistance + "rem");
    //Change the reference coordinates for the placement of the next new head
    oldPosition[newDirection] += newDistance;
  }
  
  //Change head orientation depending on direction of the snake
  function snakeHead(){
    //Remove all head direction classes
    $("#snake div").removeClass("left up right down");
    //Give a head direction class to the head
    switch(currentKey[0]){
      case 37: 
        $("#snake div:last-of-type").addClass("left");
        break;
      case 38: 
        $("#snake div:last-of-type").addClass("up");
        break;
      case 39: 
        $("#snake div:last-of-type").addClass("right");
        break;
      case 40: 
        $("#snake div:last-of-type").addClass("down");
        break;
    }
  }

  //Randomly spawns an apple in  the area
  function randomApple(special){
    var snakeParts = $("#snake div");
    var correctApplePos = false;
    var typeOfApple;
    var randomAppleX, randomAppleY;
    //If apple is spawned on top of the snake, try another random position
    while(correctApplePos == false){
      correctApplePos = true;
      //Randomly generate X and Y coordinatesfor the apple, within the game area
      randomAppleX = Math.round(Math.random()*(gameAreaX - 3));
      randomAppleY = Math.round(Math.random()*(gameAreaY - 3));
      //For each tail part, checks if the head is in the same position, if true game over
      for(var x = 0; x < snakeParts.length; x ++){
        if(snakeParts[x].offsetTop == remToPx(randomAppleY) && snakeParts[x].offsetLeft == remToPx(randomAppleX)){
          correctApplePos = false;
        }
      }
    }
    //Create special apple
    if(special == true){
      $("main").append("<div class='specialApple'></div>");
      typeOfApple = ".specialApple";
    }
    //Create normal apple
    else{
      $("main").append("<div class='normalApple'></div>");
      typeOfApple = ".normalApple";
      //Timer for bonus points depending on time take to eat apple
      appleTime = Date.now();
    }
    //Put apple in place
    $(typeOfApple).css({
      "top": randomAppleY - 2 + "rem",
      "left": randomAppleX - 2 + "rem"
    });
    //Animate apple spawn
    $(typeOfApple).animate({
      "width": "1rem",
      "height": "1rem",
      "top": "+=2rem",
      "left": "+=2rem"
    },100);
  }
  
  //Random chance to spawn a special apple
  function randomSpecialApple(){
    var randomChance = Math.round(Math.random()*150);
    if(randomChance == 50){
      randomApple(true);
      specialApplePossible = false;
    }
  }

  //Detects collision against the walls
  function boxCollision(){
    var divPos = $("#snake div:last-of-type").position();
    //If the snake tries to go out of bounds, game over
    if(divPos.left < 0 || divPos.top < 0 || divPos.left > $("main").width() - remToPx(1) || divPos.top > $("main").height() - remToPx(1)){
      gameOver();
    }
  }

  //Detects collision with apple
  function appleCollision(){
    //Snake head and apple current positions
    var divPos = $("#snake div:last-of-type").position();
    var applePos = $(".normalApple").position();
    //If the head is in the same position as the apple, add score, generate a new apple and lengthen the snake
    if(divPos.left == applePos.left && divPos.top == applePos.top){
      //Play sound
      playSound(appleSound);
      //Score display
      var timeNow = Date.now();
      if(timeNow - appleTime > 5000){
        score += scoreMultiplier * 5;
      }
      else{
        score += scoreMultiplier * (10 - Math.round((timeNow - appleTime)/1000));
      }
      var formattedScore = formatNumbers(score);
      $("#score").text(formattedScore);
      //Remove eaten apple
      $(".normalApple").remove();
      //Add a tail part to the snake, making it longer
      var tailPos = $("#snake div:first-of-type").position();
      $("#snake div:first-of-type").before("<div class='specialSnake'></div>");
      $("#snake div:first-of-type").css({
        "top": tailPos.top,
        "left": tailPos.left
      });
      //Generate new apple
      randomApple(false);
    }
  }
  
  //Detects collision with special apple
  function specialAppleCollision(){
    //Snake head and apple current positions
    var divPos = $("#snake div:last-of-type").position();
    var specialApplePos = $(".specialApple").position();
    //If the head is in the same position as the apple, add score, generate a new apple and lengthen the snake
    if(divPos.left == specialApplePos.left && divPos.top == specialApplePos.top){
      //Play sound
      playSound(specialAppleSound);
      //Add score multiplier
      scoreMultiplier = 2;
      //Remove eaten apple
      $(".specialApple").remove();
      //Add special class to snake
      $("#snake div").removeClass("normalSnake").addClass("specialSnake");
      specialClass = "specialSnake";
      //Show x10 multiplier text
      $(".multiplier").show();
      //Add 15s timer to remove multiplier and add possibility to get special apples again
      specialSnakeTimeout = setTimeout(function(){
        scoreMultiplier = 1;
        specialAppleSound.pause();
        specialApplePossible = true;
        $("#snake div").removeClass("specialSnake").addClass("normalSnake");
        specialClass = "normalSnake";
        $(".multiplier").hide();
      }, 15000);
    }
  }

  //Detects collision with tail
  function tailCollision(){
    var headPos = $("#snake div:last-of-type").position();
    var nbOfParts = $("#snake div").length -1;
    //For each tail part, checks if the head is in the same position, if true game over
    for(var x = 0; x < nbOfParts; x++){
      if(headPos.top == $("#snake div")[x].offsetTop && headPos.left == $("#snake div")[x].offsetLeft){
        gameOver();
      }
    }
  }

  //Game over
  function gameOver(){
    //Deactivate key listener
    $(document).off("keydown");
    //Play sound
    playSound(gameOverSound);
    //Stop specialApple sound if playing
    specialAppleSound.pause();
    //Stop loops
    clearInterval(mainLoopInterval);
    clearInterval(timerLoopInterval);
    clearTimeout(specialSnakeTimeout);
    //Death animation
    for(var i = 0; i < 3; i++){
      $("#snake").hide(200).show(200);
    }
    //Game over text, display options and play button again
    setTimeout(function(){
      $("#gameOver").fadeIn(1000);
    }, 1200);
    setTimeout(function(){
      $("#options, [type='button']").fadeIn();
    }, 1800);
  }
  
  //Format time and score to a fixed length number, adding enough 0s in front depending on the number length
  function formatNumbers(number){
    return "00000".substring(0, 5 - number.toString().length) + number;
  }
  
  //Play sound
  function playSound(soundFile){
    soundFile.currentTime = 0;
    soundFile.play();
  }

    /////////////
   ////LOOPS////
  /////////////

  //Main loop
  function mainLoop(){
    moveSnake();
    if(specialApplePossible == true){
      randomSpecialApple();
    } 
    appleCollision();
    if($(".specialApple").length > 0){
      specialAppleCollision();
    }
    boxCollision();
    snakeHead();
    //Only starts detecting tail collision after 10 steps because all the snake parts start in the same position
    if(loopCounter > 10){
      tailCollision();
    }
    loopCounter ++;
  }
  
  //Timer loop
  function timerLoop(){
    //Time display
    time ++;
    var formattedTime = formatNumbers(time);
    $("#timer").text(formattedTime);
  }

    ///////////////////
   //EVENT LISTENERS//
  ///////////////////

  //Options event listener
  $("[type='checkbox']").on("change", function(){
    //If box is checked, make game area smaller
    if($(this).attr("name") == "smallArea" && $(this)[0].checked == true){
      $("main").css({
        "width": "20rem",
        "height": "20rem"
      });
      gameAreaX = 20;
      gameAreaY = 20;
      $("#gameOver").hide();
      $("#snake div").remove();
      $("main>div").remove();
    }
    //If box is unchecked, make game area regular sized
    else if($(this).attr("name") == "smallArea" && $(this)[0].checked == false){
      $("main").css({
        "width": "30rem",
        "height": "30rem"
        
      });
      gameAreaX = 30;
      gameAreaY = 30;
      $("#gameOver").hide();
      $("#snake div").remove();
      $("main>div").remove();
    }
    //If box checked, make snake faster
    else if($(this).attr("name") == "fastSnake" && $(this)[0].checked == true){
      loopSpeed = 75;
    }
    //If box unchecked, give snake regular speed
    else if($(this).attr("name") == "fastSnake" && $(this)[0].checked == false){
      loopSpeed = 100;
    }
  });
  
  //Keyboard event listener
  function keyboardListener(){
    $(document).on("keydown", function(event){
      //Prevent any default behaviour of key presses
      event.preventDefault();
      //Add key pressed to the key buffer only if it's an allowed key (arrows)
      if(keyTable[event.keyCode]){
        currentKey.push(event.keyCode);
      }
    });
  }
  
  //Play button event listener
  $("[type='button']").on("click", function(event){
    //Prevent any default behaviour of button press
    event.preventDefault();
    //Hide play button, options, game over text and score multiplier
    $("#options, [type='button'], #gameOver, .multiplier").hide();
    //Reset various variables 
    specialApplePossible = true;
    specialClass = "normalSnake";
    loopCounter = 0;
    score = 0;
    time = 0;
    currentKey = [39];
    oldPosition = {
    "top" : 0,
    "left" : 0
    };
    //Remove special apple
    $(".specialApple").remove();
    //Place snake coordinates at the center of the game area
    $("#snake div").css({
      "top": 0,
      "left": 0
    });
    //Reset snake length
    $("#snake div").remove();
    $("#snake").append("<div class='normalSnake'></div><div class='normalSnake'></div><div class='normalSnake'></div><div class='normalSnake'></div><div class='normalSnake'></div><div class='normalSnake'></div><div class='normalSnake'></div>");
    //Generate the first apple
    randomApple(false);
    //Launch key listener
    keyboardListener();
    //Start loops
    mainLoopInterval = setInterval(mainLoop, loopSpeed);
    timerLoopInterval = setInterval(timerLoop, 1000);
  });
  
}); //End of jQuery
            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.

Console