Building a Simple Piano with Tone.js and NexusUI (Part 1)
Published: 2020-05-10 19:51:12

Hello! This week I set up a simple computer-keyboard-controlled piano. I used [Tone.js](https://tonejs.github.io/) for audio and [NexusUI's](https://nexus-js.github.io/ui/api/#intro) Piano for an interface. I am definitely NOT the first person to do so, but I'm just here to take you on my personal journey.

I'm going to write this blog in two parts. In part one I'll describe creating a piano interface and allowing its visual state to be controlled by the keyboard. In part two I'll describe hooking up the piano's state to actual sound.

My objective was to create a one-octave piano, easily controlled by the computer keyboard, with the ability to change the octave range. NexusUI furnishes a very neat-looking piano interface:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/550a1q392fckfe5thg47.PNG)

Getting it on your page is as easy as supplying a div id as a point for injection. Here's the function I wrote for creating a new piano with a custom range at a div with id '#piano-keyboard':

```javascript
const createPiano = (lowNote, highNote) => {
  const newPiano = new Nexus.Piano('#piano-keyboard', {
    size: [300, 125],
    mode: 'toggle',
    lowNote,
    highNote,
  });

  return newPiano;
}
```
The reason I wrote it as a function instead of creating it right away will become clear later on. To actually create a piano I simply run:

```javascript
let lowNote = 72;
let highNote = 84;
let piano = createPiano(lowNote, highNote);
```

NexusUI's Piano keys have note values in MIDI, so the range of 72 to 84 will give me the octave from C4 to C5. Now I have the piano on the page, and if I click on any of they keys they will toggle to an 'on' state, changing from their original color to light blue.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9dmhflerblseld1o31c6.PNG)

That's great, but I want to be able to toggle the keys with my keyboard. First, I came up with what I thought was an intuitive set of controls:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/rr782l6qc7tfeg5sz0y1.png)

NexusUI's Piano has a method to toggle its keys based on their index, called toggleIndex:

```javascript
piano.toggleIndex(0, true); // toggles lowest note on
piano.toggleIndex(0, false); // toggles lowest note off
```

The next step was to map the key-names to the index of the key I'd want them to correspond to:

```javascript
const keyMapper = {
  a: 0,
  w: 1,
  s: 2,
  e: 3,
  d: 4,
  f: 5,
  t: 6,
  g: 7,
  y: 8,
  h: 9,
  u: 10,
  j: 11,
  k: 12,
}
```

Now I needed to add key up and key down event listeners which would use the keyMapper object to toggle the correct key on and off.

```javascript
document.addEventListener('keydown', (event) => {
  const keyIndex = keyMapper[event.key];
  keyIndex !== undefined && !piano.keys[keyIndex]._state.state ? 
  piano.toggleIndex(keyIndex, true) : null;
});

document.addEventListener('keyup', (event) => {
  const keyIndex = keyMapper[event.key];
  keyIndex !== undefined && piano.keys[keyIndex]._state.state ? 
  piano.toggleIndex(keyIndex, false) : null;
});
```
The ._state.state looks pretty icky (please let me know if you know a better way to access a particular key's state), but it gets the job done. Essentially, before toggling a key, I want to check if that key is not already in the state to which I'm about to toggle. While toggling an active key to 'on' won't actually be noticeable visibly, it will actually fire a 'change' event on the piano. Holding a key down would fire many of these events per second, which would cause problems down the line integrating the piano's key states with starting and stopping Tone.js synths.

Now, the Nexus UI Piano does not allow changing of its lowNote and highNote properties once the piano has been created, so to allow octave-changing the current piano has to be destroyed, and a new one created in its place with the new desired octave range. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/11uflvbsl02zu3vcv2z1.jpg)

I added specific cases to the key down event listener to generate a new range, destroy the current piano, and use my createPiano function to create a new piano with the new range. The '<' key shifts down an octave, and the '>' key shifts up.

```javascript
 if (event.key === ',' && lowNote > 24) {
    piano.destroy();
    highNote = lowNote;
    lowNote -= 12;
    piano = createPiano(lowNote, highNote);  
  }

  if (event.key === '.' && highNote < 120) {
    piano.destroy();
    lowNote = highNote;
    highNote += 12;
    piano = createPiano(lowNote, highNote);  
  }
```

Alright, now my keyboard is in full control of my virtual piano. Next week I'll talk about how to get this silent piano singing.