React

HTML

<div id="app"></div>

CSS

body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

li {
  margin: 8px 0;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

.done {
  color: rgba(0, 0, 0, 0.3);
  text-decoration: line-through;
}

input {
  margin-right: 5px;
}

React

function noteDurationToMs (bpm, dur, type) {
    return 60000 * 4 * dur * type / bpm
}

function scheduleNote(ac, time, dur) {
    var osc = ac.createOscillator();
    osc.connect( ac.destination );
    
    
    osc.start(time);
    osc.stop(time + dur);
}

const ac = new AudioContext();
let to, lastNote = 0;

const Metronome = () => {
  const [bpm, setBpm] = React.useState(100);
  const [dur, setDur] = React.useState(1);  
  const [type, setType] = React.useState(1);
    const [run, setRun] = React.useState(false);    
  
  const handleChangeBPM = (e) => {
    setBpm(e.target.value)
  }
  
  const handleChangeDur = (e) => {
    setDur(e.target.value)
  }
  
  const handleChangeType = (e) => {
    setType(e.target.value)
  }
  
  const step = noteDurationToMs(bpm, dur, type) / 1000;
    const lookAhead = step / 2;
  
  const timer = () => {
    const diff = ac.currentTime - lastNote;
    if (diff >= lookAhead) {
     const nextNote = lastNote + step;
     scheduleNote(ac, nextNote, 0.025)
     lastNote = nextNote;
    }
  }
  
  const start = () => {
    setRun(true);
  }
  
  const stop = () => {
    clearInterval(to);
    setRun(false);
  }
  
  const toggle = () => {
    run?stop():start();
  }

    React.useEffect(() => {
    if (run) {
        clearInterval(to);
      to = setInterval(timer, step / 4);
    }
  });
  
    return <div>
    <label for="bpm">BPM:</label>

    
      <input id="bpm" type="number" onChange={handleChangeBPM} value={bpm} />
    
    <label for="dur">Duration:</label>
    
    <select id="dur" onChange={handleChangeDur} value={dur}>
      <option value={1}>Whole</option>
      <option value={1 / 2}>Half</option>
      <option value={1 / 4}>Quarter</option>
      <option value={1 / 8}>Eigth </option>
      <option value={1 / 16}>Sixteenth</option>
      <option value={1 / 32}>Thirtysecond</option>
    </select>
    
    <label for="typ">Type:</label>
    <select id="typ" onChange={handleChangeType} value={type}>
      <option...