Interesting! thanks for sharing.<p>I have added this to my feature list for <a href="https://glicol.org" rel="nofollow noreferrer">https://glicol.org</a><p>the source code looks fairly straightforward. very cool<p>```js
function makeFormantNode(ctx, f1, f2) {
const sinOsc = ctx.createOscillator();
sinOsc.type = 'sawtooth';
sinOsc.frequency.value = 110;
sinOsc.start();<p><pre><code> const bandPass = ctx.createBiquadFilter();
bandPass.type = 'bandpass';
bandPass.frequency.value = (f1 + f2) / 2;
bandPass.Q.value = ((f1 + f2) / 2) / (f2 - f1);
const gainNode = ctx.createGain();
gainNode.gain.value = 0.0;
sinOsc.connect(bandPass);
bandPass.connect(gainNode);
gainNode.connect(ctx.destination);
return {
start() {
gainNode.gain.setTargetAtTime(0.75, ctx.currentTime, 0.015);
},
stop() {
gainNode.gain.setTargetAtTime(0.0, ctx.currentTime, 0.015);
},
panic() {
gainNode.gain.cancelScheduledValues(ctx.currentTime);
gainNode.gain.setTargetAtTime(0, ctx.currentTime, 0.015);
},
};</code></pre>
}<p>function makeSibilanceNode(ctx) {
const buffer = ctx.createBuffer(1, NOISE_BUFFER_SIZE, ctx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < NOISE_BUFFER_SIZE; ++i) {
data[i] = Math.random();
}<p><pre><code> const noise = ctx.createBufferSource();
noise.buffer = buffer;
noise.loop = true;
const noiseFilter = ctx.createBiquadFilter();
noiseFilter.type = 'bandpass';
noiseFilter.frequency.value = 5000;
noiseFilter.Q.value = 0.5;
const noiseGain = ctx.createGain();
noiseGain.gain.value = 0.0;
noise.connect(noiseFilter);
noiseFilter.connect(noiseGain);
noiseGain.connect(ctx.destination);
noise.start();
return {
start() {
noiseGain.gain.setTargetAtTime(0.75, ctx.currentTime, 0.015);
},
stop() {
noiseGain.gain.setTargetAtTime(0.0, ctx.currentTime, 0.015);
},
panic() {
noiseGain.gain.cancelScheduledValues(ctx.currentTime);
noiseGain.gain.setTargetAtTime(0, ctx.currentTime, 0.015);
},
};</code></pre>
}<p>function initialize() {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
audioNodes['a'] = makeFormantNode(audioCtx, 0, 225);
audioNodes['s'] = makeFormantNode(audioCtx, 225, 450);
audioNodes['d'] = makeFormantNode(audioCtx, 450, 700);
audioNodes['f'] = makeFormantNode(audioCtx, 700, 1000);
audioNodes['v'] = makeFormantNode(audioCtx, 1000, 1400);
audioNodes['b'] = makeFormantNode(audioCtx, 1400, 2000);
audioNodes['h'] = makeFormantNode(audioCtx, 2000, 2700);
audioNodes['j'] = makeFormantNode(audioCtx, 2700, 3800);
audioNodes['k'] = makeFormantNode(audioCtx, 3800, 5400);
audioNodes['l'] = makeFormantNode(audioCtx, 5400, 7500);
audioNodes[' '] = makeSibilanceNode(audioCtx);
}
```