<canvas id="canvas"></canvas>
<div>
  <button id="start">Start recording</button>
  <button id="stop" disabled>Stop recording</button>
</div>
canvas {
  width: 100%;
  height: 256px;
}

button {
  padding: 0.5em 1em;
  border-width: 0;
  border-radius: 0.25em;
  font-size: 1em;
  background-color: #dfdfdf;
  color: #333;
}

button[disabled] {
  color: #aaa;
}
const start = document.querySelector('#start');
const stop = document.querySelector('#stop');
const canvas = document.querySelector('#canvas');
const drawContext = canvas.getContext('2d');
const cw = canvas.width;
const ch = canvas.height;

let mediaRecorder = null;
let mediaStream = null;

start.addEventListener('click', () => {
  start.disabled = true;
  stop.disabled = false;

  const chunks = [];
  mediaRecorder = new MediaRecorder(mediaStream, {
    mimeType: 'audio/webm'
  });

  mediaRecorder.addEventListener('dataavailable', e => {
    if (e.data.size > 0) {
      chunks.push(e.data);
    }
  });

  mediaRecorder.addEventListener('stop', () => {
    const a = document.createElement('a');
    a.href = URL.createObjectURL(new Blob(chunks));
    a.download = 'test.webm';
    a.click();
  });

  mediaRecorder.start();
});

stop.addEventListener('click', () => {
  if (mediaRecorder === null) {
    return;
  }

  start.disabled = false;
  stop.disabled = true;

  mediaRecorder.stop();
  mediaRecorder = null;
});

navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
  mediaStream = stream;

  const audioContext = new AudioContext();
  const sourceNode = audioContext.createMediaStreamSource(stream);
  const analyserNode = audioContext.createAnalyser();
  analyserNode.fftSize = 2048;
  sourceNode.connect(analyserNode);

  function draw() {
    const array = new Uint8Array(analyserNode.fftSize);
    analyserNode.getByteTimeDomainData(array);
    const barWidth = cw / analyserNode.fftSize;
    drawContext.fillStyle = 'rgba(0, 0, 0, 1)';
    drawContext.fillRect(0, 0, cw, ch);

    for (let i = 0; i < analyserNode.fftSize; ++i) {
      const value = array[i];
      const percent = value / 255;
      const height = ch * percent;
      const offset = ch - height;

      drawContext.fillStyle = 'lime';
      drawContext.fillRect(i * barWidth, offset, barWidth, 2);
    }

    requestAnimationFrame(draw);
  }

  draw();
}).catch(error => {
  console.log(error);
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.