Ambient Planet 🪐

Altered version of Christmas Planet

Log in to post a comment.

// srtuss, 2023

ditty.bpm = 60;
// athibaul's reverb filter with added modulation
class Delayline { constructor(n) { this.n = n; this.p = 0; this.data = new Float32Array(n); } get end() { return this.data[this.p]; } tap(offset) { var x = this.p + offset; x = ~~x; x %= this.n; return this.data[x]; } sample(offset) { if(offset < 0) offset = 0; var p = offset; var pi = ~~p; return lerp(this.tap(pi), this.tap(pi+1), p-pi); } clock(input) { var end = this.data[this.p]; this.data[this.p] = input; if(++this.p >= this.n) { this.p = 0; } return end; } }  const ISQRT2 = Math.sqrt(0.5); const SQRT2 = Math.sqrt(2); const SQRT8 = Math.sqrt(8); const ISQRT8 = 1/SQRT8;  const fibodelays = [1467,1691,1932,2138,2286,2567,3141,3897]; const fiboshort = [465, 537, 617, 698, 742, 802, 963, 1215]; let meandelay = fibodelays[3] * ditty.dt;
const reverb = filter.def(class { constructor(options) { this.outgain = 0.3; this.predl = [new Delayline(2), new Delayline(2)]; this.dls = []; this.s0 = new Float32Array(8); for(let i=0; i<8; i++) { this.dls.push(new Delayline(fibodelays[i])); this.dls[i].i = i; this.s0[i] = 0; } this.a0 = clamp01(2*Math.PI*options.cutoff*ditty.dt);  this.dls_nr = []; for(let i=0; i<4; i++) { this.dls_nr.push(new Delayline(fiboshort[i*2])); } } process(inpt, options) {  let s0 = this.predl[0].clock(inpt[0]), s1 = this.predl[1].clock(inpt[1]); for(let i=0; i<4; i++) { let u0 = 0.6*s0 + 0.8*s1, u1 = 0.8*s0 - 0.6*s1; let u1p = this.dls_nr[i].end; this.dls_nr[i].clock(u1); s0 = u0; s1 = u1p; } let v0 = (s0 + s1); let v1 = (s0 - s1);     let rt60 = options.rt60; let loopgain = 10 ** (-3*meandelay / rt60) * ISQRT8; let higain = 10 ** (-3*meandelay / options.rtHi) * ISQRT8; let mod = 10; let v = this.dls.map( (dl) => dl.sample((1 + Math.sin(ditty.tick * (3 + dl.i * 2))) * mod));   let w = [v[0]+v[4], v[1]+v[5], v[2]+v[6], v[3]+v[7], v[0]-v[4], v[1]-v[5], v[2]-v[6], v[3]-v[7]]; let x = [w[0]+w[2], w[1]+w[3], w[0]-w[2], w[1]-w[3], w[4]+w[6], w[5]+w[7], w[4]-w[6], w[5]-w[7]]; let y = [x[0]+x[1], x[0]-x[1], x[2]+x[3], x[2]-x[3], x[4]+x[5], x[4]-x[5], x[6]+x[7], x[6]-x[7]]; y[0] += v0; y[2] += v1; for(let i=0; i<8; i++) { let hipass = y[i] - this.s0[i]; let lopass = this.s0[i]; this.dls[i].clock(lopass * loopgain + hipass * higain); this.s0[i] += this.a0 * hipass; }  return [lerp(inpt[0], v[0], options.mix), lerp(inpt[1], v[2], options.mix)]; } }, {mix:1, rt60:10, cutoff:20000, rtHi:0.9});

input.pitch = 2; // min=0.25, max=12, step=0.25

const TAU = Math.PI * 2;
const wave1 = p => Math.sin(p * TAU * input.pitch);
const wave0 = p => Math.cos(clamp((p+10) % 1, 0, .5) * TAU * 2) - .5;
const sy = synth.def((p,e,t,o) => wave1(p) * e.value);
const noise  = synth.def( (phase, env, tick, options) => (Math.random() * 2 - 1) * env.value, { env: adsr2 } );
const kick = synth.def( (phase, env, tick, options) => Math.sin(phase * 2 * Math.PI * (1.5 - tick * 4)) * env.value);
const square = synth.def( (phase, env, tick, options) => (phase % 1 < 0.5 ? 1 : -1) * env.value, { env: adsr2 });

