← MOONSEED v1
index.html
<!doctype html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1">
<title>MOONSEED</title>
<style>
html,body{margin:0;height:100%;background:#000;overflow:hidden}
canvas{position:fixed;left:0;top:0;width:100%;height:100%;display:block}
#s{position:fixed;left:0;top:0;right:0;bottom:0;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;padding-bottom:14vh;font:14px/1.4 ui-monospace,Menlo,Consolas,monospace;color:#9fe;letter-spacing:.3em;text-align:center}
#s h1{font-size:15px;font-weight:400;margin:0 0 22px;color:#fff}
#s p{margin:22px 0 0;font-size:11px;opacity:.6}
#b{font:inherit;letter-spacing:.4em;color:#000;background:#ff1a8c;border:0;padding:16px 40px 16px 46px;cursor:pointer;font-weight:700}
#b:hover,#b:focus{background:#ff8a0a;outline:0}
</style>
<canvas id=c></canvas>
<div id=s><h1>MOONSEED</h1><button id=b>START</button><p>SOUND ON · F = FULL SCREEN</p></div>
<script>
//SYN<
// The song. Shared by the picture (for sync) and by the synth thread.
function SONG(){
var BPM=140,ROW=15/BPM,BARS=108,ev=[],kicks=[],snares=[];
var Am=[45,[57,60,64]],F=[41,[53,57,60]],C=[48,[55,60,64]],G=[43,[55,59,62]],Em=[40,[52,55,59]];
var P1=[Am,F,C,G],P2=[F,G,Em,Am],DD=[50,[57,62,66]];
var hz=function(n){return 440*Math.pow(2,(n-69)/12)};
// kinds: 0 lead, 1 chord buzz, 2 pluck, 3 bass, 4 kick, 5 snare, 6 hat, 7 riser
function add(k,row,len,note,v,x){var e={k:k,t:row*ROW,d:len*ROW,f:hz(note),v:v};if(x)for(var i in x)e[i]=x[i];ev.push(e);return e}
var MA=[0,0,76,3,0,3,81,3,0,6,79,2,0,8,76,4,0,12,74,2,0,14,76,2,
1,0,72,6,1,6,74,2,1,8,72,4,1,12,69,4,
2,0,67,3,2,3,72,3,2,6,76,2,2,8,79,6,2,14,76,2,
3,0,74,6,3,6,76,2,3,8,74,2,3,10,71,2,3,12,67,4,
4,0,76,3,4,3,81,3,4,6,83,2,4,8,84,4,4,12,83,2,4,14,81,2,
5,0,81,6,5,6,79,2,5,8,77,4,5,12,72,4,
6,0,76,3,6,3,79,3,6,6,84,2,6,8,88,6,6,14,86,2,
7,0,86,4,7,4,83,4,7,8,79,4,7,12,83,2,7,14,86,2];
var R1=[77,81,84,81,77,81,84,89,88,84,81,84,79,81,84,81],R2=[76,79,83,79,76,79,83,88,86,83,79,83,79,83,88,83];
var MB=[],i;
for(i=0;i<16;i++)MB.push(0,i,R1[i],1,2,i,R2[i],1,4,i,R1[i],1,6,i,R2[i],1);
MB.push(1,0,86,6,1,6,83,2,1,8,79,4,1,12,83,2,1,14,86,2,3,0,88,6,3,6,84,2,3,8,81,4,3,12,84,2,3,14,88,2,
5,0,91,6,5,6,86,2,5,8,83,4,5,12,86,2,5,14,91,2,7,0,81,4,7,4,84,4,7,8,88,4,7,12,93,4);
var MS=[76,81,72,77,79,76,74,71];
function mel(M,bar0,tr,v,oct){for(var j=0;j<M.length;j+=4){add(0,(bar0+M[j])*16+M[j+1],M[j+3]-.15,M[j+2]+tr,v,{w:.35});if(oct)add(0,(bar0+M[j])*16+M[j+1],M[j+3]-.15,M[j+2]+tr+12,v*.35,{w:.4,p:.4})}}
var SQ=[0,1,2,3,4,5,4,3,2,3,4,5,4,3,2,1];
for(var b=0;b<BARS;b++){
var sec=b<8?0:b<24?1:b<40?2:b<56?3:b<72?4:b<88?5:b<104?6:7,tr=sec==6?2:0;
var ch=sec==3?P2[b%4]:sec==7?DD:P1[b%4],root=ch[0]+tr,T=ch[1],q=b*16,r;
var last8=(b%8)==7,drive=sec==1||sec==2||sec==3||sec==5||sec==6;
// pluck sequence
for(r=0;r<16;r++){
var on=sec==0?(b<4?r%4==0:r%2==0):sec==4?(b<64?r%4==0:1):sec==7?(b<106?r%2==0:b==106&&r%4==0):1;
if(on){var ti=SQ[r],nn=T[ti%3]+(ti>2?12:0)+12+tr+(sec==3||sec==6?12:0);
var vv=sec==0?.05+.008*b:sec==4&&b<64?.07:sec==7?.1*(1-(b-104)/3.2):.1;
add(2,q+r,1.6,nn,vv,{p:r%2?.6:-.6,w:.55,u:r%4==2?.5:.25})}
}
// chord buzz
var st=sec==3?[0,2,3,6,8,10,11,14]:[0,3,6,10,12];
if((sec==1&&b>=16)||sec==2||sec==3||sec==5||sec==6)for(i=0;i<st.length;i++)add(1,q+st[i],1.7,T[0]+tr,.085,{f2:hz(T[1]+tr),f3:hz(T[2]+tr),p:i%2?.5:-.5,w:.25});
if(sec==4||(sec==0&&b>=4)||sec==7)add(1,q,16,T[0]+tr,sec==7?.06*(1-(b-104)/4.5):.06,{f2:hz(T[1]+tr),f3:hz(T[2]+tr),p:b%2?.3:-.3,w:.3,a:.4});
// bass
if(drive)for(r=0;r<16;r+=2){
if(sec==3){add(3,q+r,.9,root,.26);add(3,q+r+1,.9,root+(r%4==2?12:0),.22)}
else add(3,q+r,1.7,root+(r%4==2?12:r==14?7:0),.27)}
if((sec==0&&b>=4)||(sec==4&&b>=64)||sec==7)add(3,q,b>=106?32:15.5,root,sec==7?.2:.22);
// drums
var kr=[],sr=[];
if(sec==1)kr=[0,4,8,12];
if(sec==2){kr=b%2?[0,6,10,13]:[0,6,10];sr=[4,12]}
if(sec==3){kr=b%2?[0,4,8,12,14]:[0,4,8,12];sr=[4,12]}
if(sec==4&&b>=64&&b<70)kr=b<68?[0,8]:[0,4,8,12];
if(sec==5){kr=[0,4,8,12];sr=[4,12]}
if(sec==6){kr=[0,4,8,12];sr=b%2?[4,12,15]:[4,12]}
if(sec==1&&b>=16)sr=[4,12];
if(drive&&last8){kr=kr.filter(function(x){return x<12});sr=sr.filter(function(x){return x<12}).concat([12,13,14,15])}
if(b==104)kr=[0];
for(i=0;i<kr.length;i++){add(4,q+kr[i],2.8,60,.95);kicks.push((q+kr[i])*ROW)}
for(i=0;i<sr.length;i++){add(5,q+sr[i],1.9,60,sr[i]>12&&!last8?.2:.42);snares.push((q+sr[i])*ROW)}
if(b==70||b==71)for(r=0;r<16;r+=(b==70?2:1)){add(5,q+r,1.5,60,.12+.3*((b-70)*16+r)/32);snares.push((q+r)*ROW)}
// hats
if((sec==0&&b>=4)||drive||(sec==4&&b>=64))for(r=0;r<16;r++){
if(r%4==2)add(6,q+r,1.5,60,sec==0?.05:.11,{dc:28,p:.2});
else if(r%2==0&&sec)add(6,q+r,.6,60,.07,{dc:95,p:-.2});
else if(sec==3||sec==6)add(6,q+r,.5,60,.04,{dc:120,p:.3})}
if(b==8||b==24||b==40||b==72||b==88||b==96||b==104||b==56)add(6,q,24,60,.22,{dc:3.5});
if(b==6||b==70||b==86||b==38)add(7,q,32,45+tr,.3,{w:.3});
}
// melodies
mel(MA,16,0,.19);mel(MA,24,0,.2);mel(MA,32,0,.2,1);
mel(MB,40,0,.17);mel(MB,48,0,.17,1);
for(i=0;i<8;i++){add(0,(56+i)*16,7.7,MS[i],.11,{w:.5,u:.5});add(0,(56+i)*16+8,7.7,MS[(i+3)%8]+(i%2?0:5),.09,{w:.5,u:.5})}
mel(MA,64,0,.13);
mel(MA,72,0,.2);mel(MA,80,0,.2,1);
mel(MA,88,2,.2,1);mel(MA,96,2,.21,1);
add(0,104*16,30,86,.16,{w:.5});add(0,104*16,30,81,.1,{w:.5,p:-.4});add(0,104*16,30,78,.1,{w:.5,p:.4});
return{ev:ev,kicks:kicks,snares:snares,ROW:ROW,BAR:ROW*16,BARS:BARS,len:BARS*16*ROW}
}
// The synth: returns next(n) which makes the next n samples (stereo), in order.
function SYNTH(SR){
var S=SONG(),ev=S.ev.map(function(e){e.s=Math.round(e.t*SR);e.n=Math.max(1,Math.round((e.k==4?.32:e.k==5?.22:e.d)*SR));e.ph=0;e.z=0;e.h=0;e.c=0;return e}).sort(function(a,b){return a.s-b.s});
var total=Math.ceil((S.len+2.2)*SR),pos=0,idx=0,act=[],kp=0,ks=S.kicks.map(function(t){return Math.round(t*SR)}),ke=0,kd=Math.exp(-1/(.11*SR));
var DL=Math.round(3*S.ROW*SR),dl=new Float32Array(DL),dr=new Float32Array(DL),di=0,sd=22222,TAU=6.283185307;
function rnd(){sd^=sd<<13;sd^=sd>>>17;sd^=sd<<5;return(sd>>>0)/2147483648-1}
function blep(t,dt){if(t<dt){t/=dt;return t+t-t*t-1}if(t>1-dt){t=(t-1)/dt;return t*t+t+t+1}return 0}
function pul(ph,dt,du){var x=ph+1-du;x-=Math.floor(x);return(ph<du?1:-1)+blep(ph,dt)-blep(x,dt)}
return function(len){
var n=Math.min(len,total-pos);if(n<=0)return null;
var mL=new Float32Array(n),mR=new Float32Array(n),dM=new Float32Array(n),sN=new Float32Array(n),L=new Float32Array(n),R=new Float32Array(n),i,j,t,x,env,f,dt;
while(idx<ev.length&&ev[idx].s<pos+n)act.push(ev[idx++]);
for(var a=act.length-1;a>=0;a--){
var e=act[a],i0=Math.max(e.s,pos)-pos,i1=Math.min(e.s+e.n,pos+n)-pos,v=e.v,pn=e.p||0,gl=v*Math.min(1,1-pn),gr=v*Math.min(1,1+pn),wet=v*(e.w||0),rel=Math.min(e.n,Math.round(.02*SR)),o;
for(i=i0;i<i1;i++){
j=i+pos-e.s;t=j/SR;env=j<rel*.2?j/(rel*.2):1;if(e.n-j<rel)env*=(e.n-j)/rel;
switch(e.k){
case 0:f=e.f*(1+.007*Math.sin(TAU*5.6*t)*Math.min(1,Math.max(0,t-.14)*5));dt=f/SR;e.ph+=dt;e.ph-=Math.floor(e.ph);
o=pul(e.ph,dt,e.u||(.3+.17*Math.sin((i+pos)/SR*4.4)))*env*(.72+.28*Math.exp(-t*9));break;
case 1:f=(x=Math.floor(t*50)%3)==0?e.f:x==1?e.f2:e.f3;dt=f/SR;e.ph+=dt;e.ph-=Math.floor(e.ph);
o=pul(e.ph,dt,.14+.1*Math.sin((i+pos)/SR*2.1))*env*(e.a?Math.min(1,t/e.a):.4+.6*Math.exp(-t*7));break;
case 2:dt=e.f/SR;e.ph+=dt;e.ph-=Math.floor(e.ph);o=pul(e.ph,dt,e.u)*env*(.12+.88*Math.exp(-t*16));break;
case 3:dt=e.f/SR;e.ph+=dt;e.ph-=Math.floor(e.ph);x=pul(e.ph,dt,.5);e.z+=(x-e.z)*.1;o=(1.1*(4*Math.abs(e.ph-.5)-1)+.55*e.z)*env*(.65+.35*Math.exp(-t*5));break;
case 4:e.ph+=(44+175*Math.exp(-t*30))/SR;o=Math.sin(TAU*e.ph)*Math.exp(-t*8.5)*env+(j<SR*.004?rnd()*.4:0);break;
case 5:if(--e.c<=0){e.h=rnd();e.c=5}e.ph+=(150+60*Math.exp(-t*30))/SR;o=(e.h*Math.exp(-t*21)+.55*(4*Math.abs(e.ph-Math.floor(e.ph)-.5)-1)*Math.exp(-t*32))*env;break;
case 6:x=rnd();o=(x-e.h)*.6*Math.exp(-t*e.dc)*env;e.h=x;break;
default:x=j/e.n;if(--e.c<=0){e.h=rnd();e.c=SR/(400+11000*x*x)}dt=e.f*(1+7*x*x)/SR;e.ph+=dt;e.ph-=Math.floor(e.ph);o=(e.h*.8+.35*pul(e.ph,dt,.5))*x*x*env}
if(e.k==4)dM[i]+=o*v;else if(e.k<7&&e.k>4){L[i]+=o*gl;R[i]+=o*gr}else{mL[i]+=o*gl;mR[i]+=o*gr;sN[i]+=o*wet}
}
if(e.s+e.n<=pos+n)act.splice(a,1);
}
for(i=0;i<n;i++){
while(kp<ks.length&&ks[kp]<=pos+i){ke=1;kp++}
ke*=kd;var du=1-.5*ke,wa=dl[di],wb=dr[di];
dl[di]=sN[i]+wb*.42;dr[di]=wa*.42;if(++di>=DL)di=0;
x=dM[i]+L[i]+(mL[i]+wa*.55)*du;L[i]=Math.tanh(x*1.25)*.86;
x=dM[i]+R[i]+(mR[i]+wb*.55)*du;R[i]=Math.tanh(x*1.25)*.86;
}
var out={l:L,r:R,p:pos};pos+=n;return out}
}
//SYN>
</script>
<script>
(function(){
var Q=new URLSearchParams(location.search),qn=function(k,d){return Q.has(k)?+Q.get(k):d};
var D=window.DEMO={ft:[],n:0,t:0,done:0,first:-1,chunks:0,sched:0,err:[]};
var cv=document.getElementById('c'),sp=document.getElementById('s'),bt=document.getElementById('b');
var gl=cv.getContext('webgl2',{antialias:false,alpha:false,depth:false,stencil:false,powerPreference:'high-performance'});
if(!gl){sp.innerHTML='<h1>MOONSEED needs WebGL 2</h1>';return}
var HDR=!!(gl.getExtension('EXT_color_buffer_float')||gl.getExtension('EXT_color_buffer_half_float'));
var mobile=/Mobi|Android|iPhone|iPad/.test(navigator.userAgent)||(navigator.maxTouchPoints>1&&/Mac/.test(navigator.platform));
var NMAX=qn('n',mobile?1<<20:1<<22),NMIN=Math.min(NMAX,1<<17),n=Q.has('n')?NMAX:mobile?1<<19:3<<19,fixedN=Q.has('n'),ceil=NMAX;
var MAXPX=qn('px',3.7e6);
var S=SONG(),BAR=S.BAR,LEN=S.len;
// ---------- shaders
var HASH='uint pc(uint v){v=v*747796405u+2891336453u;uint w=((v>>((v>>28u)+4u))^v)*277803737u;return(w>>22u)^w;}'+
'vec4 H(uint n){uint a=pc(n),b=pc(a),c=pc(b),d=pc(c);return vec4(a>>8,b>>8,c>>8,d>>8)/16777216.;}';
var SIM='#version 300 es\nprecision highp float;precision highp int;precision highp sampler2D;'+
'layout(location=0)in vec4 aP;layout(location=1)in vec4 aV;out vec4 oP;out vec4 oV;'+
'uniform float uT,uDt,uM,uK,uN,uJ,uLag;uniform int uA,uB,uF;uniform vec4 uQ,uR;uniform vec3 uMo,uL,uC,uEye;uniform sampler2D uX;'+
'const float TAU=6.2831853;'+HASH+
'vec3 rotY(vec3 p,float a){float c=cos(a),s=sin(a);return vec3(c*p.x+s*p.z,p.y,c*p.z-s*p.x);}'+
'vec3 sph(vec2 h){float z=1.-2.*h.x,r=sqrt(max(0.,1.-z*z)),a=TAU*h.y;return vec3(r*cos(a),z,r*sin(a));}'+
// the moon: a sphere of points with craters, lit from uL
'vec3 moon(vec4 h,vec3 ctr,float R,out vec2 bc){vec3 d=sph(h.xy);float dz=0.,mar=0.;'+
'for(int i=0;i<9;i++){float fi=float(i);vec3 cc=sph(fract(vec2(.37,.71)*(fi+1.)*1.618+.1));float rj=.12+.2*fract(fi*.618);'+
'float x=acos(clamp(dot(d,cc),-1.,1.))/rj;dz+=-.035*smoothstep(1.,.75,x)+.02*exp(-pow((x-1.)*7.,2.));mar+=smoothstep(1.,.8,x);}'+
'float nz=sin(d.x*9.+sin(d.y*7.))*sin(d.z*8.+sin(d.x*5.))*.012;vec3 dr=rotY(d,uT*.25);float lam=max(dot(dr,uL),0.);'+
'vec3 ps=ctr+dr*R*(1.+dz+nz);float fc=smoothstep(-.12,.2,dot(dr,normalize(uEye-ps)));bc=vec2((.02+2.2*pow(lam,.8))*(1.-.45*min(mar,1.))*(1.+dz*12.)*(.04+.96*fc),.66*pow(1.-lam,2.));return ps;}'+
// strange attractors, each scaled to about a unit box
'vec3 att(int m,vec3 p){'+
'if(m==2){vec3 q=vec3(p.x,p.z,p.y)*22.+vec3(0,0,25);vec3 d=vec3(10.*(q.y-q.x),q.x*(28.-q.z)-q.y,q.x*q.y-2.6667*q.z);return vec3(d.x,d.z,d.y)/22.*.22;}'+
'if(m==3){vec3 q=vec3(p.x,p.z,p.y)*1.5+vec3(0,0,.55);float z=q.z;vec3 d=vec3((z-.7)*q.x-3.5*q.y,3.5*q.x+(z-.7)*q.y,.6+.95*z-z*z*z/3.-(q.x*q.x+q.y*q.y)*(1.+.25*z)+.1*z*q.x*q.x*q.x+2.*smoothstep(-.45,-.9,z));return vec3(d.x,d.z,d.y)/1.5*.9;}'+
'if(m==4){vec3 q=p*3.6;return(sin(q.yzx)-.208186*q)/3.6*3.;}'+
'if(m==5){vec3 q=p*12.-3.;vec3 d=-1.89*q-4.*q.yzx-4.*q.zxy-q.yzx*q.yzx;return d/12.*.3;}'+
'vec3 q=vec3(p.x,p.z,p.y)*13.+vec3(0,0,7);vec3 d=vec3(-q.y-q.z,q.x+.2*q.y,.2+q.z*(q.x-5.7));return vec3(d.x,d.z,d.y)/13.*1.8;}'+
// what a particle wants: a velocity, a brightness and a colour
'vec3 fld(int m,vec3 p,vec4 h,uint id,out vec2 bc){bc=vec2(1.,h.w);'+
'if(m>=2&&m<=6){vec3 d=att(m,p);bc=vec2(1.,h.w*.3+length(d)*.28);return d*(.55+.9*h.y);}'+
'vec3 T=vec3(0);float k=3.+5.*h.z;'+
'if(m==0){float a=TAU*h.x+uT*.6,R=uQ.x,c3=cos(3.*a);T=R*vec3((2.+c3)*cos(2.*a),sin(3.*a),(2.+c3)*sin(2.*a))/3.+sph(h.yz)*R*.07*h.w;bc=vec2(1.,h.x*2.+uT*.1);k=5.+6.*h.z;}'+
'else if(m==1){vec2 xy=texelFetch(uX,ivec2(int(id&1023u),int(uQ.y)*64+int((id>>10)&63u)),0).xy;T=vec3(xy,(h.z-.5)*.04);bc=vec2(1.,xy.x*.22+.5+uT*.05);k=5.+7.*h.z;'+
'if(h.w>.86){T=rotY(sph(h.xy)*(1.1+h.z),uT*.3);bc.x=.35;}}'+
'else if(m==8){float r=length(p.xz)+1e-4,an=atan(p.z,p.x);float r0=.3+1.6*h.x*h.x+.07*sin(2.*an-7.*r+uT*1.5);vec2 rd=p.xz/r;'+
'vec3 w;w.xz=vec2(-rd.y,rd.x)*(.75/sqrt(r+.12))-rd*(r-r0)*2.5;float yT=(h.y-.5)*.05*(1.+2.*r)+.25*uR.x*r*sin(an+uT*.4);w.y=(yT-p.y)*6.;'+
'bc=vec2(1.,clamp(1.-r*.55,0.,1.)*.667);if(h.w<.12){T=sph(h.xy)*(.04+.2*uR.y*pow(h.z,.33));bc=vec2(1.2,.6);return(T-p)*k;}return w;}'+
'else if(m==9){if(h.w<.12){float a=TAU*h.x+uT*(.5+.3*h.y),rr=1.25+.5*h.y*h.y;T=vec3(cos(a)*rr,(h.z-.5)*.03,sin(a)*rr);T.yz=vec2(T.y*.94-T.z*.34,T.y*.34+T.z*.94);bc=vec2(.3,.3+.15*h.z);}else T=moon(h,vec3(0),.78,bc);}'+
'else if(m==10){if(h.w<.3)T=moon(h,uMo,.3,bc);else{float rr=sqrt(h.x)*3.2,a=TAU*h.y;vec2 xz=vec2(cos(a),sin(a))*rr;if(h.z<.5)xz.y=floor(xz.y*14.+.5)/14.;'+
'float d=length(xz-uMo.xz);float y=-.6+.06*sin(xz.x*3.+uT*1.3)+.05*sin(xz.y*4.1-uT*1.7)+.03*sin((xz.x+xz.y)*7.+uT*2.3)+.5*exp(-d*d*1.6)+.05*sin(d*11.-uT*6.)*exp(-d*.8);'+
'T=vec3(xz.x,y,xz.y);bc=vec2(.5+1.2*smoothstep(-.6,-.1,y),clamp((y+.68)*1.6,0.,1.)*.667);}}'+
'else if(m==11){if(h.w<.1){float u=h.x;T=vec3(.05*sin(u*5.),-.05-u*1.7,.05*cos(u*4.))+sph(h.yz)*.012;bc=vec2(.5,.02+.07*u);}'+
'else if(h.w<.2){T=sph(h.xy)*(.07+.05*h.z)*(1.+uQ.z);T.y+=.05;bc=vec2(1.3,.66);}'+
'else{float L=floor(h.x*5.),np=6.,kI=floor(h.y*np),u=pow(h.z,.6),vv=fract(h.y*np)*2.-1.;float len=.4+.2*L,al=(.12+uQ.z*(.3+.27*L))*(.55+.75*u);'+
'float th=(kI+.5)/np*TAU+L*2.4+vv*(3.1416/np)*1.15*pow(sin(3.1416*u),.6);float rr=len*u*sin(al);'+
'T=vec3(cos(th)*rr,len*u*cos(al)-vv*vv*.04*u,sin(th)*rr);bc=vec2(.9,clamp(.95-u*.55-L*.06,0.,1.)*.667);}}'+
'return(T-p)*k;}'+
'void main(){uint id=uint(gl_VertexID);vec4 h=H(id);vec3 p=aP.xyz,v=aV.xyz;vec2 bA,bB;'+
'float g=fract(h.w*7.31+h.x*3.7),m=clamp(uM*1.6-g*.6,0.,1.);vec3 w;vec2 bc;'+
'if(m<=0.)w=fld(uA,p,h,id,bc);else if(m>=1.)w=fld(uB,p,h,id,bc);else{vec3 wA=fld(uA,p,h,id,bA),wB=fld(uB,p,h,id,bB);w=mix(wA,wB,m);bc=mix(bA,bB,m);}'+
'w+=uN*vec3(sin(p.y*2.3+uT)+cos(p.z*3.1-uT*.7),sin(p.z*2.7+uT*1.1)+cos(p.x*2.9+uT*.6),sin(p.x*2.5-uT*.9)+cos(p.y*3.3+uT*.8));'+
'float sl=length(w);if(sl>9.)w*=9./sl;'+
'v+=(w-v)*(1.-exp(-uDt*uLag));vec3 dc=p-uC;v+=dc/(length(dc)+.02)*uK*(.25+h.y);p+=v*uDt;'+
'p+=(H(id*31u+uint(uF)*7919u).xyz-.5)*uJ;'+
'if(!(dot(p,p)<25.)){p=sph(h.xy)*.3*h.z;v=vec3(0);}'+
'oP=vec4(p,mix(aP.w,bc.y,.12));oV=vec4(v,mix(aV.w,bc.x,.15));}';
var DRAW='#version 300 es\nprecision highp float;layout(location=0)in vec4 aP;layout(location=1)in vec4 aV;uniform mat4 uVP;uniform float uI,uPh,uPs;out vec3 vC;'+
'void main(){gl_Position=uVP*vec4(aP.xyz,1.);gl_PointSize=uPs;float x=fract(aP.w+uPh)*3.;vec3 a=vec3(.02,.85,.8),b=vec3(1.,.07,.5),d=vec3(1.,.48,.03);'+
'vec3 c=x<1.?mix(a,b,x):x<2.?mix(b,d,x-1.):mix(d,a,x-2.);vC=c*aV.w*uI;}';
var DRAWF='#version 300 es\nprecision highp float;in vec3 vC;out vec4 o;void main(){o=vec4(vC,1.);}';
var QUAD='#version 300 es\nout vec2 v;void main(){vec2 p=vec2(gl_VertexID==1?3.:-1.,gl_VertexID==2?3.:-1.);v=p*.5+.5;gl_Position=vec4(p,0,1);}';
var FILL='#version 300 es\nprecision highp float;uniform float uD;out vec4 o;void main(){o=vec4(0,0,0,uD);}';
var DOWN='#version 300 es\nprecision highp float;in vec2 v;uniform sampler2D uS;uniform vec2 uP;out vec4 o;void main(){o=(texture(uS,v+uP*vec2(1,1))+texture(uS,v+uP*vec2(-1,1))+texture(uS,v+uP*vec2(1,-1))+texture(uS,v+uP*vec2(-1,-1)))*.25;}';
var BLUR='#version 300 es\nprecision highp float;in vec2 v;uniform sampler2D uS;uniform vec2 uP;out vec4 o;void main(){vec4 c=texture(uS,v)*.16;'+
'for(int i=1;i<7;i++){float f=float(i);float w=.16*exp(-f*f*.085);c+=(texture(uS,v+uP*f*1.4)+texture(uS,v-uP*f*1.4))*w;}o=c*1.25;}';
var COMP='#version 300 es\nprecision highp float;in vec2 v;uniform sampler2D uS,uG;uniform float uE,uBl,uF;out vec4 o;'+
'void main(){vec3 c=texture(uS,v).rgb+texture(uG,v).rgb*uBl;c=1.-exp(-c*uE);float l=dot(c,vec3(.3,.5,.2));c=mix(c,vec3(l),smoothstep(.75,1.4,l)*.5);'+
'float n=fract(sin(dot(gl_FragCoord.xy,vec2(12.9898,78.233))+uF)*43758.5453);o=vec4(pow(c,vec3(.92))+(n-.5)/255.,1.);}';
function sh(t,s){var o=gl.createShader(t);gl.shaderSource(o,s);gl.compileShader(o);if(!gl.getShaderParameter(o,gl.COMPILE_STATUS)){var m=gl.getShaderInfoLog(o);D.err.push(m);console.error(m)}return o}
function prog(vs,fs,tf){var p=gl.createProgram();gl.attachShader(p,sh(gl.VERTEX_SHADER,vs));gl.attachShader(p,sh(gl.FRAGMENT_SHADER,fs));
if(tf)gl.transformFeedbackVaryings(p,tf,gl.INTERLEAVED_ATTRIBS);gl.linkProgram(p);
if(!gl.getProgramParameter(p,gl.LINK_STATUS)){var m=gl.getProgramInfoLog(p);D.err.push(m);console.error(m)}
var u={},c=gl.getProgramParameter(p,gl.ACTIVE_UNIFORMS);for(var i=0;i<c;i++){var nm=gl.getActiveUniform(p,i).name;u[nm]=gl.getUniformLocation(p,nm)}p.u=u;return p}
var pSim=prog(SIM,'#version 300 es\nvoid main(){}',['oP','oV']),pDraw=prog(DRAW,DRAWF),pFill=prog(QUAD,FILL),pDown=prog(QUAD,DOWN),pBlur=prog(QUAD,BLUR),pComp=prog(QUAD,COMP);
// ---------- particle buffers (zero filled: every particle starts at the one seed point)
var buf=[],vao=[],cur=0,i;
for(i=0;i<2;i++){buf[i]=gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER,buf[i]);gl.bufferData(gl.ARRAY_BUFFER,NMAX*32,gl.DYNAMIC_COPY);
vao[i]=gl.createVertexArray();gl.bindVertexArray(vao[i]);gl.enableVertexAttribArray(0);gl.enableVertexAttribArray(1);
gl.vertexAttribPointer(0,4,gl.FLOAT,false,32,0);gl.vertexAttribPointer(1,4,gl.FLOAT,false,32,16)}
gl.bindVertexArray(null);gl.bindBuffer(gl.ARRAY_BUFFER,null);
var vq=gl.createVertexArray(),tfo=gl.createTransformFeedback();
// ---------- words as particle targets
var TXW=1024,TXR=64,PAGES=['MOONSEED','FABLE 5.2|MODEL SALT @ 2'];
(function(){var c=document.createElement('canvas'),W=1024,Hh=256;c.width=W;c.height=Hh;var x=c.getContext('2d',{willReadFrequently:true}),data=new Float32Array(TXW*TXR*PAGES.length*4);
PAGES.forEach(function(pg,pi){var ln=pg.split('|'),fs=ln.length>1?96:170,j,k;x.clearRect(0,0,W,Hh);x.fillStyle='#fff';x.textAlign='center';x.textBaseline='middle';
for(;;){x.font='900 '+fs+'px "Arial Black","Helvetica Neue",Helvetica,Arial,sans-serif';var mw=0;ln.forEach(function(l){mw=Math.max(mw,x.measureText(l).width)});if(mw<=W-40||fs<20)break;fs-=4}
ln.forEach(function(l,li){x.fillText(l,W/2,Hh/2+(li-(ln.length-1)/2)*fs*1.15)});
var im=x.getImageData(0,0,W,Hh).data,pts=[];for(j=0;j<Hh;j++)for(k=0;k<W;k++)if(im[(j*W+k)*4+3]>128)pts.push(k,j);
var np=pts.length/2||1,o=pi*TXW*TXR*4,sd=pi*977+1;if(pts.length<2)pts=[W/2,Hh/2];
for(j=0;j<TXW*TXR;j++){sd=(sd*1664525+1013904223)>>>0;k=sd%np;var jx=((sd>>>8)&255)/255,jy=((sd>>>16)&255)/255;
data[o+j*4]=(pts[k*2]+jx-W/2)*(3/W);data[o+j*4+1]=-(pts[k*2+1]+jy-Hh/2)*(3/W)}});
var t=gl.createTexture();gl.activeTexture(gl.TEXTURE3);gl.bindTexture(gl.TEXTURE_2D,t);gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA32F,TXW,TXR*PAGES.length,0,gl.RGBA,gl.FLOAT,data);
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST);gl.activeTexture(gl.TEXTURE0)})();
// ---------- render targets
var W=0,H=0,qw=0,qh=0,T={};
function tex(w,h,k){var t=k||gl.createTexture();gl.bindTexture(gl.TEXTURE_2D,t);gl.texImage2D(gl.TEXTURE_2D,0,HDR?gl.RGBA16F:gl.RGBA8,w,h,0,gl.RGBA,HDR?gl.HALF_FLOAT:gl.UNSIGNED_BYTE,null);
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);
if(!t.f){t.f=gl.createFramebuffer();gl.bindFramebuffer(gl.FRAMEBUFFER,t.f);gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,t,0)}
gl.bindFramebuffer(gl.FRAMEBUFFER,t.f);gl.clearColor(0,0,0,1);gl.clear(gl.COLOR_BUFFER_BIT);t.w=w;t.h=h;return t}
function resize(){var dpr=window.devicePixelRatio||1,w=Math.max(2,Math.round(cv.clientWidth*dpr)),h=Math.max(2,Math.round(cv.clientHeight*dpr)),k=Math.min(1,Math.sqrt(MAXPX/(w*h)));
w=Math.round(w*k);h=Math.round(h*k);if(w==W&&h==H)return;W=cv.width=w;H=cv.height=h;qw=Math.max(1,w>>2);qh=Math.max(1,h>>2);
T.a=tex(W,H,T.a);T.q=tex(qw,qh,T.q);T.r=tex(qw,qh,T.r)}
// ---------- the timeline: everything about a moment t (seconds) of the demo
var ki=0,si=0,lastK=-9,lastS=-9;
function ease(x){x=x<0?0:x>1?1:x;return x*x*(3-2*x)}
var GARD=[4,5,6,2,5,4,6,2],GD=[3.1,3.3,3.3,3.0,3.3,3.1,3.3,3.0];
function state(t){
var b=t/BAR,s={A:0,B:0,M:0,R:.1,pg:0,bloom:0,warp:0,clump:0,N:0,J:0,lag:10,yaw:0,pit:0,dist:2.6,tg:[0,0,0],E:1,dec:.55,ph:0,imp:1,mo:[0,.8,0],L:[1,0,0],C:[0,0,0],bl:.55};
var kv=t<lastK?0:Math.exp(-(t-lastK)*7),sv=t<lastS?0:Math.exp(-(t-lastS)*9),rw=(t/S.ROW)%2,ap=Math.exp(-rw*2.2);
if(t<0||b<4){b=Math.max(b,0);s.R=t<0?.16+.03*Math.sin(t*1.5):(.03+.4*ease(b/4))*(1+.22*ap*Math.min(1,b));s.yaw=.5*Math.sin(b*.8);s.pit=.35;s.dist=1.9-.4*b/4;s.E=t<0?.07:.3;s.dec=.7;s.N=.02}
else if(b<7){s.B=1;s.M=ease((b-4)/1.2);s.R=.43;s.yaw=.5*Math.sin(b*.8)*(1-.5*s.M);s.pit=.35*(1-s.M)+.05;s.dist=1.5+.85*s.M;s.E=.3+.5*s.M;s.dec=.7;s.N=.02;s.J=.0015*ap}
else if(b<8){s.A=1;s.M=ease((b-7)/.75);s.R=.04;s.yaw=.5*Math.sin(b*.8)*.5;s.pit=.05;s.dist=2.35-.3*(b-7);s.E=.8-.45*s.M;s.dec=.8;s.J=.004*(b-7)}
else if(b<16){s.N=.02;s.lag=60;s.B=2;s.M=Math.min(1,(b-8)*4);s.R=.04;s.yaw=(b-8)*.33;s.pit=.2+.15*Math.sin(b*.5);s.dist=2.0-.02*(b-8);s.tg=[0,.35*Math.min(1,(b-8)/2),0];s.imp=b<8.2?40:3;s.E=1.1}
else if(b<24){s.N=.02;s.lag=60;s.A=2;s.B=3;s.M=Math.min(1,(b-16)*2);s.yaw=2.64+(b-16)*.4;s.pit=.35+.25*Math.sin(b*.6);s.dist=2.5;s.tg=[0,.35-.25*Math.min(1,(b-16)/2),0];s.E=1;s.imp=3;s.ph=.15}
else if(b<40){s.A=3;s.B=8;s.M=Math.min(1,(b-24));s.lag=60-50*ease((b-24)*2);s.warp=ease((b-32)/2);s.clump=(b-24)/16;s.yaw=(b-24)*.16;s.pit=b<32?1-.07*(b-24):.42-.33*ease((b-32)/7);s.dist=b<32?3.4-.07*(b-24):2.85-.5*ease((b-32)/8);s.E=1.2;s.dec=.68;s.imp=1.3;s.ph=b<32?0:.08}
else if(b<56){var k=Math.floor((b-40)/2);s.N=.02;s.lag=10+50*ease((b-40)*2);s.A=k?GARD[k-1]:8;s.B=GARD[k];s.M=Math.min(1,(b-40-2*k)*4);s.yaw=k*1.9+(b-40)*.5;s.pit=(k%2?.5:-.25)+.2*Math.sin(b*1.1);s.dist=GD[k]-.3*(b-40-2*k);s.E=1.15;s.dec=.5;s.ph=k*.21;s.imp=b<40.5?1.2:3;s.J=.003*sv;s.tg=[0,GARD[k]==2?.3:GARD[k]==6?.25:0,0];if(GARD[k]==6)s.pit=.8+.15*Math.sin(b*1.1)}
else if(b<72){s.dec=.75;
if(b<60){s.lag=60-50*ease((b-56)*2);s.A=2;s.B=8;s.M=ease((b-56)/2);s.clump=.6+.4*(b-56)/4;s.warp=1-ease((b-56)/3)}else{s.A=8;s.B=9;s.M=ease((b-60)/7);s.clump=1}
s.yaw=-(b-56)*.14;var la=s.yaw+2.5-1.5*ease((b-60)/10);s.L=[Math.sin(la),.25,Math.cos(la)];s.pit=.75-.6*ease((b-56)/8);s.dist=3.3-1.05*ease((b-58)/12);s.E=1.15+.8*ease((b-60)/6);s.imp=.5;s.ph=.04;if(b>=70){s.J=.006*(b-70)*(.3+sv);s.dist-=.2*(b-70)}}
else if(b<88){var ma=(b-72)*.42+.6;s.A=9;s.B=10;s.M=Math.min(1,(b-72)*2);s.mo=[Math.cos(ma)*1.25,.72+.12*Math.sin(ma*2),Math.sin(ma)*1.25];s.yaw=-(b-72)*.2;s.L=[Math.sin(s.yaw+1.9),.3,Math.cos(s.yaw+1.9)];s.pit=.16+.22*ease((b-80)/6);s.dist=3.5-.5*ease((b-72)/3)+.6*ease((b-80)/6);s.tg=[0,-.1,0];s.C=[s.mo[0],-2.5,s.mo[2]];s.imp=.8;s.E=1.6;s.dec=.6;s.ph=.02}
else if(b<104){s.A=10;s.B=11;s.M=Math.min(1,(b-88));s.mo=[.2,.7,1.2];s.bloom=ease((b-89)/7)*(1+.07*kv)+(b>96?.08*Math.sin(b*3.1):0);
s.yaw=(b-88)*.25+(b>96?(b-96)*(b-96)*.05:0);s.pit=.3+.75*ease((b-95)/5)-.25*ease((b-101)/3);s.dist=2.9-.5*ease((b-90)/5)+.5*ease((b-100)/4);s.tg=[0,.25-.2*ease((b-95)/5),0];s.E=1.25;s.imp=1.2;s.dec=.6;s.ph=b>96?(b-96)*.085:0;s.J=.003*sv}
else{s.bloom=1;s.yaw=7.2*(1-ease((b-104)/1.2));s.pit=.8*(1-ease((b-104)/1.2));s.dist=2.9;s.dec=.72;s.imp=b<104.2?4:0;
if(b<107){s.A=11;s.B=1;s.pg=1;s.M=ease((b-104.1)/.8);s.E=1.25-.45*s.M}else{s.A=1;s.B=0;s.pg=1;s.M=ease((b-107)/.8);s.R=.006;s.E=.8-.6*s.M;s.E*=1-ease((t-LEN-.2)/1.2)}}
s.kv=kv;s.sv=sv;return s}
// ---------- one simulation step and one drawn frame
var frame=0,VP=new Float32Array(16),EYE=[0,0,3],kick0=0;
function camera(s){var cp=Math.cos(s.pit),d=s.dist*(1-.035*s.kv)*(W<H?1.5:1),e=[s.tg[0]+d*cp*Math.sin(s.yaw),s.tg[1]+d*Math.sin(s.pit),s.tg[2]+d*cp*Math.cos(s.yaw)];
var f=[s.tg[0]-e[0],s.tg[1]-e[1],s.tg[2]-e[2]],l=Math.hypot(f[0],f[1],f[2]);f[0]/=l;f[1]/=l;f[2]/=l;
EYE=e;var r=[-f[2],0,f[0]];l=Math.hypot(r[0],r[2]);r[0]/=l;r[2]/=l;var u=[r[1]*f[2]-r[2]*f[1],r[2]*f[0]-r[0]*f[2],r[0]*f[1]-r[1]*f[0]];
var fy=1.8,fx=fy*H/W,nr=.05,fr=40,A=(fr+nr)/(nr-fr),B=2*fr*nr/(nr-fr),v=[r[0],u[0],-f[0],r[1],u[1],-f[1],r[2],u[2],-f[2],-(r[0]*e[0]+r[1]*e[1]+r[2]*e[2]),-(u[0]*e[0]+u[1]*e[1]+u[2]*e[2]),f[0]*e[0]+f[1]*e[1]+f[2]*e[2]];
for(var j=0;j<4;j++){var a=v[j*3],bb=v[j*3+1],c=v[j*3+2];VP[j*4]=fx*a;VP[j*4+1]=fy*bb;VP[j*4+2]=A*c+(j==3?B:0);VP[j*4+3]=-c}}
function sim(t,dt,s,imp){var u=pSim.u;gl.useProgram(pSim);
gl.uniform1f(u.uT,t);gl.uniform1f(u.uDt,dt);gl.uniform1f(u.uM,s.M);gl.uniform1f(u.uK,imp);gl.uniform1f(u.uN,s.N);gl.uniform1f(u.uJ,kick0?.5:s.J);kick0=0;gl.uniform1f(u.uLag,s.lag);
gl.uniform1i(u.uA,s.A);gl.uniform1i(u.uB,s.B);gl.uniform1i(u.uF,frame&65535);gl.uniform4f(u.uQ,s.R,s.pg,s.bloom,0);gl.uniform4f(u.uR,s.warp,s.clump,0,0);
gl.uniform3fv(u.uMo,s.mo);gl.uniform3fv(u.uL,s.L);gl.uniform3fv(u.uC,s.C);gl.uniform3fv(u.uEye,EYE);gl.uniform1i(u.uX,3);
gl.bindVertexArray(vao[cur]);gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK,tfo);gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER,0,buf[1-cur]);
gl.enable(gl.RASTERIZER_DISCARD);gl.beginTransformFeedback(gl.POINTS);gl.drawArrays(gl.POINTS,0,n);gl.endTransformFeedback();gl.disable(gl.RASTERIZER_DISCARD);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER,0,null);gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK,null);cur=1-cur;frame++}
function quad(p,target,w,h){gl.useProgram(p);gl.bindFramebuffer(gl.FRAMEBUFFER,target);gl.viewport(0,0,w,h);gl.bindVertexArray(vq);gl.drawArrays(gl.TRIANGLES,0,3)}
function draw(s){
// fade what is there (the trail), then add every particle as one additive pixel
gl.enable(gl.BLEND);gl.blendFunc(gl.ZERO,gl.SRC_ALPHA);gl.useProgram(pFill);gl.uniform1f(pFill.u.uD,s.dec);quad(pFill,T.a.f,W,H);
gl.blendFunc(gl.ONE,gl.ONE);gl.useProgram(pDraw);gl.uniformMatrix4fv(pDraw.u.uVP,false,VP);
gl.uniform1f(pDraw.u.uI,.16*(1-s.dec)*(W*H/n)*(HDR?1:8));gl.uniform1f(pDraw.u.uPh,s.ph);gl.uniform1f(pDraw.u.uPs,1);
gl.bindVertexArray(vao[cur]);gl.drawArrays(gl.POINTS,0,n);gl.disable(gl.BLEND);
// glow: exact 4x4 box down, then a wide blur
gl.activeTexture(gl.TEXTURE0);gl.bindTexture(gl.TEXTURE_2D,T.a);gl.useProgram(pDown);gl.uniform1i(pDown.u.uS,0);gl.uniform2f(pDown.u.uP,1/W,1/H);quad(pDown,T.q.f,qw,qh);
gl.bindTexture(gl.TEXTURE_2D,T.q);gl.useProgram(pBlur);gl.uniform1i(pBlur.u.uS,0);gl.uniform2f(pBlur.u.uP,1/qw,0);quad(pBlur,T.r.f,qw,qh);
gl.bindTexture(gl.TEXTURE_2D,T.r);gl.uniform2f(pBlur.u.uP,0,1/qh);quad(pBlur,T.q.f,qw,qh);
gl.useProgram(pComp);gl.activeTexture(gl.TEXTURE1);gl.bindTexture(gl.TEXTURE_2D,T.q);gl.activeTexture(gl.TEXTURE0);gl.bindTexture(gl.TEXTURE_2D,T.a);
gl.uniform1i(pComp.u.uS,0);gl.uniform1i(pComp.u.uG,1);gl.uniform1f(pComp.u.uE,s.E*(1+.45*s.kv)/(HDR?1:8));gl.uniform1f(pComp.u.uBl,s.bl);gl.uniform1f(pComp.u.uF,frame%64);quad(pComp,null,W,H)}
function step(t,dt,doDraw){var imp=0;
while(ki<S.kicks.length&&S.kicks[ki]<=t){lastK=S.kicks[ki++];imp=1}
while(si<S.snares.length&&S.snares[si]<=t)lastS=S.snares[si++];
var s=state(t);camera(s);sim(t,dt,s,imp*s.imp);if(doDraw)draw(s);return s}
function seek(t){ki=si=0;lastK=lastS=-9;while(ki<S.kicks.length&&S.kicks[ki]<t)lastK=S.kicks[ki++];while(si<S.snares.length&&S.snares[si]<t)lastS=S.snares[si++]}
// ---------- sound: a thread makes the song in pieces, the first before the press
var chunks=[],pend=1,OUT,ctx=null,t0=0,off=qn('t',0),SR=44100,mute=Q.has('mute')||Q.has('shot'),W8;
function sched(c){var bf=ctx.createBuffer(2,c.l.length,SR);bf.getChannelData(0).set(c.l);bf.getChannelData(1).set(c.r);
var src=ctx.createBufferSource();src.buffer=bf;src.connect(OUT);src.onended=function(){D.ended=(D.ended||0)+1};var when=t0+c.p/SR-off,o=Math.max(0,ctx.currentTime+.02-when);
if(o<bf.duration){src.start(when+o,o);D.sched++;D.aEnd=when+bf.duration-t0+off}}
if(!mute)try{
var src=document.scripts[0].textContent+';var nx=SYNTH('+SR+'),fi=1;(function go(){var c=nx(fi?'+SR+':'+SR*6+');fi=0;if(c){postMessage(c,[c.l.buffer,c.r.buffer]);setTimeout(go,0)}else postMessage(0)})()';
W8=new Worker(URL.createObjectURL(new Blob([src],{type:'text/javascript'})));
W8.onmessage=function(e){if(e.data){chunks.push(e.data);D.chunks++;if(ctx&&!pend)sched(e.data)}else D.all=1};
W8.onerror=function(e){D.err.push('synth: '+e.message)}
}catch(e){D.err.push(''+e)}
// ---------- run
var bad=0,slow=0,started=0,tBase=0,tClick=0,last=0,acc=0,cnt=0,ended=0;
function now(){return performance.now()/1000}
function demoTime(){var t=now()-tBase;
if(ctx&&ctx.state=='running'){var ta=ctx.currentTime-t0+off,er=ta-t;if(Math.abs(er)>.08){tBase-=er;t=ta}else{tBase-=er*.03;t+=er*.03}}
return t}
function loop(){requestAnimationFrame(loop);resize();var nw=now(),ft=nw-last;last=nw;
if(!started){n=Math.min(NMAX,1<<18);step(-20+(nw%8),Math.min(ft,.033),1);return}
var t=demoTime();D.t=t;
if(ft<.2){D.ft.push(Math.round(ft*1e4)/10);acc+=ft;cnt++;if(ft>.021)slow++;
if(cnt>=40&&!fixedN){var av=acc/cnt*1000;var bd=av>19.5||slow>4;slow=0;if(av>26||(bd&&bad)){ceil=Math.max(NMIN,n*.9|0);n=Math.max(NMIN,n*.75|0);bad=0}else if(bd)bad=1;else{bad=0;ceil=Math.min(NMAX,ceil*1.02|0);if(av<17.4&&n<ceil)n=Math.min(ceil,n*1.2|0)}acc=cnt=0}}
D.n=n;
if(t>LEN+1.6){if(!ended){ended=1;D.done=1;bt.textContent='AGAIN';sp.style.display='flex'}t=LEN+1.6}
step(Math.max(t,0),Math.min(ft,.034),1)}
function start(){if(started){if(ended)location.reload();return}started=1;tClick=performance.now();sp.style.display='none';D.n0=n=fixedN?NMAX:mobile?1<<19:3<<19;
if(off>0)kick0=1;seek(off);step(off,1/60,1);D.first=Math.round(performance.now()-tClick);
if(!mute)try{var AC=window.AudioContext||window.webkitAudioContext;try{ctx=new AC({sampleRate:SR})}catch(e){ctx=new AC()}
if(ctx.resume)ctx.resume();OUT=ctx.createGain();OUT.gain.value=Q.has('quiet')?0:1;OUT.connect(ctx.destination);t0=ctx.currentTime+.06;D.ctx=ctx;var qi=0;(function nx(){if(qi<chunks.length){sched(chunks[qi++]);if(qi<2)nx();else setTimeout(nx,30)}else pend=0})()}catch(e){D.err.push(''+e)}tBase=now()+.06-off;last=now()}
bt.onclick=start;
addEventListener('keydown',function(e){if(e.key=='f'||e.key=='F'){var d=document,el=d.documentElement;if(d.fullscreenElement||d.webkitFullscreenElement)(d.exitFullscreen||d.webkitExitFullscreen).call(d);else(el.requestFullscreen||el.webkitRequestFullscreen).call(el)}
else if((e.key==' '||e.key=='Enter')&&!started){e.preventDefault();start()}});
resize();
if(Q.has('shot')){ // one frame of the demo at ?shot=<sec>, no sound: simulate the seconds before it, then draw
var ts=+Q.get('shot'),t=Math.max(0,ts-qn('pre',5)),dt=1/60;sp.style.display='none';started=1;n=fixedN?NMAX:1<<20;D.n=n;seek(t);kick0=1;
D.shot=function(sec){for(;t<sec-1e-6;t+=dt)step(t,dt,sec-t<.5);step(sec,dt,1);gl.finish();D.t=sec;D.done=1};
D.shot(ts)}
else{last=now();requestAnimationFrame(loop);if(Q.has('auto'))start()}
})();
</script>