ditty.bpm = 500;
const xenakisTone = synth.def((phase, env, tick, options) => {
let freq = midi_to_hz(options.note);
freq *= 1 + 0.01 * Math.sin(tick * 0.5);
return Math.sin(phase * 2 * Math.PI * freq) * env.value;
}, { attack: 0.02, release: 0.4, duration: 2 });
const xenakisSnare = synth.def((phase, env, tick, options) => {
return (Math.random() * 2 - 1) * env.value;
}, { attack: 0.005, release: 0.15, duration: 0, amp: 0.4 });
const xenakisNoise = synth.def((phase, env, tick, options) => {
return (Math.random() * 2 - 1) * env.value;
}, { attack: 0.001, release: 0.05, duration: 0, amp: 0.3 });
loop(() => {
const clusterSize = Math.floor(Math.random() * 3) + 1;
for (let i = 0; i < clusterSize; i++) {
xenakisTone.play(Math.floor(Math.random() * 36) + 40, { pan: (tick, opts) => Math.random() * 2 - 1 });
sleep(Math.random() * 0.3);
}
sleep(1 + Math.random() * 0.5);
}, { name: 'Random Clusters' });
loop((loopCount) => {
const s = xenakisTone.play((tick, opts) => opts.note, { duration: 8, note: Math.floor(Math.random() * 36) + 40, amp: 0.5 });
for (let i = 0; i < 8; i++) {
s.options.note = Math.floor(Math.random() * 36) + 40;
sleep(1);
}
}, { name: 'Glissandi' });
loop(() => {
if (Math.random() < 0.3) xenakisSnare.play();
if (Math.random() < 0.2) xenakisNoise.play();
sleep(0.25);
}, { name: 'Percussion' });