<meta name="viewport" content="width=device-width, initial-scale=1.0">
<head>
  <title>HTML Audio Compressor Tutorial</title>
</head>
<body>
<div id="wrap"class="grid-container">
<div id="controls">
  Threshold: <input type="range" id="thresh_text" value="-12" min="-60" max="0" oninput="compress()"></input>
  Ratio: <input type="number" id="ratio_text" value="4" step="0.5" min="1" max="100" oninput="compress()" min="1"></input>
  Attack:<input type="range" id="atk_text" value="1" oninput="compress()"></input>
  Release:<input type="range" id="rel_text" value="20" oninput="compress()"></input>
  Makeup:<input type="number" id="gain_text" value="0" oninput="compress()" min="0" max="24"></input>
</div>
<canvas id="waveform" width="900px" height="500px"></canvas>
</div>
</body>
</html>
body{
  font-family: 'Roboto';
  font-size:100%;

}

.grid-container {
  display: grid;
  grid-template-columns: 250px auto;
  grid-template-rows: 1fr;
  grid-template-areas: ". .";
}

#wrap{
  position:absolute;
  top:50%;
  left:50%;
  margin-left:auto;
  margin-right:auto;
  transform:translateY(-50%) translateX(-50%); 
  width:100%;
  height:100%;
}

#controls{
  float:left;
  position:relative;
  background:#ddd;
  padding:20px;
}

#controls > input{
  border-style:none;
  display:block;
  font-size:150%;
  margin-bottom:20px;
  width:200px;
  padding:5px;
  background:white;
  outline:2px solid #aaa;
}

#waveform{
  border:4px solid #ddd;
}


@media only screen and (max-width: 1000px) {
  #controls{
    max-height:100vh;
    overflow:scroll;
    font-size:75%;
  }
  #controls > input {
    font-size:100%;
    margin-bottom: 5px;
    zoom:0.7;
  }
}
var canvas = null; // Waveform canvas
var ctx = null;
var ratio = 1;
var thr = 1;

var sig = null;
var wet = null;
var gainred = null;
var sigsz = 320;

//===================================
function init(){
  canvas = document.getElementById("waveform");
  ctx = canvas.getContext("2d");
  
  makeSig();
  compress();
  resizeWaveform();
}
init();

//===================================
function makeSig() {
  sig = [];
  var env = 0;
  var ecoef = 1 - Math.exp(-1 / (0.004 * sigsz));
  for (var i = 0; i < sigsz; i++) {
    var p = i / sigsz;
    var t =  1;
    t *= Math.sin(Math.pow(1 - p, 2) * 3.1415926);
    
    var tmp = i / sigsz;
    tmp -= 0.05;
    tmp = Math.min(1, tmp);
    tmp = Math.max(0, tmp);
    tmp = Math.pow(tmp, 0.5);
    tmp = -Math.pow(tmp, 0.5);
    t *= -Math.sin(3.1415926 * tmp);
    
    
    var tmp = i / sigsz;
    tmp -= 0.42;
    tmp = Math.min(1, tmp);
    tmp = Math.max(0, tmp);
    tmp = Math.pow(tmp, 0.25);
    tmp = Math.sin(3.1415926 * tmp);
    //var TEST = tmp;
    t *= 1 + 0.8*tmp;
    
    tmp = Math.pow(15*p, 4);
    if(tmp > 1) tmp = 0;
    
    env = Math.max(tmp, env * ecoef);
    t += env * 0.8;
    
    
    tmp = Math.sin(2*(Math.pow(i-0.5,2)+0.5));
    t += tmp * 0.02;
    
    sig.push(Math.abs(t*0.8) + 0.0000001);
    //sig.push(TEST);
  }
  
  
  wet = sig.slice();
}


