DEV Community 👩‍💻👨‍💻

Je We
Je We

Posted on

Building a Simple Piano with Tone.js and NexusUI (Part 1)

Hello! This week I set up a simple computer-keyboard-controlled piano. I used Tone.js for audio and NexusUI's 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

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':

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:

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

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

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

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:

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.

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

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.

 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.

Top comments (2)

Collapse
 
bobdevil64 profile image
BobDEvil64 • Edited on

And really, playing a real piano can't be compared to anything! And if you want to buy a real piano, then I hear that you can buy a used Steinway piano here. My friend once bought a piano there and still plays it! It is very high quality! I think it should suit you just right and you can play and enjoy! Try it, I think it should suit you!

Collapse
 
cevin77 profile image
Cevin77

Thank you so much for telling me about this, but I prefer to play the real piano more. If someone knows where you can buy a piano, then share it with me!

Find and follow new tags! 🤔 Did you know?
 
DEV has a variety of tags to help you find the content you like. Find and follow your favorite tags