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);
} );
...