Acid Warrior

Happy 303 Day

Log in to post a comment.

// by srtuss 2026-3-03

ditty.bpm = 143;
let max=Math.max, min=Math.min,abs=Math.abs,random=Math.random,sin=Math.sin,exp=Math.exp,tanh=Math.tanh,PI=Math.PI,pow=Math.pow,TAU=2*PI;
let flo = v => ~~v;
let tri = p => abs(p % 1 - .5) * 4 - 1;
let shift = (a,n) => {for(let i = 0; i < n; ++i) a.unshift(a.pop()); return a}
const pblep = (t,dt)=>{if(t<dt){t/= dt;return t+t-t*t-1;} else if (t>1-dt){t=(t-1)/dt; return t*t+t+t+1; } return 0; }
//const bd = synth.def((phase, env) => sin((abs(phase % 1 - .5) * 4 - 1)*1.7) * env.value);
const triangle = synth.def((phase, env) => tri(phase) * env.value);
const saw = synth.def((phase, env) => (phase%1-.5) * env.value);
const hat = synth.def((p,e,t,o)=>o.fba.process(random()-.5)*e.value, { attack: .0001, release: .04, duration:.25 });
const hat2 = synth.def((p,e,t,o)=>o.fba.process(random()-.5)*e.value, { attack: .0001, release: .04, duration:0 });
const natural = (v) => 20 * (2 ** (clamp01(v)*9.9657));
const ll = (a, b, v) => exp(lerp(Math.log(a), Math.log(b), v));
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 echo = filter.def(class {constructor(opt) {this.lastOut = [0, 0];var division = opt.division || 3/4;var pan = clamp01((opt.pan || 0)*.5+.5);var sidetime = (opt.sidetime || 0) / ditty.dt;var time = 60 * division / ditty.bpm;this.fb = clamp(opt.feedback || 0, -1, 1);this.kl = 1-pan;this.kr = pan;this.wet = opt.wet || .5;this.stereo = isFinite(opt.stereo) ? opt.stereo : 1;var n = ~~(time / ditty.dt);this.delay = [new Delayline(n), new Delayline(n)];this.dside = new Delayline(~~sidetime);}process(inv, opt) {this.dside.clock(inv[0]);var l = this.dside.end * this.kl;var r = inv[1] * this.kr;var nextl = l + this.delay[1].end * this.fb;var nextr = r + this.delay[0].end * this.fb;this.lastOut[0] = inv[0] + this.delay[0].end * this.wet;this.lastOut[1] = inv[1] + this.delay[1].end * this.wet;this.delay[0].clock(nextl);this.delay[1].clock(nextr);if(this.stereo != 1) {var m = (this.lastOut[0] + this.lastOut[1])*.5; var s = (this.lastOut[0] - this.lastOut[1])*.5;s *= this.stereo;this.lastOut[0] = m+s;this.lastOut[1] = m-s;}return this.lastOut;}}, {sidetime: .01, division: 3/3, pan: .5, wet: .3, feedback: .5, stereo: 1.5});

function sleeps(v) {
    let h = (ditty.tick*2)%1;
    let sf = .02;
    sleep(v + (h < 0.5 ? sf : -sf));
}

//input.lowcut = 0;
//input.ring = 0; // min=0,max=24,step=.1
//input.wtf = 0;

//let param = input; // override of "input"
param = {lowcut:0, ring:0, wtf:0 } //structuredClone(param);
param.wtf = 1;
param.ring = 21.6;
param.bdmix = (t,o) => (t % 32) < 30 && !(t % 512 >= 128 && t % 512 < 160) 
param.pmix = (t,o) => t % 256 >= 128;
param.tomix = (t,o) => t >= 64;
param.cpmix = (t,o) => t % 256 >= 32;
param.ohmix = (t,o) => t % 256 >= 64;

function autom() {
    let t = ditty.tick;
    let cy = t % 64;
    param.ring = lerp(20, 5, (t*.5) % 1);//sin(t * PI * 4) * .5 + .5;
    param.wtf = clamp01((t%128) - 119);
    param.lowcut = 
        //sin(t * PI * 4) * .5 + .5;
        clamp01(cy - 55);
    param.bdmix = t < 10;
    //debug.probe('r', param.ring, 1);
}

let scf = filter.def(class {
    constructor() {
    }
    process(inlr, opt) {
        let t = ditty.tick % 1;
        let v = min(t * 2, 1) + exp(t * -200);
        v = v * .9 + .1;
        //debug.probe('a', v, 1, 4);
        return [inlr[0]*v, inlr[1]*v];
    }
});

