Keyboard - Instrument using javascript.

In this blog, I will describe how I developed a mini-application, where we can play the keyboard instrument.

Background:

My husband is a musician and uses plenty of software to produce his tracks. These applications are quite expensive and sometimes he needs only a few features of them to make his work better. Never too late, it occurred to me to explore and see if I can build them for him. So, this is my first step to trying out the instruments first. And the plan is to build an app around instruments. I will keep sharing the developed parts in this series.

Prerequisites of the project:

Let us get started with this mini-app. In order to get started, we need all the key sounds. We could either use sound files or generate them using a synthesizer. In this project, I am using downloaded sound files from the internet and you can download them from my git-repo. I am sure you can find better files, please do your research if you don't like the sounds here.

Folder structure:

We start by creating three files in the root directory - index.html, index.js, and styles.css. Open index.html and type the exclamation mark and hit enter to get the boilerplate code for html file. We can then add the link tag for styles.css file and the script tag for index.js file. We could hold all the sound files in a folder in the root directory, as shown in the below screenshot.

Screenshot 2022-11-21 at 12.27.05 PM.png

UI of the Keyboard:

We would need a container and then 7 white keys with 5 black keys(representing the flat keys) in between. You can find the code below. We would use data attributes to link to respective sound files. In order to access sound files, we shall use the audio tags and each of those would hold the id attribute the same as data-note of the respective keys.

  <div class="keys">        
        <div data-note="C" class="key white"></div>
        <div data-note="Db" class="key black"></div>
        <div data-note="D" class="key white"></div>
        <div data-note="Eb" class="key black"></div>
        <div data-note="E" class="key white"></div>
        <div data-note="F" class="key white"></div>
        <div data-note="Gb" class="key black"></div>
        <div data-note="G" class="key white"></div>
        <div data-note="Ab" class="key black"></div>
        <div data-note="A" class="key white"></div>
        <div data-note="Bb" class="key black"></div>
        <div data-note="B" class="key white"></div>
   </div>

      <audio id="C" src="sounds/C.mp3"></audio>
      <audio id="Db" src="sounds/Db.mp3"></audio>
      <audio id="D" src="sounds/D.mp3"></audio>
      <audio id="Eb" src="sounds/Eb.mp3"></audio>
      <audio id="E" src="sounds/E.mp3"></audio>
      <audio id="F" src="sounds/F.mp3"></audio>
      <audio id="Gb" src="sounds/Gb.mp3"></audio>
      <audio id="G" src="sounds/G.mp3"></audio>
      <audio id="Ab" src="sounds/Ab.mp3"></audio>
      <audio id="A" src="sounds/A.mp3"></audio>
      <audio id="Bb" src="sounds/Bb.mp3"></audio>
      <audio id="B" src="sounds/B.mp3"></audio>

Styling:

We can start with setting the border-box for the entire layout. Width of the key is different for white keys and black ones, so we use a variable for width and calculate it for the key. In order to get the black keys on top of the white ones, we set the margin-left and margin-right of the black keys to a negative value. This would let the black keys on top of one white key, and to get them on top of both white keys on either of their sides, set the z-index to 2.

*, *::before, *::after {
    box-sizing: border-box;
  }

  body {
    background-color: #d3ebe6fc;
    margin: 0;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .keys {
    display: flex;
  }

  .key {
    height: calc(var(--width) * 4);
    width: var(--width);
  }

  .white {
    --width: 100px;
    background-color: white;
    border: 1px solid #333;
  }

  .white.active {
    background-color: #CCC;
  }

  .black {
    --width: 60px;
    background-color: black;
    margin-left: calc(var(--width) / -2);
    margin-right: calc(var(--width) / -2);
    z-index: 2;
  }

  .black.active {
    background-color: #333;
  }

Here's the final output:

Screenshot 2022-11-21 at 12.50.31 PM.png

Fun part - javascript:

Here is where all the magic is created. In order to get started, we shall get all the keys as DOM elements. And for each of these keys add an event listener to play the respective sound. Find the function playNoteSound from below code snippet.

const allkeys = document.querySelectorAll('.key');

allkeys.forEach(key => {
  //adding event listener for click event  
  key.addEventListener('click', () => playNoteSound(key)); 
});

//function to play the sound of key
function playNoteSound(key) {
  const noteAudio = document.getElementById(key.dataset.note)
  noteAudio.currentTime = 0;
  noteAudio.play();
  key.classList.add('active');
  noteAudio.addEventListener('ended', () => {
    key.classList.remove('active');
  })
}

This would get you going with clicking the keys. However, it is ideal to have the keys from keyboard mapped to the piano keys. Hence, we can add another event listener for 'keydown' and play the respective sounds. We need to define the keys for the white keys and black keys of the piano, as shown in the below snippet.

const WHITE_KEYS = ['z', 'x', 'c', 'v', 'b', 'n', 'm'];
const BLACK_KEYS = ['s', 'd', 'g', 'h', 'j'];

const whiteKeys = document.querySelectorAll('.key.white');
const blackKeys = document.querySelectorAll('.key.black');

//adding event listener for key down from keyboard
document.addEventListener('keydown', e => {
    if (e.repeat) return;
    const key = e.key;
    const whiteKeyIndex = WHITE_KEYS.indexOf(key);
    const blackKeyIndex = BLACK_KEYS.indexOf(key);

    if (whiteKeyIndex > -1) playNoteSound(whiteKeys[whiteKeyIndex]);
    if (blackKeyIndex > -1) playNoteSound(blackKeys[blackKeyIndex]);
  })

Try it for yourself and have fun. I hope this was helpful. Thank you! Happy learning.