Pen Settings

HTML

CSS

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

JavaScript

Babel is required to process package imports. If you need a different preprocessor remove all packages first.

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

Behavior

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.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <div class="main">
  <div class="backboard"></div>
  <div class="strict-label">strict mode</div>
  <div class="switch-container">
    <label class="switch">
      <input id = "strict-toggle" type="checkbox">
      <div class="slider round"></div>
      </label>
  </div>
  <div class="strike-label">strike mode</div>
  <div class="strikes"></div>
  <div class="backcircle"></div>
  <div class="quadrant1 simon-button" data-id="0"></div>  
  <div class="quadrant2 simon-button" data-id="1"></div>
  <div class="quadrant3 simon-button" data-id="2"></div>
  <div class="quadrant4 simon-button" data-id="3"></div>
  <div class="center">
    <div class="message" id="message">you did it!</div>
    <button id="start-button" class="start-button">start</button>
  </div>
  <div class="progress"></div>
</div>
<div id ="credits">designed and coded by <a href="https://yuzu-r.github.io" target="_blank">yuzu-r</a></div>
              
            
!

CSS

              
                $backboard-padding: 8px;
$max-steps: 20;  /* this needs to stay in sync w simon! */
$start-font-family: 'Orbitron', sans-serif;
$message-font-family: 'Montez', cursive; 
$slider-color: #4E4F54;
$slider-button-color: white;
$message-color: black;
$progress-bubble-color: white;
$empty-strike-bubble-color: #4E4F54;
$start-button-color: white;
$center-color: #D8DBE2;
$empty-progress-bubble-color: #4E4F54;
$back-color: #0F0707;

$desktop-width: 520px;

@mixin styling( $simon-board-size, $simon-border, 
                $center-border, $start-button-font-size, 
                $label-width, $label-size,
                $message-font-size, $strikes-width) {
  .main {
    width: $simon-board-size;
    height: $simon-board-size;
  }
  .backboard {
    width: 2* $simon-board-size + $backboard-padding;
    height: $simon-board-size + $backboard-padding;
    border-radius: ($simon-board-size+$backboard-padding)/4;    
  }
  #credits {
    width: 2* $simon-board-size + $backboard-padding;
  }
  .backcircle {
    left: -$backboard-padding/2;
    height: $simon-board-size + $backboard-padding;
    width:  $simon-board-size + $backboard-padding;
    border-radius: ($simon-board-size + $backboard-padding)/2;   
  }
  .simon-button {
    border: $simon-border;
    opacity: .75;
  }
  .center {
    border: $center-border;
  }
  .start-button {
    border-radius: $simon-board-size * .12;
    font-size: $start-button-font-size;
  }
  .strict-label {
    width: $label-width;    
    font-size: $label-size;
  }
  .strike-label {
    width: $label-width;
    font-size: $label-size;    
  }
  #message {
    font-size: $message-font-size;
  }
  .progress {
    height: $simon-board-size - $backboard-padding ;
    width: ($simon-board-size - $backboard-padding) / $max-steps;   
  }
  .strikes {
    height: 3 * $strikes-width;
    width: $strikes-width;    
  }
  .strike-bubble {
    width: $strikes-width;
    height: $strikes-width;   
  }
  .progress-bubble {
    width: ($simon-board-size - $backboard-padding) / $max-steps;
    height: ($simon-board-size - $backboard-padding) / $max-steps;  
  }
  .switch-container {
    height: $strikes-width;
    width: $strikes-width*2;
  }
  .switch {
    width: $strikes-width*2;
    height: $strikes-width;   
  }
  .slider:before {
    height: $strikes-width * .98;
    width: $strikes-width * .98;
  }
  input:checked + .slider:before {
    -webkit-transform: translateX($strikes-width);
    -ms-transform: translateX($strikes-width);
    transform: translateX($strikes-width);
  }
  .slider.round {
    border-radius: $strikes-width;
  }
}

@media (max-width: $desktop-width - 1px) {
  @include styling( $simon-board-size: 140px, 
                    $simon-border: 2px solid black, 
                    $center-border: 3px solid, 
                    $start-button-font-size: 12px, 
                    $label-width: 65px, $label-size: 10px, 
                    $message-font-size: 12px, $strikes-width: 13px);
  .backboard {
    background-color: $back-color;
    // border: 2px solid red;
  }
}

@media (min-width: $desktop-width) {
  @include styling( $simon-board-size: 320px, 
                    $simon-border: 4px solid black, 
                    $center-border: 7px solid, 
                    $start-button-font-size: 22px, 
                    $label-width: 130px, $label-size: 16px, 
                    $message-font-size: 24px, $strikes-width: 26px);
  .backboard {
    background-color: $back-color;
  }
}

