played more with p5 generation, this time looking at more fft bins than i normally do (usually just hit the lowest bin because it’s the most obviously correlative, but this viz shows very clear correlates to all bins)

// Fft ersatz 3d timeseries

// p5 canvas recursion: p5 x hydra @errorbesque
p.remove()
p = new P5();

// setup 
cnv=p.createCanvas(400,400);
oldCnv=p.createGraphics(p.width,p.height);
p.noFill();

// reset
p.background(0);
v0y=p.height/2;
v0y2=p.height/2;

p.draw = () => {  
  if ((a.fft[0]<0.4 && p.frameCount%5!=0)) return;  
oldCnv.image(cnv,0,0,p.width,p.height);  
oldCnv.loadPixels();  
p.imageMode(p.CENTER)  
p.angleMode(p.DEGREES)  
p.push()  
p.translate(p.width/2, p.height/2);  
p.image(oldCnv,-12,p.sin(p.frameCount)*2,400,400);  
p.pop()  
a0=a.fft[0]+a.fft[1];  
p.stroke(255,0,255,200);  
di=50  
p.strokeWeight(2)  
if(p.random(-1,1)>0) 
p.blendMode(p.ADD)  
p.ellipse(p.width/2,p.height/2,di*a.fft[1]/2,di*a.fft[0]*2+2);  
p.stroke(255,255,0,200)  
v0y+=p.random(-2,2);  
p.ellipse(p.width/2+10,v0y+p.sin(p.frameCount/10),di*a.fft[1],di*a.fft[0]*2+2);  
p.stroke(0,255,255,200)  v0y2+=p.random(-2,2);  
p.ellipse(p.width/2-10,v0y2+p.sin(p.frameCount/10),di*a.fft[1],di*a.fft[0]*2+2);  
p.blendMode(p.BLEND)
}

s0.init({src:p.canvas})src(s0).out(o0)

src(o0)  
  .scrollX(.05)  
  .blend(src(o0).pixelate(1000,2).modulate(src(o2).scale(.999)),
    ()=>a.fft[2]/3).hue([0.32].smooth().fast(.1)).out(o1)
src(o1)  
.scrollX(-.1)  
.scale(1.15)  
.modulate(src(o2).scale(1.01)            
.pixelate(1000,1000),0.0)  
.out(o2)render(o2)a.show()a.setSmooth(.4)