An Introduction to the Web MIDI API - Project

by Keith McMillen

HTML

<div id="content">
		<div class="button" data-key="q" data-sound="https://files.keithmcmillen.com/blog/mmitb/dinky-kick.mp3"></div>
		<div class="button" data-key="w" data-sound="https://files.keithmcmillen.com/blog/mmitb/dinky-snare.mp3"></div>
		<div class="button" data-key="e" data-sound="https://files.keithmcmillen.com/blog/mmitb/dinky-hat-2.mp3"></div>
		<div class="button" data-key="r" data-sound="https://files.keithmcmillen.com/blog/mmitb/dinky-cym.mp3"></div>
		<div class="button" data-key="t" data-sound="https://files.keithmcmillen.com/blog/mmitb/dinky-cym-noise.mp3"></div>
</div>
<div id="device_info">
    <div id="key_data"></div>
</div>

CSS

*:before, *:after {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
h4 {
    margin: 0 0 5px 0;
}
p {
    margin: 0 0 10px 0;
}
#content, #device_info {
    max-width: 800px;
    /*height: 125px;*/
    margin: 0 auto;
    padding: 10px 0;
    font-family: sans-serif;
    font-size: 12px;
    line-height: 12px;
    letter-spacing: 1.5px;
}
#content, #key_data {
    margin-top: 0px;
    text-align: center;
}
#inputs, #outputs {
    display: inline-block;
    width: 49%;
    margin-top: 10px;
    vertical-align: top;
}
#outputs {
    text-align: right;
}
.info {
    padding: 20px;
    border-radius: 3px;
    box-shadow: inset 0 0 10px #ccc;
    background-color: rgba(233, 233, 233, 0.25);
}
.small {
    border-bottom: 1px solid #ccc;
    margin-left: 10px;
}
p:not(.small) {
    text-transform: uppercase;
    font-weight: 800;
}
.button {
    display: inline-block;
    width: 100px;
    height: 100px;
    margin: 10px;
    background-color: #00adef;
    border-radius: 10px;
    opacity: 1;
    cursor: pointer;
    border: 2px solid white;
    transition: all 0.2s;
}
.button.active {
    background-color: #9a6aad;
    opacity: 0.25;
    box-shadow: inset 0px 0px 30px orange;
    border: 2px solid rgba(100, 100, 100, 0.3);
    animation: shake .2s ease-in-out;
}
@keyframes shake {
    0% {
        transform: translateX(0);
        transform: translateY(0);
        transform: scale(1, 1);
    }
    20% {
        transform: translateX(-10px);
        transform: translateY(-100px);
        transform: scale(0.5, 0.75);
    }
    40% {
        transform: translateX(10px);
        transform: translateY(0px);
        transform: scale(1.5, 2);
    }
    60% {
        transform: translateX(-10px);
        transform: translateY(-50px);
        transform: scale(0.5, 0.75);
    }
    80% {
        transform: translateX(10px);
        transform: translateY(50px);
        transform: scale(1.3, 2);
    }
    100% {
        transform:...

JavaScript

var log = console.log.bind(console),
  keyData = document.getElementById("key_data"),
  midi
var AudioContext
var context
var btnBox = document.getElementById("content"),
  btn = document.getElementsByClassName("button")
var data, cmd, channel, type, note, velocity

try {
  AudioContext = window.AudioContext || window.webkitAudioContext // for ios/safari
  context = new AudioContext()
} catch (e) {
  alert("Web Audio API is not supported in this browser")
}
// request MIDI access
if (navigator.requestMIDIAccess) {
  navigator
    .requestMIDIAccess({
      sysex: false,
    })
    .then(onMIDISuccess, onMIDIFailure)
} else {
  alert("No MIDI support in your browser.")
}

// add event listeners
document.addEventListener("keydown", keyController)
document.addEventListener("keyup", keyController)
for (var i = 0; i < btn.length; i++) {
  btn[i].addEventListener("mousedown", clickPlayOn)
  btn[i].addEventListener("mouseup", clickPlayOff)
}
// prepare audio files
for (var i = 0; i < btn.length; i++) {
  addAudioProperties(btn[i])
}
// this maps the MIDI key value (60 - 64) to our samples
var sampleMap = {
  key60: 1,
  key61: 2,
  key62: 3,
  key63: 4,
  key64: 5,
}
// user interaction, mouse click
function clickPlayOn(e) {
  e.target.classList.add("active")
  e.target.play()
}

function clickPlayOff(e) {
  e.target.classList.remove("active")
}
// qwerty keyboard controls. [q,w,e,r,t]
function keyController(e) {
  if (e.type == "keydown") {
    switch (e.keyCode) {
      case 81:
        btn[0].classList.add("active")
        btn[0].play()
        break
      case 87:
        btn[1].classList.add("active")
        btn[1].play()
        break
      case 69:
        btn[2].classList.add("active")
        btn[2].play()
        break
      case 82:
        btn[3].classList.add("active")
        btn[3].play()
        break
      case 84:
        btn[4].classList.add("active")
        btn[4].play()
        break
      default:
      //console.log(e);
    }
  } else if...