.body {
  -webkit-touch-callout: none;
  -webkit-user-select: none; 
}
.main {
  position: relative;
  margin: 0 auto;
}
div {
  box-sizing: border-box;
}
.backboard {
  position: absolute;
  top: -2%;
  left: -50%; 
}
.backcircle {
  position: absolute;
  top: -2%;
  background-color: $back-color;
}
.simon-button {
  position: absolute;
  width: 50%;
  height: 50%;
  cursor: pointer;
  text-align: center;
  /* get rid of highlight which exposes simon buttons as circles on touchscreens */
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 
  -webkit-tap-highlight-color: transparent;
}
.quadrant1 {
  top: 0;
  left: 0;
  background-color: red;
  border-radius: 100% 0 0 0;
  border-style: solid;
  border-color: $back-color;
}
.quadrant2 {
  top: 0;
  right: 0;
  background-color: #07BC4C;
  border-radius: 0 100% 0 0;
  border-style: solid;
  border-color: $back-color;
}
.quadrant3 {
  bottom: 0;
  left: 0;
  background-color: yellow;
  border-radius: 0 0 0 100%;
  border-style: solid;
  border-color: $back-color; 
}
.quadrant4 {
  bottom: 0;
  right: 0;
  background-color: blue;
  border-radius: 0 0 100% 0;
  border-style: solid;
  border-color: $back-color;
}
.center {
  width: 50%;
  height: 50%;
  background-color: $center-color;
  position: absolute;
  top: 25%;
  left: 25%;
  border-radius: 50%;
}
.start-button {
  position: absolute;
  left: 1%;
  top: 37%;
  width: 98%;
  height: 1.25em;
  background-color: $start-button-color;
  cursor: pointer;
  font-family: $start-font-family; 
  text-align: center;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 
  -webkit-tap-highlight-color: transparent;
}
.start-button:focus {
  outline: none;
}
.start-button:active {
  background-color: #4E4F54;
  color: white;  
}
.strict-label {
  color: white;
  text-align: center;
  height: 20px;
  position: absolute;
  top: 17%;
  left: -49%;
  font-family: $start-font-family;
}
.strike-label {
  color: white;
  text-align: center;
  height: 20px;
  position: absolute;
  top: 49%;
  left: -49%;
  font-family: $start-font-family;  
}
#message {
  position: absolute;
  left: 20%;
  top: 12%;
  font-family: $message-font-family;
  color: $message-color;
}

.progress {
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: column-reverse;
  flex-direction: column-reverse;
  -webkit-align-items: stretch;
  align-items: stretch;
  position: absolute;
  left: 115%;
  top: 4px;
}
.strikes {
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: column-reverse;
  flex-direction: column-reverse;
  -webkit-align-items: stretch;
  align-items: stretch;
  position: absolute;
  left: -30%;
  top: 57%;
  -webkit-transition: opacity .4s;
  transition: opacity .4s;
  opacity: 1;
}
.strikes.hide {
  opacity: 0;
}
.strike-bubble {
  border-radius: 50%;
  background-color: $empty-strike-bubble-color; 
}
.strike-bubble.filled {
  background-color: red;
}
.progress-bubble {
  border-radius: 50%;
  background-color: $empty-progress-bubble-color;
}
.progress-bubble.filled {
  background-color: $progress-bubble-color;
}

/* The switch - the box around the slider */
.switch {
  position: absolute;
  display: inline-block;
  left: -34%;
  top: 30%;
  transform:rotate(90deg); 
}

/* Hide default HTML checkbox */
.switch input {display:none;}

/* The slider */
.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: $slider-color;
  -webkit-transition: .4s;
  transition: .4s;
}

.slider:before {
  position: absolute;
  content: "";
  background-color: $slider-button-color;
  -webkit-transition: .4s;
  transition: .4s;
}

input:checked + .slider {
  background-color: $slider-color;
}

input:focus + .slider {
  box-shadow: 0 0 1px $slider-color;
}

.slider.round:before {
  border-radius: 50%;
}

#credits{
  padding-top: 10px;
  font-family: $start-font-family;
  font-size: 8pt;
  text-align: center;
  margin: 0 auto;
}
              
            
!

JS

              
                // first things first.. audioContext is not supported by IE, so bail right away if needed
var ms_ie = false;
var ua = window.navigator.userAgent;
var old_ie = ua.indexOf('MSIE ');
var new_ie = ua.indexOf('Trident/');

if ((old_ie > -1) || (new_ie > -1)) {
  ms_ie = true;
}