let master = filter.def(class {
    constructor() {
        this.fl = new SVF();
        this.fr = new SVF();
        this.rp0 = 0;
        this.rp1 = 0;
    }
    process(inlr, opt) {
        autom();
        this.fl.sf(ll(20, 800, param.lowcut), 2);
        this.fr.sf(ll(20, 800, param.lowcut), 2);
        let l = inlr[0]*1.2;
        let r = inlr[1]*1.2;
        l = this.fl.process(l).hp;
        r = this.fr.process(r).hp;
        if(param.wtf > .5) {
            l += l * sin(this.rp0) * .5;
            r += r * sin(this.rp1) * .5;
            this.rp0 += PI*midi_to_hz(f5+param.ring)*ditty.dt;
            this.rp1 += PI*midi_to_hz(f5+param.ring+1)*ditty.dt;
        }
        let s = [(tanh(l)+l*.2)*2,(tanh(r)+r*.2)*2];
        return s;//this.tt.process();
    }
}).createShared();

const anim = synth.def(class {
    constructor(options) {
        this.r = 0;
    }
    process() {
        let px = '─█';
        let w = 59, h = 5;
        this.r += ditty.dt * 60;
        if(this.r >= 1) {
            let fo = (sin(ditty.tick * .1) + 1) * 4;
            function re(x, y) {
                y *= 4;
                let l = Math.sqrt(x * x + y * y);
                let a = Math.atan2(y, x)
                return Math.sin(40/l + PI*ditty.tick + a * fo) > 0;
            }
            this.r = 0; 
            for(let y = 0; y < h; ++y) {
                let li = [];
                for(let x = 0; x < w; ++x) {
                    li.push(px[1&re(x-w*.5, y-h*.5)]);
                }
                debug.log(y, li.join(''));
            }
        }
        return 0;
    }
});

const syc = synth.def(class {
    constructor(o) {
        this.p = 0;
        this.bob = new Bob(o.syp.r);
        this.en = 0;
        this.of = new SVF().sf(150, 1);
    }
    process(n,e,t,o) {
        let syp = o.syp;
        let p = this.p % 1;
        syp.n += (syp.nn - syp.n) * .001;
        let dp = ditty.dt * midi_to_hz(syp.n);
        let v = p*2 - pblep(p, dp) - 1;
        let pw = .48;
        v = (p < pw ? 1 : - 1) + pblep(p, dp) - pblep((p - pw + 1) % 1, dp);
        this.p += dp;
        this.bob.sf(ditty.dt * natural(.2 + sin(t*.1+syp.a) * .1 + .4 * Math.exp(syp.t * -4.)));
        syp.t += ditty.dt;
        if(syp.g) {
            if(this.en < 1)
                this.en = min(this.en + ditty.dt * 5000, 1);
        }
        else {
            this.en *= .995;
        }
        v = this.of.process(v*.8).hp;
        let s = this.bob.process(v)* this.en;
        return clamp(s*10, -1, 1) * .1;
    }
});

const bd = synth.def((p, e, t, o) => bdf.sf(.005+.4*exp(-t*20)).process(tri(p)) * e.value);

loop( () => {
    bd.play((t,_)=>f1+30*2**(t*-30)+10*(t<.55?0:2**-max((t-.55)*10,0)), { attack: 0.00015, release: 0.2, duration: 0.7 });
    sleep(1);
}, { name: 'a', amp:(t,o)=>.4*param.bdmix(t,o) }).connect(master);

let xxx = synth.def((p,e,t,o) => o.f.length ? (o.f.pop()(),0) : 0);
let poke = (f) => xxx.play(1,{f:[f]});
let gen = (n, f) => { let r = []; for(let i = 0; i < n; ++i) r.push(f(i)); return r};

let syp = {n:a1, nn:a1, t:0, g:0, a:0, r:.8};
loop( lc => {
    if(!lc) {
        syc.play(a1, { attack: 0, release: 0, duration: 1e38, syp:syp });
        anim.play(a1, { duration: 1e38 });
    }
    let mels = [
        [12, 0, 17, 25, 12, 24],
        [0, 0, 15, 0, 12, 24],
        [0, 0, 13, 0, 12, 24],
        [0, 13, 7, 12, 12, 24],
        [0, 12, 7, 12+7, 12, 40-9]
    ];
    let l = [5, 8, 16].choose();
    let mel = mels.choose();
    let acid_n = gen(l, i=>mel.choose());
    let acid_s = gen(l, i=>[0, 0, 1].choose());
    for(let j = 0; j < 8; ++j) {
        for(let i = 0; i < acid_n.length; ++i) {
            let sl = acid_s[i];
            let nsl = acid_s[(i+1)%acid_n.length];
            poke(()=>{
                syp.nn = f1+acid_n[i];
                if(!sl) {
                    syp.n = syp.nn;
                    syp.t = 0;
                }
                syp.g = 1;
            });
            sleep(.15);
            poke(()=>{
                if(!nsl)
                    syp.g = 0;
            });
            sleep(1/4-.15);
        };
    }
}, { name: 'c', amp:param.tomix }).connect(echo.create({mix:.1})).connect(scf.create()).connect(master);

