sequencer

by kthornbloom

HTML

<div id="sampler">
  <div id="sampler-mode">
    <div id="sampler-mode-a" class="sampler-mode-visible">A</div>
    <div id="sampler-mode-b">B</div>
    <div id="sampler-mode-c">C</div>
    <div id="sampler-mode-d">D</div>
  </div>
  <div id="sampler-pad-wrap">
    <div id="sampler-a" class="sampler-pad-visible"></div>
    <div id="sampler-b"></div>
    <div id="sampler-c"></div>
    <div id="sampler-d"></div>
  </div>
  <div id="sampler-control">
    <div>
      START
    </div>
  </div>
</div>

<audio src="https://freesound.org/data/previews/183/183098_2394245-lq.mp3" id="sampler-a-audio"></audio>
<audio src="https://freesound.org/data/previews/35/35418_19852-lq.mp3" id="sampler-b-audio"></audio>
<audio src="https://freesound.org/data/previews/273/273186_4965320-lq.mp3" id="sampler-c-audio"></audio>
<audio src="https://freesound.org/data/previews/24/24787_165785-lq.mp3" id="sampler-d-audio"></audio>

SCSS

// parent
#sampler {
  display: flex;
  font-family: sans-serif;
}

// Instrument Selector
#sampler-mode {
  background: #ccc;
  border-radius: 3px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  margin-right: 4px;
}

#sampler-mode:hover {
  cursor: pointer;
  background: #eee;
}

// CONTROLLER
#sampler-control {
  height: 40px;
  background: #ccc;
  font-size: 11px;
  border-radius: 3px;
  padding: 0 4px;
  display: flex;
  align-items: center;
  text-align: center;
  hr {
    margin: 0;
    border: 0;
    border-top: 1px solid rgba(0,0,0,.5);
    width: 50px;
  }
}

// Pad groups
#sampler-pad-wrap {
  font-size: 0;
  line-height: 0;
}

#sampler-pad-wrap > div,
#sampler-mode > div {
  display: none;
}

// Pad buttons
.sequencer-pad {
  border-radius: 2px;
  display: inline-block;
  width: 15px;
  height: 30px;
  background: #DF1402;
  margin-right: 4px;
  position: relative;
 
  &:before {
    content:'';
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    background: linear-gradient(to bottom, transparent 20%, rgba(0, 0, 0, 0.25) 35%, rgba(255, 255, 255, 0.3) 40%, rgba(255, 255, 255, 0.1), transparent, rgba(255, 255, 255, 0.3));
    border-radius: 2px;
    box-shadow: inset 0 1px 0 rgba(255,255,255,.3), inset 0 -1px 0 rgba(0,0,0,.3), 0 0 0 1px #555, 0 6px 4px #bbb;
  }
  &:after {
    content:'';
    display: block;
    width: 7px;
    height: 7px;
    background: radial-gradient(circle at 5px 2px, #fff, #963333 20%, #523838);
    border-radius: 50%;
    position: absolute;
    top: 3px;
    left: 50%;
    margin-left: -4px;
  }
}

.sequencer-pad:nth-child(1n + 3){background: #F47500;}
.sequencer-pad:nth-child(1n + 5){background: #E0DD04;}
.sequencer-pad:nth-child(1n + 7){background: #efeace;}

.pad-active {
  &:after {
    background:radial-gradient(circle at 5px 2px, #fff, red 20%, #ffa1b7);
    box-shadow: 0 0 5px red, 0 0 6px red;
  }
}

.selected {
  &:after {
   ...

JavaScript

// Setup
const beats = 8;
var bpm = 200;
var runit = null;
bpm = (60000 / bpm) / 2; //convert to MS
const aContainer = document.getElementById('sampler-a'); 
const aAudio = document.getElementById('sampler-a-audio');
const bContainer = document.getElementById('sampler-b'); 
const bAudio = document.getElementById('sampler-b-audio');
const cContainer = document.getElementById('sampler-c'); 
const cAudio = document.getElementById('sampler-c-audio');
const dContainer = document.getElementById('sampler-d'); 
const dAudio = document.getElementById('sampler-d-audio');
var interval = 0;
var currentActive = null;

// Make Pads
for (i = 0; i < beats; i++) {
	var aNode = document.createElement("DIV");  
  aNode.className = "sequencer-pad";
	aContainer.appendChild(aNode);
  var bNode = aNode.cloneNode(true);
  bContainer.appendChild(bNode);
  var cNode = aNode.cloneNode(true);
  cContainer.appendChild(cNode);
  var dNode = aNode.cloneNode(true);
  dContainer.appendChild(dNode);
}

// Define Pad Groups
const aPads = document.querySelectorAll('#sampler-a .sequencer-pad');
const bPads = document.querySelectorAll('#sampler-b .sequencer-pad');
const cPads = document.querySelectorAll('#sampler-c .sequencer-pad');
const dPads = document.querySelectorAll('#sampler-d .sequencer-pad');


// Player
function player(container, beat, pads, audio){
	var currentActive = container.querySelectorAll('.pad-active');
  if(currentActive.length > 0){
  	currentActive[0].classList.remove('pad-active');
  }
  pads[beat].classList.add('pad-active');
  if(pads[beat].classList.contains('selected')){
  	audio.currentTime = 0;
    audio.play();
  }
}

// Toggle Pads
var pads = document.querySelectorAll('.sequencer-pad');
Array.from(pads).forEach(function(pad) {
  pad.addEventListener('click', function(){
    this.classList.toggle('selected');
  });
});

// Instrument Switcher
var samplerSwitch = document.getElementById('sampler-mode');
var maxSwitches = document.querySelectorAll('#sampler-mode >...