// The Dittytoy Library v1.0 - https://github.com/srtuss/dittytoy-library
const{max,min,abs,random,sin,cos,exp,tanh,pow,log,PI}=Math;const TAU=2*PI;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());}const llerp=(a,b,v)=>exp(lerp(log(a),log(b),v));const __pks=synth.def((p,e,t,o)=>o.f.length?(o.f.pop()(),0):0);const poke=(f)=>__pks.play(1,{f:[f]});let _st=0;function sleeps(v){if(ditty.swing>0){let sf=min(ditty.swing,.5);let oo=1+~~(v*100);v/=oo;let zz=0;for(let i=0;i<oo;++i){let pha=(_st*2)%1;let st=pha<.5;zz+=v*(st?1+sf:1-sf);_st+=v;}sleep(zz);}else sleep(v);}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);this.lp=0;}sf(fc){this.f=Math.tan(Math.PI*fc*ditty.dt);return this;}sq(reso){this.r=(40.0/9.0)*reso;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);this.lp=y3;return this;}}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||1;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});class SVF{constructor(){this.lp=this.bp=this.hp=this.ap=this.no=this.ic1eq=this.ic2eq=0;}sp(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;s.no=s.lp+s.hp;s.ap=s.lp+s.hp-s.k*s.bp;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;}}
// by srtuss 2026-3-03
ditty.bpm = 143;
ditty.swing = .05;
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));
//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.sp(llerp(20, 800, param.lowcut), 2);
this.fr.sp(llerp(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().sp(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(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).lp * this.en;
return clamp(s*10, -1, 1) * .1;
}
});
const bd = synth.def((p, e, t, o) => bdf.sf(240+19200*exp(-t*20)).process(tri(p)).lp * 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 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);
class MR {
constructor(seed=11941, na=1) {
this.f = [];
this.t = 0;
this.na = na;
for(let i = 0; i < 64; ++i) {
let ff = llerp(500, 13000, (((sin(i)+1)*seed)%1));
let f = new SVF().sp(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().sp(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().sp(3000, 1.5)});
sleeps(1/4);
})
}
}, {name:'▓', amp:param.cpmix})
.connect(echo.create({wet:.2}))
.connect(scf.create())
.connect(master);