JSFiddle - React, Tailwind, and code Playground

HTML

<h4>
 beware the audio generated by this demo is very unpleasant
</h4>
<button id="btn">
start
</button>
<label>audio volume<input id="volume" type="range" value="0" max="0.5" step="0.001"></label><br>
<label>play sounds<input id="input" type="checkbox" checked></label><br>
<label>hard pressure CPU<input id="hard_pressure" type="checkbox" checked></label><br>
FPS: <span id="FPS_meter">0</span>
<canvas id="canvas"></canvas>

CSS

canvas { border: 1px solid; }

JavaScript

btn.onclick = async () => {
	let max_fps = 0;
	btn.remove();
  const context = new (window.AudioContext || window.webkitAudioContext)();
  const gainnode = context.createGain();
  gainnode.connect( context.destination );
  volume.oninput = (evt) => {
  	gainnode.gain.value = volume.value;  
  };
  volume.oninput();
  const buffer = await fetch( "https://dl.dropboxusercontent.com/s/1cdwpm3gca9mlo0/kick.mp3" )
    .then( (resp) => resp.ok && resp.arrayBuffer() );
  const audiobuffer = await context.decodeAudioData( buffer );
	const delays = [];
	function playSounds() {
  	// plays 50 sounds one after the other
    const dur = 0.01
    for ( let i = 0; i < 50; i++ ) {
      const buffersource = context.createBufferSource();
      buffersource.buffer = audiobuffer;
      buffersource.connect( gainnode );
      const starttime = context.currentTime + (i * dur);
      buffersource.start( starttime, 0, dur );
    }
	}
  let prev_time = document.timeline.currentTime;
  function anim(t) {
  	if( input.checked ) {
	  	playSounds();
    }
    updateFPS( t );
    if( hard_pressure.checked ) {
      const begin = performance.now();
      while( performance.now() - begin < (1000 / max_fps - 2) ) {
				// block the CPU for as long as we can,
        // leaving only 2ms to do something else
        // including drawing the FPS meter...
      }
    }
    requestAnimationFrame( anim );
  }
  requestAnimationFrame( anim );
  
  function updateFPS( t ) {
    delays.push( t - prev_time );
    prev_time = t;
    if( delays.length > 100 ) {
    	delays.shift();
    }
    const FPS = 1000 / (delays.reduce( (s,t) => s + t, 0 ) / delays.length);
    max_fps = Math.max( FPS, max_fps );
    FPS_meter.textContent = FPS.toFixed( 2 );

  	const ctx = canvas.getContext("2d");
    const w = canvas.width;
    const h = canvas.height;
    ctx.clearRect(0, 0, w, h);
    ctx.beginPath();
    delays.forEach( (t, i) => {
    	ctx.lineTo( i / delays.length * w, (t / max_fps) * h);
    } );
   ...