A tiny SFX module using Web Audio. Click to operate. Has noise, square, sine, and saw waves, as well as a pitch shift and tremolo. Will also draw the sound wave to canvas.
let X,N=(m,r)=>Math.random()*r+m;b.onclick=()=>{if(!X)X=new(AudioContext||webkitAudioContext)();let s=X.sampleRate,O,I=N(0,4)|0,A=N(0,.3)*s,D=N(0,.3)*s,L=N(.2,.6),S=N(0,.4)*s,R=N(0,.4)*s,ad=A+D,as=ad+S,F=N(100,1600),P=s/F,p=N(0,.002),tF=N(0,50),tW=N(0,1),t=.9,V=.9,B=X.createBuffer(2,(as+R),s),C,sQ=(i,p)=>{if(i%(p|0/2)==0)V*=-1},sN=(i,p)=>{return Math.sin(i*6.28/p)},sW=(i,p)=>V=((V+1+(2/p))%2)-1,e=0,eN=i=>{if(i<=A)e=i/A;else if(i<=ad)e=-(i-ad)/D*(1-L)+L;if(i>as)e=(-(i-as)/R+1)*L},w=a.width,bL=B.length,sc=w/bL
for(let i=0;i<2;i++){C=B.getChannelData(i);for(let j=0,jL=bL;j<jL;j++){if(I==0)sQ(j,P);if(I==1)V=N(-1,2);if(I==2)V=sN(j,P);if(I==3)sW(j,P);t=sN(j,s/tF)*tW+(1-tW);eN(j);C[j]=V*t*e*0.9;if(C[j]>1)C[j]=1;if(C[j]<-1)C[j]=-1;P-=p}
c.save();c.fillStyle='black';c.fillRect(0,0,w,256);c.scale(sc,1);c.strokeStyle="green";c.beginPath();c.moveTo(0,+C[0]);for(var k=1;k<C.length;k++)
c.lineTo(k,128+(C[k]*128));c.stroke();c.restore()}
O=X.createBufferSource();O.buffer=B;O.connect(X.destination);O.start()}
bGV0IFgsTj0obSxyKT0+TWF0aC5yYW5kb20oKSpyK207Yi5vbmNsaWNrPSgpPT57aWYoIVgpWD1uZXcoQXVkaW9Db250ZXh0fHx3ZWJraXRBdWRpb0NvbnRleHQpKCk7bGV0IHM9WC5zYW1wbGVSYXRlLE8sST1OKDAsNCl8MCxBPU4oMCwuMykqcyxEPU4oMCwuMykqcyxMPU4oLjIsLjYpLFM9TigwLC40KSpzLFI9TigwLC40KSpzLGFkPUErRCxhcz1hZCtTLEY9TigxMDAsMTYwMCksUD1zL0YscD1OKDAsLjAwMiksdEY9TigwLDUwKSx0Vz1OKDAsMSksdD0uOSxWPS45LEI9WC5jcmVhdGVCdWZmZXIoMiwoYXMrUikscyksQyxzUT0oaSxwKT0+e2lmKGklKHB8MC8yKT09MClWKj0tMX0sc049KGkscCk9PntyZXR1cm4gTWF0aC5zaW4oaSo2LjI4L3ApfSxzVz0oaSxwKT0+Vj0oKFYrMSsoMi9wKSklMiktMSxlPTAsZU49aT0+e2lmKGk8PUEpZT1pL0E7ZWxzZSBpZihpPD1hZCllPS0oaS1hZCkvRCooMS1MKStMO2lmKGk+YXMpZT0oLShpLWFzKS9SKzEpKkx9LHc9YS53aWR0aCxiTD1CLmxlbmd0aCxzYz13L2JMDQpmb3IobGV0IGk9MDtpPDI7aSsrKXtDPUIuZ2V0Q2hhbm5lbERhdGEoaSk7Zm9yKGxldCBqPTAsakw9Ykw7ajxqTDtqKyspe2lmKEk9PTApc1EoaixQKTtpZihJPT0xKVY9TigtMSwyKTtpZihJPT0yKVY9c04oaixQKTtpZihJPT0zKXNXKGosUCk7dD1zTihqLHMvdEYpKnRXKygxLXRXKTtlTihqKTtDW2pdPVYqdCplKjAuOTtpZihDW2pdPjEpQ1tqXT0xO2lmKENbal08LTEpQ1tqXT0tMTtQLT1wfQ0KYy5zYXZlKCk7Yy5maWxsU3R5bGU9J2JsYWNrJztjLmZpbGxSZWN0KDAsMCx3LDI1Nik7Yy5zY2FsZShzYywxKTtjLnN0cm9rZVN0eWxlPSJncmVlbiI7Yy5iZWdpblBhdGgoKTtjLm1vdmVUbygwLCtDWzBdKTtmb3IodmFyIGs9MTtrPEMubGVuZ3RoO2srKykNCmMubGluZVRvKGssMTI4KyhDW2tdKjEyOCkpO2Muc3Ryb2tlKCk7Yy5yZXN0b3JlKCl9DQpPPVguY3JlYXRlQnVmZmVyU291cmNlKCk7Ty5idWZmZXI9QjtPLmNvbm5lY3QoWC5kZXN0aW5hdGlvbik7Ty5zdGFydCgpfQ==
// AUDIO CONTEXT
let X,
// RANDOM NUMBER m = minimum, r = range
N = (m,r)=> Math.random()*r+m;
b.onclick=()=>{
// INITIALIZE AUDIO CONTEXT
if(!X) X = new (AudioContext||webkitAudioContext)();
// SAMPLE RATE
let s = X.sampleRate,
O,
I = N(0,4)|0,
// I=3,
A = N(0,.3)*s,
D = N(0,.3)*s,
L = N(.2,.6),
S = N(0,.4)*s,
R = N(0,.4)*s,
// A=0*s,
// D=0.01 *s,
// L=0.5,
// S=0.2*s,
// R=0.3*s,
ad=A+D,as=ad+S,
F = N(100,1600),
P = s/F,
p = N(0,.002),
tF = N(0,50),
tW = N(0,1),
t = .9,
V = .9,
// CREATE BUFFER(CHANNELS, LENGTH IN SAMPLES, SAMPLE RATE)
B = X.createBuffer(2,(as+R),s),
// WHICH BUFFER CHANNEL WE WILL BE WORKING ON
C,
// SQUARE WAVE
sQ = (i,p)=> {if(i%(p|0/2)==0) V*= -1},
// SINE WAVE
sN = (i,p)=> {return Math.sin(i*6.28/p)},
// SAW WAVE
sW = (i,p)=> V = ((V+1+(2/p)) % 2) - 1,
// ENVELOPE VALUE
e=0,
// ADSR
eN = i=> {
if(i<=A) e=i/A;
else if(i<=ad) e=-(i-ad)/D*(1-L)+L;
if(i>as) e= (-(i-as)/R+1)*L;
},
// SCALE
w = a.width,
bL = B.length,
sc = w/bL
for(let i=0;i<2; i++) {
C = B.getChannelData(i);
for(let j=0,jL=bL; j<jL; j++) {
// PROCESS WAVE GENERATORS
if(I==0) sQ(j,P);
if(I==1) V = N(-1,2);
if(I==2) V = sN(j,P);
if(I==3) sW(j,P);
// PROCESS TREMOLO
t = sN(j,s/tF)*tW+(1-tW);
// PROCESS ADSR ENVELOPE
eN(j);
// SET SAMPLE VALUE
C[j] = V*t*e*0.9;
// CUT OFF SAMPLE VALUE
if(C[j]>1)C[j]= 1;
if(C[j]< -1)C[j] = -1;
// SHIFT PITCH
P-= p;
}
// DRAW WAVEFORM
c.save();
c.fillStyle = 'black';
c.fillRect(0,0,w,256);
c.scale(sc,1);
c.strokeStyle = "green";
c.beginPath();
c.moveTo(0,+C[0]);
for (var k=1;k<C.length;k++)
c.lineTo(k,128+(C[k]*128));
c.stroke();
c.restore();
}
O = X.createBufferSource();
O.buffer = B;
O.connect(X.destination);
O.start();
}