if ( ms_ie ) {
  var alertText = "Sorry, this game is not supported for Internet Explorer.";
  alertText += " Use with Firefox, Chrome, or Safari.";
  alert(alertText);
  $("#start-button").on('click', function() {
    alert(alertText); 
  })  
}
else {
  var Simon = function() {
    var maxSteps = 20; // codepen asks for 20
    var series = [];
    var playerSeries=[];
    var numStrikes = 0;  // non-strict mode: allow 3 tries at the series
    var maxStrikes = 3;  // this and numStrikes only used if isStrict is false
    var isStrict = true; // strict mode fails the player if they miss
    var isPlayerTurn = false;
    var audioContext = new (window.AudioContext || window.webkitAudioContext || window.audioContext);
    var playerOscillator;
    var frequencies = [329.63, 440, 554.37, 659.25]; // E A C# E 
    var volume = 0.02; // gain goes from 0 to 1 but is very very loud
    function beep(duration, frequency, callback){
      var oscillator = audioContext.createOscillator();
      var gainNode = audioContext.createGain();
      oscillator.connect(gainNode);
      gainNode.connect(audioContext.destination);
      gainNode.gain.value = volume;
      oscillator.frequency.value = frequency;
      oscillator.type = "square";  // for that electronic toy sound
      if (callback) {
        oscillator.onended = callback;
        oscillator.disconnect;
      }
      oscillator.start();
      setTimeout(function(){oscillator.stop()}, duration);    
    }
    function playerPressStart(buttonId) {
      if (isPlayerTurn) {
        $(".simon-button[data-id='" + buttonId + "']").css("opacity", "1");
        playerStep(buttonId);
        playerOscillator = audioContext.createOscillator();
        var gainNode = audioContext.createGain();
        playerOscillator.connect(gainNode);
        playerOscillator.type = "square";
        gainNode.gain.value = volume;
        gainNode.connect(audioContext.destination);
        playerOscillator.frequency.value = frequencies[buttonId];
        playerOscillator.start(); 
      }
    }
    function playerPressStop(){
      if (playerOscillator) {
        playerOscillator.stop();
        playerOscillator.disconnect();
        resetColors();
      }    
    }
    function setMode() {
      // set isStrict value based on slider input
      // note: the slider is "on" when in the down (strike mode) position
      // the slider is unchecked when in the up (strict) position
      var elChecked = $("#strict-toggle")[0].checked;
      isStrict = !elChecked;
      if (isStrict) {
        $(".strikes").addClass("hide");
      }
      else {
        $(".strikes").removeClass("hide");
      }
    }
    function newStep() {
      // generate a random next note and add it to series
      // random int between 0 and 3
      series.push(Math.floor(Math.random() * (4)));
      console.log('series is now: ' + series);
    }
    function stepCount() {
      return series.length;
    }
    function playerStep(buttonId) {
      // figure out what point in the series we are
      var stepNumber = playerSeries.length;
      // figure out what note it should be matching
      var correctNote = series[stepNumber];
      if(buttonId==correctNote) {
         // add the player's button press to the player series
        playerSeries.push(buttonId);
        if(playerSeries.length == series.length && 
          maxSteps > series.length) {
          fillDot(".progress", ".progress-bubble");
          isPlayerTurn = false;
          newStep();
          playSeries(); 
          playerSeries = []; // restart their array
        }
        else if (playerSeries.length == maxSteps) {
          $("#message").text("you did it!");
          fillDot(".progress", ".progress-bubble");
          celebrate();
          $("#start-button").text("start");
        }
      } 
      else {
        //wrong move
        isPlayerTurn = false;
        if (isStrict == true) {
          showLoser();
          $("#message").text("game over!");
          $("#start-button").text("start");
        }
        else {
          numStrikes++;
          $("#message").text("  strike " + numStrikes + "!");
          fillDot(".strikes", ".strike-bubble");
          if (numStrikes == 3) {
            $("#message").text("game over!");
            showLoser();
            $("#start-button").text("start");
          }
          else {
            // game not over yet, replay the current series
            playSeries();
            playerSeries = [];
          }       
        }
      }
    }
    function playSeries(){
      isPlayerTurn = false;
      var maxLoops = stepCount()-1;
      var counter = 0;
      (function playIt() {
        if (counter > maxLoops) {
          $("#message").text("your turn: ");
          isPlayerTurn = true;
          return;
        } 
        setTimeout(function() {
          var note = series[counter];
          var button = $(".simon-button[data-id='"+note+"']");      
          button.css("opacity", "1");
          beep(700, frequencies[note], resetColors);
          counter++;
          $("#message").text("...listen...");
          playIt();
        }, 1000);
      })();
    }
    function resetGame(){
      numStrikes = 0;
      series = [];
      playerSeries = [];
      isPlayerTurn = false;
      // events alter the progress bubbles, remove and redraw them
      $(".progress-bubble").remove();
      for (var i=0; i < maxSteps; i++) {
        var elProgressBar = $(".progress");
        elProgressBar.append("<div class='progress-bubble'></div>");
      }
      // remove and redraw the strikes in case they were altered
      $(".strikes .strike-bubble").remove();
      for (var j= 0; j< maxStrikes; j++) {
        var elStrikeBar = $(".strikes");
        elStrikeBar.append("<div class='strike-bubble'></div>");
      }
      setMode();
      $("#message").text("let's play!!!");
    }
    function startGame(){
      resetGame();
      $("#start-button").text("restart");
      newStep();
      playSeries();
    }
    function showLoser() {
      // don't do a buzzer, that's obnoxious
      // disintegrate the progress bar instead
      var maxLoops = maxSteps-1;
      // base the interval depending on how many steps overall there are,
      // so if there are 20 steps it doesn't take a large amount of time to do
      var displayInt = 1000/maxLoops;
      var counter = 0;
      (function removeIt() {
        if (counter > maxLoops) {
          return;
        }
        setTimeout(function(){
          $(".progress-bubble").eq(maxLoops-counter).css("background-color", "#0F0707");
          counter++;
          removeIt();
        }, displayInt);
      })();
    }
    function celebrate() {
      // celebrate by filling the progress dots one at a time with the winning sequence
      // first clear the dots out
      resetDots(".progress", ".progress-bubble");
      var colors = ["red", "#07BC4C", "yellow", "blue" ]; // E A C# E
      var maxLoops = stepCount()-1;
      var counter = 0;
      (function showIt() {
        if (counter > maxLoops) {
          return;    
        } 
        setTimeout(function() {
          var colorIndex = series[counter];
          fillDotWithColor(".progress", ".progress-bubble", colors[colorIndex]);
          counter++;
          showIt();
        }, 500);
      })();
    }
    function resetColors(){
      // restore any button back to its original opacity
      $(".simon-button").css("opacity", ".75");
    }
    function resetDots(parentClass, childClass) {
      $(parentClass).children(childClass).removeClass("filled");
      // also clear the style field which might be populated after
      // a celebration (a win)
      $(parentClass).children(childClass).removeAttr("style");
    }
    function fillDot(parentClass, childClass) {
      // then look for childClass and determine if there is an unfilled child
      // (they should already be there - based on maxSteps, maxStrikes)
      // fill the next child if it can, and return true if it did
      // return false if it could not fill the next child (could not find/full)
      var elParent = $(parentClass);
      var elChildren = elParent.children(childClass);
      var numChildren = elChildren.length;
      for (var i = 0; i < numChildren; i++ ) {
        var childClass = elChildren[i].getAttribute('class');
        if (childClass.search("filled") === -1) {
          break;
        }
      }
      if (i == numChildren) {
        // all are filled
        return false;
      }
      var fillChild = elChildren[i];
      childClass = childClass + " filled";   
      fillChild.setAttribute("class", childClass);
      return true;
    }
    function fillDotWithColor(parentClass, childClass, color) {
      // pass in the class selector with the dot included
      var elParent = $(parentClass);
      var elChildren = elParent.children(childClass);
      var numChildren = elChildren.length;
      for (var i = 0; i < numChildren; i++ ) {
        var childClassAttribute = elChildren[i].getAttribute('class');
        if (childClassAttribute.search("filled") === -1) {
          break;
        }
      }
      if (i == numChildren) {
        // all are filled
        return false;
      }
      var fillChild = elChildren[i];
      childClassAttribute = childClassAttribute + " filled";   
      fillChild.setAttribute("class", childClassAttribute);
      $(childClass).eq(i).css("background-color", color);
      return true;
    }  
    return {
      setMode:setMode,
      resetGame:resetGame,
      startGame:startGame,
      playerPressStart:playerPressStart,
      playerPressStop:playerPressStop
    }
  }();  
  $(window).scrollTop(1);
  // get rid of highlight when touching div
  document.addEventListener("touchstart", function(){}, true);
  // stop scrolling on touchscreens 
  document.ontouchmove = function(e){ e.preventDefault(); }
  // add this to avoid seeing the titlebar when flipping from portrait to landscape mode
  window.addEventListener("resize", function() {$(window).scrollTop(1);});
  $("#strict-toggle").change(function() {
    Simon.setMode();
  });

  $("#start-button").on('click', function() {
    Simon.startGame();  
  })
  
  $(".simon-button").on('mousedown touchstart', function(e) {
    e.preventDefault(); // needed to prevent bad things on touches
    var buttonId = $(e.target).data('id');
    Simon.playerPressStart(buttonId);
  })

  $(".simon-button").on('mouseup touchend', function() { 
    Simon.playerPressStop();
  })

  $(".simon-button").on('mouseleave', function(){
    Simon.playerPressStop();
  });

  Simon.resetGame();
  
}


 


              
            
!
999px

Console