skip to Main Content

I have tried many methods, and I’m a simple beginner with JavaScript but I want to make a site where I can play a digital piano (without a piano) and have it group keys together that I press at the same time.

For example, if I press "a" then "b" then I press "c" at the same time as "d" it will format it like this: "a b [cd]". I’ve gotten to the point to where I’ve failed so many times that I asked ChatGPT, but even it was no help.     I Wasn’t Surprised

2

Answers


  1. I’m not exactly sure what you’re looking for. The notes should be displayed in a text box or div?

    Login or Signup to reply.
  2. hello, keyboard events

    Here’s a minimal proof of concept. Run the demo below –

    function App() {
      const [state, setState] = React.useState(new Set())
      React.useEffect(() => {
        function onkeydown(event) {
          setState(s => ISet.add(s, event.key))
        }
        function onkeyup(event) {
          setState(s => ISet.delete(s, event.key))
        }
        addEventListener("keyup", onkeyup)
        addEventListener("keydown", onkeydown)
        return () => {
          removeEventListener("keyup", onkeyup)
          removeEventListener("keydown", onkeydown)
        }
      }, [])
      return <div>
        {JSON.stringify(Array.from(state))}
      </div>
    }
    
    const ISet = {
      add(t, v) {
        return new Set(t).add(v)
      },
      delete(t, v) {
        const r = new Set(t)
        r.delete(v)
        return r
      }
    }
    
    ReactDOM.createRoot(document.querySelector("#app")).render(<App />)
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <ol>
      <li>Click here to focus the demo's iframe</li>
      <li>Press any combination of keys on the keyboard</li>
    </ol>
    <div id="app"></div>

    From there, you will want to connect the state changes to WebAudio calls, such as –

    1. playback of piano samples using AudioBufferSourceNode
    2. synthesize audio using OscillatorNode

    hello, music to my ears

    Expanding on the code above, we make a piano from a QWERTY keyboard layout –

    //    W E   T Y U   O P   ]
    //   A S D F G H J K L ; '
    
    const piano = new Map(Array.from(
      "awsedftgyhujkolp;']",
      (key, semitone) => [key, semitone]
    ))
    

    Then a createOsc that computes the frequency for the semitone and plays it –

    function createOsc(audioCtx, semitone) {
      const hz = 440 * Math.pow(2, semitone / 12)
      const osc = audioCtx.createOscillator()
      osc.type = "square"
      osc.frequency.setValueAtTime(hz, audioCtx.currentTime)
      osc.connect(audioCtx.destination)
      osc.start();
      return osc
    }
    

    Run the snippet below and play your next masterpiece –

    function App() {
      const [state, setState] = React.useState(new Map())
      const audioCtx = React.useRef(new AudioContext())
      const tuning = React.useRef(-9)
      React.useEffect(() => {
        function onkeydown(e) {
          setState(prev => {
            const osc = prev.get(e.key)
            if (osc != null) return prev
            const semitone = piano.get(e.key)
            if (semitone != null) return IMap.add(
              prev,
              e.key,
              createOsc(audioCtx.current, semitone + tuning.current)
            )
            return prev
          })
        }
        function onkeyup(e) {
          setState(prev => {
            const osc = prev.get(e.key)
            if (osc != null) {
              osc.stop()
              return IMap.delete(prev, e.key)
            }
            return prev
          })
        }
        addEventListener("keyup", onkeyup)
        addEventListener("keydown", onkeydown)
        return () => {
          removeEventListener("keyup", onkeyup)
          removeEventListener("keydown", onkeydown)
        }
      }, [])
      return <div>
        {JSON.stringify(Array.from(state.keys()))}
      </div>
    }
     
    const piano = new Map(Array.from(
      "awsedftgyhujkolp;']",
      (key, semitone) => [key, semitone]
    ))
    
    function createOsc(audioCtx, semitone) {
      const hz = 440 * Math.pow(2, semitone / 12)
      const osc = audioCtx.createOscillator()
      osc.type = "square"
      osc.frequency.setValueAtTime(hz, audioCtx.currentTime)
      osc.connect(audioCtx.destination)
      osc.start();
      return osc
    }
    
    const IMap = {
      add(t, k, v) {
        return new Map(t).set(k, v)
      },
      delete(t, k) {
        const r = new Map(t)
        r.delete(k)
        return r
      }
    }
    
    ReactDOM.createRoot(document.querySelector("#app")).render(<App />)
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <ol>
      <li>Click here to focus the demo's iframe</li>
      <li>Play piano on your QWERTY keyboard</li>
    </ol>
    <pre>
      W E   T Y U   O P   ]
     A S D F G H J K L ; '
    </pre>
    
    <div id="app"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search