//===================================
function compress(){
  var atkMS = parseFloat(document.getElementById("atk_text").value);
  var relMS = parseFloat(document.getElementById("rel_text").value);
  var threshVal = parseFloat(document.getElementById("thresh_text").value);
  var ratio = parseFloat(document.getElementById("ratio_text").value);
  var makeup = parseFloat(document.getElementById("gain_text").value);
 
  ratio = 1 - 1 / ratio;
  
  
  thr = Math.pow(10, threshVal / 20);
  wet = sig.slice();
  gainred = wet.slice();
  
  var acoef = 1 - Math.exp(-1 / (atkMS * 0.001 * sigsz));
  var rcoef = 1 - Math.exp(-1 / (relMS * 0.003 * sigsz));
  var outgain = Math.pow(10, makeup/20);
  var env = 1;  
  for(var i = 0; i < wet.length; i++){
    
    var gr = threshVal - 20 * Math.log10(wet[i]);
    gr = Math.pow(10, ratio * gr / 20);
    gr = Math.min(gr, 1);
    env += (gr - env) * (env < gr ? rcoef : acoef);    
    
    wet[i] *= env * outgain;
    gainred[i] = env;
  }
 
  drawWavform();
}


//===================================
function drawWavform(){
  var w = sig.length;
  var h = canvas.clientHeight;
  
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.lineWidth = 2;
  
  // Draw normal wavform
  ctx.beginPath();
  ctx.moveTo(0, h/2);
  
  for(var i = 0; i < sig.length; i++)
  {
    var lx = i / wet.length * canvas.clientWidth;
    ctx.lineTo(lx, h / 2 * (1 + sig[i]));
  }
  ctx.lineTo(canvas.clientWidth, h/2);
  for(var i = sig.length - 1; i >=0; i--){
    var lx = i / wet.length * canvas.clientWidth;
    ctx.lineTo(lx, h / 2 *(1 - sig[i]));
  }
  ctx.fillStyle = 'rgba(0,255,0,0.2)';
  ctx.fill();
  ctx.strokeStyle="green";
  ctx.stroke();
  
  // Draw compressed wavform
  ctx.beginPath();
  ctx.moveTo(0, h/2);
  
  for(var i = 0; i < sig.length; i++)
  {
    var lx = i / wet.length * canvas.clientWidth;
    ctx.lineTo(lx, h / 2 * (1 + wet[i]));
  }
  ctx.lineTo(canvas.clientWidth, h/2);
  for(var i = sig.length - 1; i >=0; i--){
    var lx = i / wet.length * canvas.clientWidth;
    ctx.lineTo(lx, h / 2 *(1 - wet[i]));
  }
  ctx.fillStyle = 'rgba(0,0,255,0.2)';
  ctx.fill();
  ctx.strokeStyle="blue";
  ctx.stroke();
  
  
  // Draw gain reduction wavform
  ctx.beginPath();
  ctx.moveTo(0, 0);
  
  for(var i = 0; i < sig.length; i++)
  {
    var lx = i / gainred.length * canvas.clientWidth;
    ctx.lineTo(lx, h / 4 * (1 - gainred[i]));
  }
  ctx.lineTo(canvas.clientWidth, 0);
  ctx.lineTo(0,0);
  
  ctx.fillStyle = 'rgba(255,0,0,0.2)';
  ctx.fill();
  ctx.strokeStyle='rgba(255,0,0,0.2)';
  ctx.stroke();
  
  // Draw Threshold wavform
  ctx.strokeStyle="rgba(100,100,100,0.8)";
  ctx.beginPath();
  var ty = h / 2 * (1-thr);
  ctx.moveTo(0, ty);
  ctx.lineTo(canvas.clientWidth, ty);
  ctx.stroke();
  
}

drawWavform();



//===================================
function resizeWaveform()
{
  var w = document.documentElement.clientWidth;
  var h = document.documentElement.clientHeight;
  //alert(w + " x " + h);
  
  canvas.setAttribute("width", w - 300);
  canvas.setAttribute("height", h-20);
  drawWavform();
}

window.addEventListener("resize", resizeWaveform);
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.