Web Audio API Test

HTML

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/css/materialize.min.css">
<br>

	<div class="options row">
		<!-- <div class="col s12">
			<span>
				<input type="checkbox" id="sus">
				<label for="sus">Sustain</label>
			</span>
		</div> -->		
		
		<span>
			<label class="btn grey" for="record">			
				Record
			</label>
			<input type="checkbox" id="record">
		</span>

		<span>
			<label class="btn grey" id="playback" for="">
				Play
			</label>
		</span>

	</div>
	
	<div id="piano">
	  <div class="blacks">
	    	    	<div class="key" data-octave="3" data-note="C#" data-freq="138.6"></div>
	    	    	<div class="key" data-octave="3" data-note="D#" data-freq="155.6"></div>
	    	    	<div class="key" data-octave="3" data-note="F#" data-freq="185"></div>
	    	    	<div class="key" data-octave="3" data-note="G#" data-freq="207.7"></div>
	    	    	<div class="key" data-octave="3" data-note="A#" data-freq="233.1"></div>
	    	    	<div class="key" data-octave="4" data-note="C#" data-freq="277.2"></div>
	    	    	<div class="key" data-octave="4" data-note="D#" data-freq="311.1"></div>
	    	    	<div class="key" data-octave="4" data-note="F#" data-freq="370"></div>
	    	    	<div class="key" data-octave="4" data-note="G#" data-freq="415.3"></div>
	    	    	<div class="key" data-octave="4" data-note="A#" data-freq="466.2"></div>
	    	  </div>
	  <div class="whites">
	    	    	<div class="key" data-octave="3" data-note="C" data-freq="130.8"></div>
	    	    	<div class="key" data-octave="3" data-note="D" data-freq="146.8"></div>
	    	    	<div class="key" data-octave="3" data-note="E" data-freq="164.8"></div>
	    	    	<div class="key" data-octave="3" data-note="F" data-freq="174.6"></div>
	    	    	<div class="key" data-octave="3" data-note="G" data-freq="196"></div>
	    	    	<div class="key" data-octave="3" data-note="A" data-freq="220"></div>
	    	    	<div class="key" data-octave="3" data-note="B"...

SCSS

$octaves: 2;
$white_keys: $octaves * 7;
$black_keys: $octaves * 5;
$white_keys_width: calc(100% / #{$white_keys});
$black_keys_width: calc(100% / #{$white_keys} * .75);
html,
body {
  background-color: #fafafa;
}

#piano {
  position: relative;
  width: 800px;
  height: 200px;
  background-color: #fff;
  border: 1px solid silver;
}

.whites {
  position: relative;
  width: 100%;
  height: 100%;
}

.blacks {
  width: 100%;
  height: 75%;
  position: absolute;
  z-index: 10;
  margin-left: calc(100% / 14 * .625);
  .key {
    width: calc(100% / 14 * .75); // 14 * 1.5 = 24.5
    background-color: #333;
    border: none;
    margin-right: calc((100% / 14) - (100% / 14 * .75));
    &:nth-child(5n+2),
    &:nth-child(5n+5) {
      margin-right: calc((100% / 14) + (100% / 14) - (100% / 14 * .75));
    }
    &[aria-pressed] {
      background-color: #000;
    }
  }
}

.key {
  height: 100%;
  width: $white_keys_width;
  float: left;
  box-sizing: border-box;
  border-left: 1px solid silver;
  &:first-child {
    border-left: none;
  }
  &[aria-pressed] {
    background-color: #eee;
  }
}

JavaScript

var keys = document.querySelectorAll('.key'),
	sustain = document.querySelector('#sus'),
	record = document.querySelector('#record'),
	recordBtn = document.querySelector('[for="record"]'),
	play = document.querySelector('#playback');

var context = new AudioContext(),
	notes = {},
	isRecording = false,
	isPlayback = false,
	_record = { start : null, sequence : [] };


record.addEventListener('change',function () {	

	if ( isPlayback ) {
		this.checked = false;
		return;
	}

	isRecording = this.checked;
	if ( isRecording ) {
		// new record evertime record is activated
		_record.start = Date.now();
		_record.sequence = [];
		recordBtn.className = recordBtn.className.replace('grey','red');
	} else {
		recordBtn.className = recordBtn.className.replace('red','grey');
		console.log('_record',_record);
	}
});

play.addEventListener('click',function () {	
	if ( _record.sequence.length ) {
		isPlayback = true;
		console.log('playing');
		_record.sequence.forEach(function (_note,_index) {
			setTimeout(function () {
				notes[_note.noteId].play();
				if ( _index == _record.sequence.length - 1 ) {
					isPlayback = false;
					console.log('stopped');
				}
			},_note.delay);
		});
	}
});


[].forEach.call(keys,function(el){

	var _note = {};

	_note.el = el;
	_note.f = el.getAttribute('data-freq');
	_note.octave = el.getAttribute('data-octave');
	_note.note = el.getAttribute('data-note');
	_note.id = _note.octave+_note.note;
	_note.uiTimeout = null,
	_note.oscillator = null,
	_note.gainNode = null;

	_note.play = function () {

		if ( _note.oscillator ) return;

		_note.oscillator = context.createOscillator();
	    _note.gainNode = context.createGain();

	    // connect
	    _note.oscillator.connect(_note.gainNode);
	    _note.gainNode.connect(context.destination);

	    _note.oscillator.frequency.value = _note.f;
	    _note.gainNode.gain.value = 0.15;
	    _note.oscillator.start(context.currentTime);

	    // check use of sustain
	    // if ( !sustain.checked ) {
	   ...