Skip to content

Instantly share code, notes, and snippets.

@ckmahoney
Last active August 22, 2023 05:12
Show Gist options
  • Save ckmahoney/82987ad7dbdfa9b4329f6894ac1be336 to your computer and use it in GitHub Desktop.
Save ckmahoney/82987ad7dbdfa9b4329f6894ac1be336 to your computer and use it in GitHub Desktop.
Infinite music player with Web Audio API
// copy and paste me into your browser console to play music!
function createReverbNode(context, decayTime = 3, wetLevel = 0.33) {
const reverbNode = context.createConvolver();
// Create an impulse response buffer for the reverb
const bufferLength = context.sampleRate * decayTime;
const impulseResponse = context.createBuffer(2, bufferLength, context.sampleRate);
// Fill the buffer with random noise for a simple reverb effect
for (let channel = 0; channel < impulseResponse.numberOfChannels; channel++) {
const channelData = impulseResponse.getChannelData(channel);
for (let i = 0; i < bufferLength; i++) {
channelData[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / bufferLength, 2);
}
}
reverbNode.buffer = impulseResponse;
const wetGain = context.createGain();
const dryGain = context.createGain();
wetGain.gain.value = wetLevel;
dryGain.gain.value = 1 - wetLevel;
reverbNode.connect(wetGain);
reverbNode.connect(dryGain);
return {
connect(x) {
reverbNode.connect(x)
},
read(x) {
x.connect(reverbNode)
}
};
}
function createMix() {
let ctx = new AudioContext()
let mixer = new AnalyserNode(ctx, {
fftSize: 2048,
maxDecibels: -25,
minDecibels: -60,
smoothingTimeConstant: 0.5,
});
let decay = 1 + 5 * Math.random()
let wet = Math.random()/3
const reverb = createReverbNode(ctx, decay, wet);
let xs = ["sine", "sawtooth", "square", "triangle"]
let oscs = ([5,2,3,4]).map(n => {
let o = new OscillatorNode(ctx)
let g = new GainNode(ctx)
o.type = xs[(n + (new Date()).getTime()) % (xs.length - 1)]
o.gain = g.gain
o.connect(g)
o.frequency.value = n * 100
g.connect(mixer)
return o
})
let volume = new GainNode(ctx)
volume.connect(ctx.destination)
reverb.connect(ctx.destination)
reverb.read(mixer)
mixer.connect(volume)
ts = []
const r = 1 + Math.random()
let v
return {
ctx,
knob(type, value) {
switch (type) {
case "volume":
volume.gain.value = value
break;
case "reverb":
reverb.gain.value = value
break;
}
},
start: function start(scale, vol = 0.1) {
v = vol
oscs.forEach((o, i) => {
let n = i + 1
o.start()
const rate = Math.random() * 2000 * scale
let sustain = 0.05 + (0.8 * Math.random())
o.gain.value = vol
let dur = rate * sustain
function autoLoop() {
let time = new Date()
let ampMod = (Math.abs(Math.sin(n * time))/2) + Math.random()/4
o.gain.value = v * ampMod
o.frequency.value = r * 30 * n * (1 + (time % 7))
ts.push(setTimeout($ => o.gain.value = 0, ctx.currentTime + dur))
ts.push(setTimeout(autoLoop, rate))
}
autoLoop()
})
},
play(x = 0.1) {
v = x
oscs.forEach(o => o.gain.value = v)
},
kill() {
ts.forEach(id => {
try { clearTimeout(id) } catch (e) { /* noop */ }
})
oscs.forEach(o => o.stop())
},
hush() {
oscs.forEach(o => o.gain.value = 0)
}
}
}
// create a mini infiplayer
(function go(scale = 1, sampleRate = 12000) {
io = createMix()
io.start(scale)
scale = Math.sin((new Date()).getTime()/100000) * 5
scale = Math.abs(scale)
setTimeout($ => { io.kill(); go(scale, 1000 + Math.random() * 10000 * scale) }, sampleRate)
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment