const sawtooth = (t) => 2 * (t % 1) - 1; const random = () => Math.random() * 2 - 1; const osc = synth.def( class { constructor(options) { this.pan = 0; this.phase = 0; this.targetPan = random(); if (Math.abs(this.targetPan) < 0.5) { this.targetPan *= 2.0; } this.startFreq = this.currentFreq = 300 + random() * 100; this.detune = 1 + 0.01 * random(); this.targetFreq = midi_to_hz(options.note) * this.detune; } process(note, env, tick, options) { if (tick < 8) { // For 8 ticks, move slowly and randomly this.currentFreq = this.startFreq * (1 + 0.1 * Math.sin(ditty.time / 1000)); } else { // After 8 ticks, move to target this.pan = lerp(this.pan, this.targetPan, 0.00003); this.currentFreq = lerp(this.currentFreq, this.targetFreq, 0.00003); } this.phase += this.currentFreq * ditty.dt; let boost = 1; if (tick > 20 && this.currentFreq < 220) { boost = 1.5; } return sawtooth(this.phase) * env.value * boost; } }, { amp: 0.1, attack: 8, duration: 30, release: 3 } ); // THX chord // https://twitter.com/THX/status/1000077588415447040 for (let i = 0; i < 4; i++) { [d1, d2, a2, d3, a3, d4, a4, d5, a5, d6, fs6].forEach((n) => osc.play(n)); }