Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
WebKit's createMediaStreamSource has a bug which requires it to be called very soon after the stream
is created. If called after a delay it appears to produce a silent webaudio node.
<pre>
navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
  cleanupJobs.push(() => stream.getTracks().forEach(track => track.stop()));
  if (useDelay) {
    <font color='red'>// Bug occurs</font>
    return delay(500).then(() => stream);
  }
  <font color='green'>// Bug does not occur</font>
  return stream;
}).then((stream) => {
  const source = audioCtx.createMediaStreamSource(stream);
  // ... Visualize audio data from source
};
</pre>
  
</body>
</html>
 
'use strict';
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const cleanupJobs = [];
const cleanup = () => {
  for (const job of cleanupJobs) {
    job();
  }
  cleanupJobs.length = 0;
};
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const start = (useDelay) => {
  cleanup();
  navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
    cleanupJobs.push(() => stream.getTracks().forEach(track => track.stop()));
    if (useDelay) {
      return delay(500).then(() => stream);
    }
    return stream;
  }).then((stream) => {
    const source = audioCtx.createMediaStreamSource(stream);
    const dot = document.createElement('div');
    dot.style.borderRadius = '100%';
    dot.style.backgroundColor = 'blue';
    const setDotSize = (sz) => {
      dot.style.width = `${10 + 200 * sz}px`;
      dot.style.height = `${10 + 200 * sz}px`;
      dot.style.marginTop = `${-(10 + 200 * sz) / 2}px`;
      dot.style.marginLeft = `${-(10 + 200 * sz) / 2}px`;
    };
    dot.style.position = 'absolute';
    dot.style.left = '50vw';
    dot.style.top = '50vh';
    document.body.appendChild(dot);
    cleanupJobs.push(() => dot.remove());
    setDotSize(0);
    const analyser = audioCtx.createAnalyser();
    source.connect(analyser);
    const div = document.createElement('div');
    document.body.appendChild(div);
    cleanupJobs.push(() => div.remove());
    let bufferLength = analyser.fftSize;
    let dataArray = new Uint8Array(bufferLength);
    const draw = () => {
      analyser.getByteTimeDomainData(dataArray);
      div.textContent = dataArray.join(', ');
      const volume = Array.from(dataArray)
        .map(x => Math.abs(x - 128) / 128)
        .reduce((x, y) => Math.max(x, y))
      ;
      setDotSize(volume);
    };
    let animRequest;
    const loop = () => {
      draw();
      animRequest = requestAnimationFrame(loop);
    };
    cleanupJobs.push(() => cancelAnimationFrame(animRequest));
    loop();
  });
};
window.addEventListener('load', () => {
  const makeButton = (txt, action) => {
    const btn = document.createElement('button');
    btn.textContent = txt;
    document.body.appendChild(btn);
    btn.addEventListener('click', action);
  };
  makeButton('Start', () => start(false));
  makeButton('Start With Delay', () => start(true));
  makeButton('Clear', cleanup);
});
Output

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
voltrevopro
0viewers