<div id="keyboard">
  <div class="header">
    <div class="controls">  
      <span class="control left">
      <svg
           width="100%"
           height="100%"
           viewBox="0 0 100 75"
           >

      <polygon points="0,0 100,0 50,75"/>
    </svg>
      </span>
    <span class="tone-value"></span>
      <span class="control right">
      <svg
           width="100%"
           height="100%"
           viewBox="0 0 100 75"
           >

      <polygon points="0,0 100,0 50,75"/>
    </svg>
      </span>
      
     </div>
  </div>
  <div class="row row-1">
    <div class="key key-1">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-2">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-3">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-4">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-5">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-6">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-7">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>

  </div>
  <div class="row row-2">
    <div class="key key-1">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-2">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-3">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-4">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-5">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-6">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-7">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
  </div>
  <div class="row row-3">
    <div class="key key-1">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-2">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-3">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-4">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-5">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-6">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
    <div class="key key-7">
      <span class="note"></span>
      <span class="tone-value"></span>
    </div>
  </div>
</div>
@import url('https://fonts.googleapis.com/css?family=Roboto+Condensed')
html,
body
  background-color: #222222
  padding: 0
  margin: 0
  height: 100%
  overflow: hidden
  font-family: 'Roboto Condensed', sans-serif
  color: #ffffff
  
body
  display: flex
  align-items: center
  justify-content: center
.header
  margin-bottom: 10px
  display: flex
  align-items: center
.controls
  display: flex
  display: flex
  align-items: center
  user-select: none
  .control
    width: 30px
    height: 30px
    display: block
    margin-left: 10px
    margin-right: 10px
    cursor: pointer
    &.right
      transform: rotate(-90deg)
      .tone-5 &
        opacity: .3
        pointer-events: none
    &.left
      transform: rotate(90deg)
      .tone-1 &
        opacity: .3
        pointer-events: none
    svg
      overflow: visible
    polygon
      fill: transparent
      stroke-width: 4px
      stroke: #ffffff
.row
  display: flex
  flex-wrap: wrap
  justify-content: center
.key
  width: 80px
  height: 80px
  background-color: #affec7
  margin: 10px
  position: relative
  color: rgba(#000000, .25)
  display: flex
  box-sizing: border-box
  padding: 10px
  align-items: flex-end
  justify-content: flex-end
  user-select: none
  &:before
    content: ""
    display: block
    width: 100%
    height: 100%
    background-color: #111111
    opacity: 0
    position: absolute
    top: 0
    left: 0
    transition: opacity .14s 
  .row-1 &
    background-color: #888888
  .row-3 &
    background-color: #ffffff
  &:hover
    &:before
      opacity: .625
  &:active,
  &.active
    &:before
      opacity: 1
View Compiled
const notes = "CDEFGAB".split("");
const eventKeys = "QWERTYUASDFGHJZXCVBNM".split("").map(_ => `Key${_}`);
const notetypes = ["#", "b", ""];
let tone = 4;
var synth = new Tone.Synth().toMaster();

const keyboard = document.body.querySelector("#keyboard");
const keys = document.body.querySelectorAll(".key");
const whiteKeys = document.body.querySelectorAll(".row-3 .key");
const greenKeys = document.body.querySelectorAll(".row-2 .key");
const greyKeys = document.body.querySelectorAll(".row-1 .key");
const toneControls = document.body.querySelectorAll(".control");
const toneValue = document.body.querySelectorAll(".tone-value");

function updateToneValue() {
  toneValue.forEach(el => {
    el.innerHTML = tone;
  });
  keyboard.className = `tone-${tone}`;
}
updateToneValue();
function toneDown() {
  tone = tone <= 1 ? 1 : tone - 1;
  updateToneValue();
}
function toneUp() {
  tone = tone >= 5 ? 5 : tone + 1;
  updateToneValue();
}
toneControls[0].addEventListener("click", toneDown);
toneControls[1].addEventListener("click", toneUp);
function applyKeys(keys, notetype = "") {
  keys.forEach((el, i) => {
    el.querySelector(".note").innerHTML = `${notes[i]}${notetype}`;
    el.addEventListener("mousedown", () => {
      synth.triggerAttackRelease(`${notes[i]}${notetype}${tone}`, "8n");
      el.classList.remove("active");
    });
  });
}
applyKeys(greyKeys, "#");
applyKeys(greenKeys, "b");
applyKeys(whiteKeys);

window.addEventListener("keydown", event => {
  const found = eventKeys.indexOf(event.code);
  if (found >= 0) {
    const idx = Math.floor(found / 7);
    const i = found % 7;
    keys[found].classList.add("active");
    synth.triggerAttackRelease(`${notes[i]}${notetypes[idx]}${tone}`, "8n");
  } else if(event.code === 'ArrowLeft') {
    toneDown()
  } else if(event.code === 'ArrowRight') {
    toneUp()
  }
});
window.addEventListener("keyup", event => {
  const found = eventKeys.indexOf(event.code);
  if (found >= 0) keys[found].classList.remove("active");
});
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/tone/14.4.9/Tone.js