const range = (a,b) => a+(b-a)*Math.random();
const shuffle = arr => arr.sort(_ => -0.5+Math.random());
const pickArr = (arr) => arr[Math.random()*arr.length|0];
const pick = (...arr) => pickArr(arr);

loop( (lc) => {
    var chords = [
        [a4, c5, e5],       // Am
        [a4, c5, e5, g5],   // Am7
        [a4, c5, e5, g5, b5], // Am9
        [a4, b4, e5],       // Asus2
        [a4, d5, e5],       // Asus4
        [c5, e5, g5],       // C
        [c5, e5, g5, a5],   // C6
        [c5, e5, g5, b5],   // Cmaj7
        [c5, d5, g5, e6],   // Cadd9
    
        [e4, g4, b4],       // Em
        [e4, g4, b4, d5],   // Em7
        [e4, g4, b4, d5, fs5], // Em9
    
        [f4, a4, c5],       // F
        [f4, a4, c5, e5],   // Fmaj7
        [f4, a4, c5, d5],   // F6
        [f4, a4, c5, g5],   // Fadd9
    
        [g4, b4, d5],       // G
        [g4, b4, d5, f5],   // G7
        [g4, c5, d5],       // Gsus4
        [g4, b4, d5, a5],   // Gadd9
    
        [d4, f4, a4],       // Dm
        [d4, f4, a4, c5],   // Dm7
        [d4, f4, a4, c5, e5], // Dm9
        [d4, fs4, a4],      // D (modal mixture)
        [d4, fs4, a4, c5],  // D7
    
        [b4, d5, f5],       // Bdim
        [b4, d5, f5, a5],   // Bm7b5
    
        [e4, gs4, b4, d5],   // E7 (harmonic minor dominant)
        [fs4, a4, c5, e5],   // Fsm7b5 (melodic minor)
        [fs4, a4, c5],       // Fsdim
    ];
    
    var tp = -18 + (lc&1)*12 - 12;
    shuffle(chords);
    chords.forEach(chord => {
        shuffle(chord);
        chord.forEach(note => sy.play(note + tp, {duration: pick(2,4), attack: range(0.1,0.2), release: range(1,2), amp: range(0.05,0.2)}));
        pick(square,sy).play(chord[Math.random()*chord.length|0] + tp - 24, {duration: pick(40,32,24,8,2,0), attack: pick(0.25,0.5,1,4), release: pick(2,3,4), amp: range(0.05,0.2)});
        let speed = pick(0.125, 0.25, 0.5);
        for(let i = 0; i < 32; ++i) {
            if (i === 16) speed = pick(0.125, 0.25, 0.5);
            sy.play(pickArr(chord) + tp, {duration: 0, attack: range(0.01,0.02), release: range(0.05,0.2), amp: .5, pan: Math.random() - .5});
            sleep(speed);
        }
    });
}, { name: 'synth'})
    .connect(reverb.create());

function addLoop(name, loops, fn) {
    let iLoop = loops.choose();
    let tick = 0;
    loop((lc) => {
        if (iLoop.ring(lc) === 'x') fn();
        if (tick++ === iLoop.length) {
            iLoop = loops.choose();
            tick = 0;
            console.log(`new ${name} loop`, iLoop);
        }
    }, { name, sync: 0.5})
        .connect(reverb.create());
}

const kickLoops = ['x---x---x-x-x-xx--------',  'x---x---x------xx---', 'x-------', 'x-x-----','x-x-x---','----','------------', 'xxxx', 'x---x---x---x---x---'];
addLoop(
    'kick', 
    kickLoops,
    ()  => kick.play(c2, { attack: 0.025, release: 0.3, amp: 1.5 })
);

const hithatLoops = ['--x---x---x---xx',  '--x---x---x-x-x---', '--x-----', 'xxx-----','----','------------', '--x---x---x---x-'];
addLoop(
    'hi-hat', 
    kickLoops,
    ()  => noise.play(c3, { attack: range(0.01, 0.015), release:range( 0.075, 0.085), amp: range(.02, 0.08)})
);