Search code examples
javascriptshadow-domweb-miditone.js

How to add an event listener to the notes being played in tone.js


I'm building a small web app powered by Tone.js to train yourself to play the piano in key (and maybe some other exercises along the way). So far, you can pick the root note and scale type and click play and it will loop that scale. There's also a keyboard/synth that I took from one of the tone.js examples that you can click on the piano keys, use the keys on your querty keyboard, or select a midi keyboard connected to your computer to use and play sound.

The next step I'm trying to do is create a bar that lights up green when a note in the scale is played and red when a note outside of the scale is played, and maybe record how many notes in a row are played correctly or something like that. The problem I'm running into is that there seems to be no way to get the notes being played by the user directly from tone.js. I can convert the keypresses on the keyboard to notes, but that's about it. I cannot capture any event from someone clicking on the keyboard. I also cannot get the selected midi input device from tone.js to be able to interpret the piano keys being played alongside the tone.js synth.

I have tried attaching event listeners using jquery, since the virtual piano keys are just buttons, but they are inaccessible in the shadow DOM.

Has anyone worked with tone.js and tonejs/ui and know if this is possible or do I have to code that outside of code.js somehow? Any ideas for workarounds?

Edit: I have completed my v1.0.0. I was able to listen to midi devices alongside Tone.js and convert them to the notes being played. This is a decent solution, but as I've mentioned it would be better to get the notes being played directly from the Tone.js synth to simplify the code, allow the trainer bar to work with notes being clicked with the mouse, and so that notes being played can be displayed with their octave number on the trainer bar.

git Repo: https://github.com/erikstagg/music-theory


Solution

  • You can listen to the mousedown event for example on the main document.

    Then check in the Event.path array property (it exposes the path of the target element) to check if its a click inside the keyboard note (which should be at the index 3). You can aslo find the octave at the index 7.

    document.addEventListener( 'mousedown', ev => {
      if ( ev.path[3].localName == 'tone-keyboard-note' ) {
        console.log( 'note #' + ev.path[3].getAttribute( 'note' ) )
        console.log( 'octave ', ev.path[7].getAttribute( 'octave' ) )
      }
    } )
    

    NB: on Firefox you should use composedPath instead of path.