Improve this and make it shorter, make a really good filter design tool: const sin=Math.sin,cos=Math.cos,PI=Math.PI,TAU=PI*2,sqrt=Math.sqrt;
var b0,b1,b2,a1,a2,
    i1=0,i2=0,o1=0,o2=0,
    zeros=[],poles=[];
function calculateCoefficients(freq,Q,type){
    /*
    const RC=1/(TAU*freq),
          dt=1/sampleRate,
          alpha=dt/(RC+dt);
    */
    const w0=TAU*freq/sampleRate, //normalized frequency
          alpha=sin(w0)/(2*Q);
    switch(type){
        case"lowpass":
            b0=(1-cos(w0))/2;
            b1=1-cos(w0);
            b2=b0;
            a1=-2*cos(w0);
            a2=1-alpha;
            break;
        case"highpass":
            b0=(1+cos(w0))/2;
            b1=-(1+cos(w0));
            b2=b0;
            a1=-2*cos(w0);
            a2=1-alpha;
            break;
        case"bandpass":
            b0=alpha;
            b1=0;
            b2=-alpha;
            a1=-2*cos(w0);
            a2=1-alpha;
            break;
        case"notch":
            b0=1;
            b1=-2*cos(w0);
            b2=1;
            a1=b1;
            a2=1-alpha;
            break;
        case"peak":
            b0=1+alpha;
            b1=-2*cos(w0);
            b2=1-alpha;
            a1=b1;
            a2=b2;
            break;
    }
    //Normalize coefficients
    b0/=a0;b1/=a0;b2/=a0;
    a1/=a0;a2/=a0;
    //Calculate zeros and poles using Z-transform
    zeros=findRoots(b0,b1,b2);
    poles=findRoots(1,a1,a2);
}
function findRoots(a,b,c){
    var roots=[],
        d=b*b-4*a*c;
    if(d>=0){
        var sqrtD=sqrt(d);
        roots.push((-b+sqrtD)/(2*a));
        roots.push((-b-sqrtD)/(2*a));
    }else{
        var real=-b/(2*a),
            imag=sqrt(-d)/(2*a);
        roots.push({re:real,im:imag});
        roots.push({re:real,im:-imag});
    }
    return roots;
}
function processFilter(input){
    let output=b0*input+b1*i1+b2*i2-a1*o1-a2*o2;
    i2=i1;i1=input;o2=o1;o1=output;
    return output;
}
function plotZerosPoles(){
    var canvas=document.getElementById("canvas"),ctx=canvas.getContext("2d"),width=canvas.width,height=canvas.height;
    ctx.clearRect(0,0,width,height);
    //Draw unit circle
    ctx.beginPath();
    ctx.arc(width/2,height/2,height/2-20,0,TAU);
    ctx.strokeStyle="#000";
    ctx.stroke();
    //Draw axes
    ctx.beginPath();
    ctx.moveTo(0,height/2);
    ctx.lineTo(width,height/2);
    ctx.moveTo(width/2,0);
    ctx.lineTo(width/2,height);
    ctx.strokeStyle="#888";
    ctx.stroke();
    //Plot zeros (blue circles)
    ctx.fillStyle="blue";
    zeros.forEach(function(z){
        var x,y,zx,zy;
        if(typeof z==="object"){
            zx=z.re;zy=z.im;
        }else{
            zx=z;zy=0;
        }
        x=width/2+(zx*(height/2-20));
        y=height/2-(zy*(height/2-20));
        ctx.beginPath();
        ctx.arc(x,y,5,0,TAU);
        ctx.fill();
    });
    //Plot poles (red crosses)
    ctx.strokeStyle="red";
    poles.forEach(function(p){
        var x,y,px,py;
        if(typeof p==="object"){
            px=p.re;py=p.im;
        }else{
            px=p;py=0;
        }
        x=width/2+(px*(height/2-20));
        y=height/2-(py*(height/2-20));
        ctx.beginPath();
        ctx.moveTo(x-5,y-5);
        ctx.lineTo(x+5,y+5);
        ctx.moveTo(x-5,y+5);
        ctx.lineTo(x+5,y-5);
        ctx.stroke();
    });
}