let syp2 = {n:a1, nn:a1, t:0, g:0, a:10, r:.2};
loop( lc => {
    if(!lc) syc.play(a1, { attack: 0, release: 0, duration: 1e38, syp:syp2 });
    let mels = [
        [0, 12, 24],
    ];
    let l = [8, 4, 5].choose();
    let mel = mels.choose();
    let acid_n = gen(l, i=>mel.choose());
    let acid_s = gen(l, i=>[0, 0, 1].choose());
    for(let j = 0; j < 8; ++j) {
        for(let i = 0; i < acid_n.length; ++i) {
            let sl = acid_s[i];
            let nsl = acid_s[(i+1)%acid_n.length];
            poke(()=>{
                syp2.nn = f0+acid_n[i];
                if(!sl) {
                    syp2.n = syp2.nn;
                    syp2.t = 0;
                }
                syp2.g = 1;
            });
            sleep(.15);
            poke(()=>{
                if(!nsl)
                    syp2.g = 0;
            });
            sleep(1/4-.15);
        };
    }
}, { name: 'i' }).connect(echo.create({mix:.1})).connect(scf.create()).connect(master);

let rhy2 = rhy(10, 16);

loop( () => {
    let rhy1 = shift(rhy([4].choose(), 16), 2);
    for(let i = 0; i < 4; ++i) {
        rhy1.forEach(v => {
            if(v)
                hat.play(c4, {fba: new MR()});
            sleep(1/4);
        })
    }
}, { name: 'd', amp: (t,o)=>param.ohmix(t,o)*.1 }).connect(scf.create()).connect(master);

loop( () => {
    let rhy1 = shift(rhy([13, 11, 3, 15].choose(), 16), 2);
    for(let i = 0; i < 4; ++i) {
        rhy1.forEach(v => {
            if(v) {
                hat2.play(c4, {fba: new MR()});
            }
            sleeps(1/4);
        })
    }
}, { name: '░', amp: .1 }).connect(scf.create()).connect(master);

function tanhXdX(x) {
    var a = x*x;
    return ((a + 105)*a + 945) / ((15*a + 420)*a + 945);
}

class Bob {
    constructor(q=.9, f=.01) {
        this.zi = 0;
        this.s = [0, 0, 0, 0];
        this.sf(f);
        this.sq(q);
    }
    sf(cutoff) {
        this.f = Math.tan(Math.PI * cutoff);
        return this;
    }
    sq(resonance) {
        this.r = (40.0/9.0) * resonance;
        return this;
    }
    process(v0) {
        var s = this.s;
        var f = this.f;
        var r = this.r;
        var ih = 0.5 * (v0 + this.zi);
        this.zi = v0;
        var t0 = tanhXdX(ih - r * s[3]);
        var t1 = tanhXdX(s[0]);
        var t2 = tanhXdX(s[1]);
        var t3 = tanhXdX(s[2]);
        var t4 = tanhXdX(s[3]);
        var g0 = 1 / (1 + f*t1), g1 = 1 / (1 + f*t2);
        var g2 = 1 / (1 + f*t3), g3 = 1 / (1 + f*t4);
        var f3 = f*t3*g3;
        var f2 = f*t2*g2*f3;
        var f1 = f*t1*g1*f2;
        var f0 = f*t0*g0*f1;
        var y3 = (g3*s[3] + f3*g2*s[2] + f2*g1*s[1] + f1*g0*s[0] + f0*v0) / (1 + r*f0);
        var xx = t0*(v0 - r*y3);
        var y0 = t1*g0*(s[0] + f*xx);
        var y1 = t2*g1*(s[1] + f*y0);
        var y2 = t3*g2*(s[2] + f*y1);
        s[0] += 2*f * (xx - y0);
        s[1] += 2*f * (y0 - y1);
        s[2] += 2*f * (y1 - y2);
        s[3] += 2*f * (y2 - t4*y3);
        return y3;
    }
}

function rhy(k, n) {
    let nx = k, ny = n-k, x = [1], y = [0];
    while(nx && ny > 1) {
        if(nx < ny) {
            x = x.concat(y);
            ny -= nx;
        }
        else {
            let t = nx - ny;
            nx = ny;
            ny = t;
            let k = x.concat(y);
            y = x;
            x = k;
        }
    }
    return Array(nx).fill(x).flat().concat(Array(ny).fill(y).flat());
}

class SVF {
    constructor()
    {
        this.lp=this.bp=this.hp=this.ic1eq=this.ic2eq=0;
    }
    sf(fc, q) {
        fc = clamp(fc * ditty.dt, 0, .499);
        let g = Math.tan(PI * fc);
        this.k = 1 / q;
        this.a1 = 1 / (1 + g * (g + this.k));
        this.a2 = g * this.a1;
        this.a3 = g * this.a2;
        return this;
    }
    process(v0)
    {
        let s = this;
        let v1, v2, v3;
        v3 = v0 - s.ic2eq;
        v1 = s.a1 * s.ic1eq + s.a2 * v3;
        v2 = s.ic2eq + s.a2 * s.ic1eq + s.a3 * v3;
        s.ic1eq = 2 * v1 - s.ic1eq;
        s.ic2eq = 2 * v2 - s.ic2eq;
        s.lp = v2;
        s.bp = v1;
        s.hp = v0 - s.k * v1 - v2;
        return s;
    }
    cp() {
        let r = new SVF();
        r.k = this.k; r.a1 = this.a1; r.a2 = this.a2; r.a3 = this.a3;
        return r;
    }
}

class MR {
    constructor(seed=11941, na=1) {
        this.f = [];
        this.t = 0;
        this.na = na;
        for(let i = 0; i < 64; ++i) {
            let ff = ll(500, 13000, (((sin(i)+1)*seed)%1));
            let f = new SVF().sf(ff, 10);
            this.f.push([f, f.cp(), f.cp(), 60000 / ff]);
        }
    }
    process(v) {
        let o = 0;
        let pulse = exp(-this.t);
        this.f.forEach(f => o += f[2].process(f[1].process(f[0].process(pulse*.6 + this.na*Math.random()).bp).bp).bp * exp(-this.t * f[3]));
        this.t += ditty.dt;
        return o * .004;
    }
}

const bdf = new Bob(.05);

let piano = synth.def(
    class {
    constructor(o) {
        this.f = [];
        for(let i = 0; i < 80; ++i) {
            let ff = midi_to_hz(-4-12+o.cho[i%o.cho.length]+random()*.1);
            ff *= (1+(i*135)%22);
            let f = new SVF().sf(ff, 80);
            this.f.push({f:[f, f.cp(), f.cp()], a:.01/ff, d:2400 / ff, p:2*random()-1});
        }
    }
    process(n,e,t,o) {
        let u = 0, v = 0;
        let pu = exp(t * -20) * 2;
        let no = Math.random()*.2;
        this.f.forEach(f => {
            let k = f.f[2].process(f.f[1].process(f.f[0].process(pu+no).bp).bp).bp * exp(-t * f.d) * f.a;
            v += k*f.p;
            u += k*(1-f.p);
        });
        return [u*e.value,v*e.value];
    }
});

loop((lc) => {
    let rhy1 = shift(rhy([9, 6, 7, 5].choose(), 16), 2);
    let cho = [
        [a4, a5, c5, e5, g5],
        [a4, a5, c5, d5, fs5]
    ];
    for(let i = 0; i < 4; ++i) {
        rhy1.forEach(v => {
            if(v)
                piano.play(c4, {cho:cho[lc%2]});
            sleeps(1/4);
        })
    }
}, {name:'▒', amp:(o,t)=>param.pmix(o,t)}).connect(echo.create({wet:.9})).connect(scf.create()).connect(master);

let clp = synth.def((p,e,t,o)=>(o.f.process(min(abs(t-.01)*100,1) * random()-.5)).bp*e.value, {release:.1});
loop(() => {
    let rhy1 = shift(rhy([2, 6].choose(), 16), 4);
    for(let i = 0; i < 4; ++i) {
        rhy1.forEach(v => {
            if(v)
                clp.play(c4, {f:new SVF().sf(3000, 1.5)});
            sleeps(1/4);
        })
    }
}, {name:'▓', amp:param.cpmix})
.connect(echo.create({wet:.2}))
.connect(scf.create())
.connect(master);