this logo: 245 b

← TIDEMARK v2

index.html

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<title>TIDEMARK</title>
<style>
html,body{margin:0;height:100%;background:#000;overflow:hidden;color:#fff;font-family:"Arial Black","Helvetica Neue",Impact,system-ui,sans-serif}
canvas{position:fixed;inset:0;width:100%;height:100%;display:block}
#ui{position:fixed;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;background:radial-gradient(ellipse at 50% 60%,rgba(0,0,0,.15),rgba(0,0,0,.72));transition:opacity .6s;padding:4vmin;box-sizing:border-box}
#ui.off{opacity:0;pointer-events:none}
h1{font-style:italic;font-weight:900;font-size:min(15vw,150px);letter-spacing:-.03em;margin:0;line-height:.9;text-shadow:.04em 0 0 rgba(255,60,120,.7),-.04em 0 0 rgba(40,220,255,.7),0 0 .5em rgba(255,150,80,.5)}
p{font:600 min(3.4vw,15px)/1.6 ui-monospace,Menlo,Consolas,monospace;letter-spacing:.22em;text-transform:uppercase;opacity:.8;margin:2.2em 0 2.6em}
button{font:inherit;font-style:italic;font-size:min(5.5vw,26px);color:#000;background:#fff;border:0;padding:.7em 1.8em;cursor:pointer;letter-spacing:.04em;min-width:min(78vw,330px);box-shadow:.25em .25em 0 #ff3c78,-.25em -.25em 0 #28dcff}
button:disabled{background:#222;color:#9ab;cursor:default;box-shadow:none}
small{font:500 11px/1.5 ui-monospace,Menlo,monospace;letter-spacing:.15em;opacity:.55;margin-top:2.6em;text-transform:uppercase}
</style>
</head>
<body>
<canvas id="c"></canvas>
<div id="ui">
<h1>TIDEMARK</h1>
<p>a realtime demo &middot; 1999 style<br>procedural 3D + synth, nothing loaded</p>
<button id="go" disabled>PRECALC 0%</button>
<small>sound on<span id="fsh"> &middot; F = fullscreen</span></small><script>document.fullscreenEnabled||document.getElementById("fsh").remove()</script>
</div>
<script id="fs" type="x-shader/x-fragment">#version 300 es
precision highp float;
uniform vec2 uR;uniform float uT;uniform vec3 uCp,uCt,uSd;
uniform vec4 uCr,uB[8],uBB,uRg,uRu,uFx,uTx;uniform sampler2D uX;
out vec4 o;
float E,DAY,DW;vec3 GL,LC,AC,LD;
float h1(vec2 p){vec3 q=fract(vec3(p.xyx)*.1031);q+=dot(q,q.yzx+33.33);return fract((q.x+q.y)*q.z);}
float vn(vec2 p){vec2 i=floor(p),f=fract(p);f=f*f*(3.-2.*f);return mix(mix(h1(i),h1(i+vec2(1,0)),f.x),mix(h1(i+vec2(0,1)),h1(i+vec2(1,1)),f.x),f.y);}
float fbm(vec2 p){float a=.5,s=0.;for(int i=0;i<3;i++){s+=a*vn(p);p=p*2.03+17.;a*=.5;}return s/.875;}
vec3 sky(vec3 d){
  float y=max(d.y,0.),sd=max(dot(d,uSd),0.);
  vec3 zen=mix(vec3(.008,.014,.05),vec3(.07,.24,.62),DAY),hor=mix(vec3(.03,.065,.13),vec3(.72,.68,.74),DAY);
  vec3 c=mix(hor,zen,pow(y,.45));
  c+=GL*DW*exp(-y*5.5)*(.22+.9*pow(sd,3.));
  c+=GL*DW*pow(sd,10.)*.35;
  vec3 g=floor(d*210.);
  c+=step(.9986,h1(g.xz+g.y*13.7))*pow(y,.6)*(1.-smoothstep(-.2,-.02,E))*(.6+.4*sin(uT*3.+g.x));
  vec2 cp=d.xz/(d.y+.12)*1.15+vec2(3.,uT*.015+uCp.z*.0015);
  float cl=smoothstep(.46,.8,fbm(cp))*smoothstep(0.,.16,d.y);
  vec3 cc=mix(zen*.6+vec3(.015,.02,.04),GL*1.25,clamp(DW*(.25+pow(sd,2.)),0.,1.))+DAY*vec3(.55,.5,.5);
  c=mix(c,cc,cl*.75);
  float vis=step(0.,d.y);
  c+=vis*smoothstep(.9993,.9997,dot(d,uSd))*vec3(1.,.92,.75)*8.;
  c+=vis*(pow(sd,90.)*1.2+pow(sd,14.)*.25)*(GL*DW+vec3(1.,.85,.6)*DAY)*smoothstep(-.06,.06,E);
  return c;
}
vec3 env(vec3 r){
  if(uCp.y<0.){float u=r.y*.5+.5;return mix(vec3(0.,.03,.06),vec3(.08,.42,.5),u*u)+pow(max(r.y,0.),6.)*vec3(.6,.95,.9);}
  vec3 s=sky(vec3(r.x,abs(r.y),r.z));
  if(r.y<0.)s=s*.42+vec3(.004,.02,.03);
  return s*(.3+.7*smoothstep(0.,.1,abs(r.y)));
}
float dBl(vec3 p){
  float b=length(p-uBB.xyz)-uBB.w;if(b>1.)return b;
  float s=0.;for(int i=0;i<8;i++)s+=exp(-3.*(length(p-uB[i].xyz)-uB[i].w));
  return -log(s)/3.;
}
float dRu(vec3 p){
  vec2 c=floor(p.xz/8.),q=p.xz-(c+.5)*8.;
  float h=h1(c),m=smoothstep(uRu.x,uRu.x+70.,p.z)*(1.-smoothstep(uRu.y-90.,uRu.y,p.z));
  float ht=(1.5+h*h*24.)*m;
  if(h<.22||abs(c.x+.5)<1.||m<=0.)ht=-9.;
  vec2 w=vec2(1.6+2.*fract(h*7.3),1.6+2.*fract(h*13.7));
  vec3 d=abs(vec3(q.x,p.y,q.y))-vec3(w.x,ht,w.y);
  float b=min(max(d.x,max(d.y,d.z)),0.)+length(max(d,0.));
  if(fract(h*5.)>.5)b=max(b,(p.y-ht+1.5+(q.x*(fract(h*31.)-.5)-q.y*(fract(h*17.)-.5))*1.3)*.7);
  return min(b,min(4.-abs(q.x),4.-abs(q.y))+.4);
}
float dRg(vec3 p,out vec2 q){
  float k=clamp(floor((p.z-uRg.x)/24.+.5),0.,uRg.y),zr=uRg.x+24.*k;
  float cy=.5-11.*smoothstep(70.,140.,zr-uCp.z);
  q=vec2(length(vec2(p.x,p.y-cy))-5.,p.z-zr);
  vec2 a=abs(q)-vec2(.26,.5);
  return length(max(a,0.))+min(max(a.x,a.y),0.)-.06;
}
float th(vec2 x){return fbm(x*.04+5.)*30.-9.;}
float dTr(vec3 p){
  float m=uRu.z*smoothstep(uRu.w,uRu.w+90.,p.z)*smoothstep(8.,36.,abs(p.x));
  if(m<=0.)return p.y+4.;
  return (p.y-mix(-4.,th(p.xz),m))*.5;
}
float map(vec3 p){vec2 q;float d=min(dBl(p),min(dRu(p),dRg(p,q)));if(uRu.z>0.)d=min(d,dTr(p));return d;}
float march(vec3 ro,vec3 rd,float tm,int n){
  float t=.05;
  for(int i=0;i<n;i++){float d=map(ro+rd*t);if(d<.0015*t+.002)return t;t+=d;if(t>tm)break;}
  return -1.;
}
vec3 shade(vec3 p,vec3 rd,float t){
  vec2 e=vec2(.004+.0015*t,-.004-.0015*t);
  vec3 n=normalize(e.xyy*map(p+e.xyy)+e.yyx*map(p+e.yyx)+e.yxy*map(p+e.yxy)+e.xxx*map(p+e.xxx));
  vec2 q;float db=dBl(p),du=dRu(p),dg=dRg(p,q),dt=uRu.z>0.?dTr(p):1e9;
  vec3 r=reflect(rd,n),c;
  float uw=step(p.y,0.)*step(uCp.y,0.),sl=max(dot(n,LD),0.),fr=pow(1.-max(dot(n,-rd),0.),3.);
  float ca=pow(.5+.5*sin(p.x*1.3+sin(p.z*1.1+uT)*1.5+uT*.7)*sin(p.z*1.7+sin(p.x*.9-uT)*1.5),3.);
  vec3 li=mix(AC*(.6+.4*n.y)+LC*sl,vec3(.03,.16,.2)*(.5+n.y*.5)+ca*vec3(.1,.4,.42)*max(n.y+.4,0.),uw);
  if(db<du&&db<dg&&db<dt){
    c=env(r)*vec3(.9,.95,1.)*(1.+fr)+pow(max(dot(r,uSd),0.),160.)*LC*5.+fr*.06;
  }else if(dg<du&&dg<dt){
    c=env(r)*vec3(.75,.7,.65);
    float st=step(q.x,-.24)*step(abs(q.y),.3);
    c+=st*mix(vec3(.1,.9,1.),vec3(1.,.35,.1),DAY+DW*.5)*(.5+3.*uRg.z+3.*exp(-abs(p.z-uCp.z)*.08));
  }else if(du<dt){
    vec3 an=abs(n);
    vec2 w=an.y>.5?p.xz:vec2(an.x>an.z?p.z:p.x,p.y);
    float cn=vn(w*2.5)*.6+vn(w*9.)*.4;
    c=vec3(.2,.21,.24)*(.45+cn)*li;
    vec2 wq=w*vec2(.85,.7),wf=fract(wq);
    float wh=h1(floor(wq)+floor(p.xz/8.)*7.);
    float win=step(.2,wf.x)*step(.3,wf.y)*step(an.y,.5)*step(.3,wh);
    c=mix(c,(vec3(.01,.015,.025)+env(r)*.55)*mix(vec3(1.),vec3(.3,.9,.9),uw),win*.9);
    c+=win*step(.972,wh)*vec3(1.,.55,.2)*1.6*(1.-DAY)*(1.-uw);
    c=mix(c,vec3(.02,.05,.035)*(li+.2),smoothstep(2.6,0.,abs(p.y))*.75*(1.-uw));
  }else{
    float k=vn(p.xz*.7)*.5+vn(p.xz*2.9)*.25;
    c=mix(vec3(.17,.13,.11),vec3(.08,.15,.06),smoothstep(.55,.85,n.y)*(1.-smoothstep(9.,13.,p.y+k*4.)));
    c=mix(c,vec3(.75,.78,.85),smoothstep(12.,15.,p.y+k*6.)*smoothstep(.5,.8,n.y));
    c*=(.6+k)*li*1.4;
  }
  return c;
}
vec3 fogc(vec3 rd){return sky(normalize(vec3(rd.x,.03+abs(rd.y)*.25,rd.z)));}
vec3 wn(vec2 p,float t){
  float a=.55/(1.+t*.035);vec2 g=vec2(0.);
  for(int i=0;i<4;i++){
    float fi=float(i);vec2 d=vec2(cos(fi*1.7+.3),sin(fi*1.7+.3));
    float f=.4*pow(1.75,fi);
    g+=d*cos(dot(p,d)*f+uT*(1.1+.45*fi))/(1.+fi);
  }
  g+=(vec2(vn(p*1.6+uT*.35),vn(p*1.6+7.-uT*.35))-.5)*1.3;
  return normalize(vec3(-g.x*a,1.,-g.y*a));
}
void main(){
  E=uSd.y;DAY=smoothstep(-.08,.36,E);DW=exp(-E*E*30.);
  GL=mix(vec3(.9,.13,.42),vec3(1.,.5,.12),smoothstep(-.22,.1,E));
  LC=GL*DW*1.1+vec3(1.,.9,.75)*DAY*1.4;
  AC=mix(vec3(.035,.055,.11),vec3(.26,.33,.46),DAY)+GL*DW*.09;
  LD=normalize(vec3(uSd.x,max(uSd.y,.05)+.12,uSd.z));
  vec2 uv=(2.*gl_FragCoord.xy-uR)/min(uR.y,uR.x*1.25);
  uv+=uFx.z*.03*sin(uv.yx*8.+uT*vec2(17.,13.));
  float cr=cos(uCr.x),sr=sin(uCr.x);uv=vec2(uv.x*cr-uv.y*sr,uv.x*sr+uv.y*cr);
  vec3 ro=uCp,f=normalize(uCt-uCp),s=normalize(cross(vec3(0,1,0),f)),u=cross(f,s);
  vec3 rd=normalize(uv.x*s+uv.y*u+uCr.y*f);
  bool uw=ro.y<0.;
  float tw=-ro.y/rd.y;if(tw<0.)tw=1e5;
  float far=uw?75.:320.;
  float t=march(ro,rd,min(tw,far),uw?64:100);
  vec3 c;float ft=-1.;
  vec3 UF=vec3(0.,.085,.125)*(.7+DAY)+GL*DW*.03+vec3(.04,.28,.32)*pow(max(rd.y,0.),3.);
  if(t>0.){c=shade(ro+rd*t,rd,t);ft=t;}
  else if(tw<far){
    vec3 p=ro+rd*tw,n=wn(p.xz,tw);ft=tw;
    if(!uw){
      vec3 r=reflect(rd,n);r.y=abs(r.y)+.015;r=normalize(r);
      float t2=march(p,r,140.,44);
      vec3 rc;
      if(t2>0.){rc=shade(p+r*t2,r,t2+tw);rc=mix(fogc(r),rc,exp(-t2*uCr.z));}else rc=sky(r);
      float fr=.03+.97*pow(1.-max(dot(-rd,n),0.),5.);
      c=mix(vec3(.004,.022,.03)+AC*.06,rc,.14+.86*fr);
    }else{
      vec3 rf=refract(rd,-n,1.33);
      if(dot(rf,rf)<.5)c=UF*1.6+vec3(0.,.05,.06);
      else c=sky(rf)*vec3(.55,.95,1.)*1.3+vec3(.03,.2,.22);
    }
  }else c=uw?UF:sky(rd);
  if(ft>0.)c=uw?mix(UF,c,exp(-ft*.085)):mix(fogc(rd),c,exp(-ft*uCr.z));
  if(uw)c+=vec3(.1,.38,.42)*pow(max(0.,sin(rd.x*13.+sin(rd.z*9.+uT*.5)*2.+uT*.3)),6.)*smoothstep(-.2,.7,rd.y)*.22;
  c=1.-exp(-c*1.75);
  c=pow(c,vec3(.4545));
  c=mix(c,c*c*(3.-2.*c),.45);
  vec2 v=gl_FragCoord.xy/uR-.5;
  c*=1.-dot(v,v)*.85;
  c*=1.+.07*uFx.y;
  vec2 q=(2.*gl_FragCoord.xy-uR)/uR.y;
  float W=min(uR.x/uR.y*.94,1.9);
  vec2 tu=vec2(q.x,q.y-uTx.z)/(uTx.y*W);tu=vec2(tu.x*.5+.5,.5-tu.y*2.);
  float ab=uFx.w*.012;
  vec3 tx=vec3(texture(uX,tu+vec2(ab,0.)).r,texture(uX,tu).r,texture(uX,tu-vec2(ab,0.)).r);
  float gw=textureLod(uX,tu,3.5).r+textureLod(uX,tu,5.5).r;
  c=mix(c,vec3(1.),tx*uTx.x)+mix(vec3(1.,.25,.5),vec3(1.,.6,.2),DAY)*gw*uTx.w*uTx.x;
  c+=uFx.x;
  c*=uCr.w;
  c+=(h1(gl_FragCoord.xy+fract(uT)*91.)-.5)/128.;
  o=vec4(c,1.);
}
</script>
<script>
"use strict";
// ---------- soundtrack: rendered to a buffer in a worker, nothing loaded ----------
function SYN(){
const SR=44100,SS=4725,BARS=SS*16,N=96*BARS+SR*7,TAU=6.283185307179586,MK=self.MK||1023;
const L=new Float32Array(N),R=new Float32Array(N),V=new Float32Array(N),E=new Float32Array(N);
const mt=n=>440*Math.pow(2,(n-69)/12);
let sd=12345;const rn=()=>{sd=(sd*16807)%2147483647;return sd/1073741823.5-1};
const P1=[[57,60,64,33],[57,60,65,29],[55,60,64,36],[55,59,62,31]],P2=[P1[1],P1[3],P1[0],P1[2]];
const ch=b=>b>=96?P1[0]:(b>=48&&b<80?P2:P1)[(b>>1)&3];
const kickOn=(b,q)=>((b>=8&&b<40)||(b>=48&&b<88))&&!(q==3&&(b==23||b==39||b==63||b==79));
const drum=b=>(b>=16&&b<40)||(b>=48&&b<88);
const saw=(p,d)=>{let v=2*p-1;if(p<d){const t=p/d;v-=t+t-t*t-1}else if(p>1-d){const t=(p-1)/d;v-=t*t+t+t+1}return v};
function F(){let a=0,b=0,g=0,k=1,a1=0,a2=0,a3=0,v1=0,v2=0;
  const run=x=>{const v3=x-b;v1=a1*a+a2*v3;v2=b+a2*a+a3*v3;a=2*v1-a;b=2*v2-b};
  return{set(f,q){g=Math.tan(Math.PI*Math.min(f,SR*.45)/SR);k=1/q;a1=1/(1+g*(g+k));a2=g*a1;a3=g*a2},
    lp(x){run(x);return v2},bp(x){run(x);return v1*k},hp(x){run(x);return x-k*v1-v2}}}
const prog=p=>postMessage({p});
const cl=(x,a,b)=>x<a?a:x>b?b:x;
// arp
const arpCut=x=>x<8?260+x*x/64*2400:x<40?2700+1300*Math.sin(x*.4):x<48?450+(x-40)*(x-40)/64*5200:x<88?3700+1700*Math.sin(x*.4):3500*(1-(x-88)/5)+300;
const AP=[0,1,2,3,2,1,4,2,3,1,2,3,5,3,2,1];
if(MK&1)for(let s=64;s<92*16;s++){
  const b=s>>4,x=s/16,c=ch(b),k=AP[s&15],n=c[k%3]+12*(k/3|0)+12;
  const vol=.3*Math.min(1,(x-4)/4)*(b>=40&&b<48?.6:1)*(b>=88?1-(x-88)/4.2:1)*[1,.55,.75,.6][s&3];
  const f=mt(n),d=f/SR,d2=d*1.007,i0=s*SS,len=SS*1.7|0,cut=arpCut(x),fl=F();let p=0,p2=.5;
  for(let i=0;i<len;i++){const t=i/SR;if(!(i&15))fl.set(cut*(.35+.65*Math.exp(-t*16))+200,3);
    const en=Math.min(1,i/60)*Math.exp(-t*11)*(i>len-200?(len-i)/200:1);
    p+=d;p-=p|0;p2+=d2;p2-=p2|0;
    E[i0+i]+=fl.lp(saw(p,d)+saw(p2,d2))*en*vol}
}
prog(.12);
// lead
const ML={33:[76,6,74,2,72,4,76,4,81,8,79,4,76,4],29:[77,6,76,2,72,4,77,4,81,8,79,4,77,4],36:[79,6,76,2,72,4,79,4,84,8,83,4,79,4],31:[74,6,71,2,67,4,74,4,79,8,83,4,86,4]};
if(MK&2){const fl=F();let f=0,en=0,ph=[0,.3,.6,.1],gate=0;
  for(let s=24*16;s<80*16;s++){
    const b=s>>4;if(b>=40&&b<56){en=0;f=0;continue}
    const m=ML[ch(b)[3]],j=s&31;let a=0,nt=0,last=0;
    for(let k=0;k<m.length;k+=2){if(j<a+m[k+1]){nt=m[k];last=j==a+m[k+1]-1;break}a+=m[k+1]}
    const ft=mt(nt),hi=b>=64,i0=s*SS;if(!f)f=ft;
    for(let i=0;i<SS;i++){
      f+=(ft-f)*.004;gate=last&&i>SS*.6?0:1;en+=(gate-en)*(gate?.003:.0012);
      const t=(i0+i)/SR,ff=f*(1+.004*Math.sin(TAU*5.6*t));
      if(!(i&15))fl.set(1500+3000*en,1.3);
      let v=0;for(let k=0;k<4;k++){const d=ff*[1,1.006,.994,2][k]/SR;ph[k]+=d;ph[k]-=ph[k]|0;v+=saw(ph[k],d)*(k==3?(hi?.5:0):1)}
      E[i0+i]+=fl.lp(v)*en*.13}
  }}
prog(.2);
// ping-pong delay (dotted 8th) on the arp+lead bus
{const D=3*SS,bl=new Float32Array(D),br=new Float32Array(D);let p=0;
  for(let i=0;i<N;i++){const x=E[i],a=bl[p],b=br[p];bl[p]=x+b*.55;br[p]=a*.75;if(++p==D)p=0;
    L[i]+=x*.75+a*.42;R[i]+=x*.75+b*.42;V[i]+=(x+a+b)*.3;E[i]=0}}
prog(.26);
// pad + choir, one pass per chord (2 bars)
for(let g=0;g<=48;g++){
  const b=g*2,c=ch(b),i0=b*BARS,len=(g==48?20:32)*SS,rel=SR*.6|0,tot=Math.min(len+rel,N-i0);
  if(MK&4){
    const pv=.08*(b<8?.45+b/14:1)*(b>=40&&b<48?.8:1),cut=b<8?450+170*b:b>=40&&b<48?700+60*(b-40):2300;
    const ns=[c[0],c[1],c[2],c[3]+24,c[0]+12],ph=[],dl=[];
    for(const n of ns)for(const dt of[.9965,1.0035]){ph.push(rn()*.5+.5);dl.push(mt(n)*dt/SR)}
    const fl=F(),fr=F();
    for(let i=0;i<tot;i++){
      if(!(i&31)){const cc=cut*(1+.3*Math.sin((i0+i)/SR*.7));fl.set(cc,.9);fr.set(cc*1.05,.9)}
      let l=0,r=0;for(let k=0;k<10;k++){ph[k]+=dl[k];ph[k]-=ph[k]|0;const v=saw(ph[k],dl[k]);if(k&1)r+=v;else l+=v}
      const en=Math.min(1,i/(SR*.25))*(i>len?1-(i-len)/rel:1)*pv;
      l=fl.lp(l)*en;r=fr.lp(r)*en;L[i0+i]+=l;R[i0+i]+=r;V[i0+i]+=(l+r)*.2}
  }
  if(MK&8&&(b<8||(b>=40&&b<48)||b>=64)){
    const cv=(b<8?.5+b/20:b<48?1:b<80?.75:1)*.3,ph=[],dl=[];
    for(let k=0;k<3;k++)for(const dt of[.996,1.004]){ph.push(rn()*.5+.5);dl.push(mt(c[k]+12)*dt/SR)}
    const fm=[F(),F(),F(),F(),F(),F()];
    for(let i=0;i<tot;i++){
      const t=(i0+i)/SR;
      if(!(i&63)){const m=.5+.5*Math.sin(t*.9),f1=450+350*m,f2=800+350*m;
        fm[0].set(f1,9);fm[1].set(f2,10);fm[2].set(2850,9);fm[3].set(f1*1.03,9);fm[4].set(f2*1.03,10);fm[5].set(2950,9)}
      let l=0,r=0;
      for(let k=0;k<6;k++){const d=dl[k]*(1+.0045*Math.sin(TAU*(5.2+k*.13)*t+k));ph[k]+=d;ph[k]-=ph[k]|0;const v=saw(ph[k],d);if(k&1)r+=v;else l+=v}
      l+=rn()*.25;r+=rn()*.25;
      const en=Math.min(1,i/(SR*.45))*(i>len?1-(i-len)/rel:1)*cv;
      l=(fm[0].bp(l)+fm[1].bp(l)*.55+fm[2].bp(l)*.22)*en;r=(fm[3].bp(r)+fm[4].bp(r)*.55+fm[5].bp(r)*.22)*en;
      L[i0+i]+=l;R[i0+i]+=r;V[i0+i]+=(l+r)*.55}
  }
  if(!(g&3))prog(.26+.4*g/48);
}
// sub drone: intro and the underwater break
if(MK&16)for(const[b0,b1]of[[0,8],[40,48]])for(let i=b0*BARS,e=b1*BARS;i<e;i++){
  const u=(i-b0*BARS)/(e-b0*BARS),v=Math.sin(TAU*55*i/SR)*Math.sin(Math.PI*u)**.6*.14;L[i]+=v;R[i]+=v}
// bass: offbeat, rolling 16ths after the drop
if(MK&32)for(let b=8;b<88;b++)for(let q=0;q<4;q++){
  if(!kickOn(b,q))continue;const c=ch(b),roll=b>=48;
  for(const[o,vv]of roll?[[1,.7],[2,1],[3,.8]]:[[2,1]]){
    const i0=((b*4+q)*4+o)*SS,len=roll?SS*.95|0:SS*1.9|0,f=mt(c[3]+12),d=f/SR,fl=F();let p=0;
    for(let i=0;i<len;i++){const t=i/SR;if(!(i&15))fl.set(240+(roll?1500:1000)*Math.exp(-t*22),2);
      p+=d;p-=p|0;
      const v=(fl.lp(saw(p,d))+Math.sin(TAU*f*.5*t)*.9)*Math.min(1,i/80)*Math.exp(-t*4)*Math.min(1,(len-i)/200)*.26*vv;
      L[i0+i]+=v;R[i0+i]+=v}
  }
}
prog(.72);
// noise sweeps, crashes (stereo, on the ducked bus)
if(MK&64){
  for(const[b0,b1,am]of[[6,8,.7],[22,24,.6],[44,48,1],[62,64,.9],[78,80,.6]]){
    const i0=b0*BARS,len=(b1-b0)*BARS,fl=F(),fr=F();
    for(let i=0;i<len;i++){const u=i/len;if(!(i&15)){const f=300*Math.pow(32,u);fl.set(f,2.5);fr.set(f*1.1,2.5)}
      const a=u*u*.5*am,l=fl.bp(rn())*a,r=fr.bp(rn())*a;L[i0+i]+=l;R[i0+i]+=r;V[i0+i]+=(l+r)*.3}
  }
  for(const b of[8,16,24,40,48,56,64,80,96]){
    const i0=b*BARS,len=SR*2.6|0,fl=F(),fr=F();fl.set(3500,.7);fr.set(4200,.7);
    for(let i=0;i<len;i++){const a=Math.exp(-i/SR*2.1)*.26*Math.min(1,(len-i)/2000),l=fl.hp(rn())*a,r=fr.hp(rn())*a;L[i0+i]+=l;R[i0+i]+=r;V[i0+i]+=(l+r)*.25}
  }
  // splash at the dive
  {const i0=40*BARS-SS*2,len=SR*3,fl=F();fl.set(900,.8);
    for(let i=0;i<len;i++){const t=i/SR,v=fl.lp(rn())*Math.min(1,t*8)*Math.exp(-t*1.6)*.5;V[i0+i]+=v;L[i0+i]+=v*.5;R[i0+i]+=v*.5}}
}
prog(.78);
// drums (mono, not ducked)
if(MK&128){
  const mk=(len,fn)=>{const a=new Float32Array(len);for(let i=0;i<len;i++)a[i]=fn(i/SR,i);return a};
  const add=(s,i0,g)=>{for(let i=0;i<s.length;i++)E[i0+i]+=s[i]*g};
  let ph=0;const kick=mk(SR*.42|0,(t,i)=>{ph+=(46+185*Math.exp(-t*36))/SR;return Math.tanh((Math.sin(TAU*ph)*Math.exp(-t*7)+rn()*Math.exp(-t*300)*.25)*1.7)*Math.min(1,(SR*.42-i)/300)});
  let f1=F();f1.set(1400,1.1);const clap=mk(SR*.35|0,t=>f1.bp(rn())*(Math.exp(-(t%.011)*160)*(t<.033?1:0)+(t>=.033?Math.exp(-(t-.033)*17):0))*1.6);
  let f2=F();f2.set(8500,.8);const hc=mk(SR*.08|0,t=>f2.hp(rn())*Math.exp(-t*75));
  let f3=F();f3.set(7000,.8);const ho=mk(SR*.25|0,(t,i)=>f3.hp(rn())*Math.exp(-t*13)*Math.min(1,(SR*.25-i)/900));
  let f4=F();f4.set(1900,.9);const sn=mk(SR*.16|0,t=>f4.bp(rn())*Math.exp(-t*28)*1.5+Math.sin(TAU*(190*t+20*Math.exp(-t*30)))*Math.exp(-t*38));
  ph=0;const imp=mk(SR*2|0,(t,i)=>{ph+=(30+40*Math.exp(-t*5))/SR;return Math.sin(TAU*ph)*Math.exp(-t*2.2)*Math.min(1,(SR*2-i)/2000)});
  for(let b=0;b<96;b++)for(let q=0;q<4;q++){
    const i0=(b*4+q)*4*SS;
    if(kickOn(b,q))add(kick,i0,.8);
    if(drum(b)){
      if(q&1)add(clap,i0,.5);
      add(hc,i0,.14);add(hc,i0+SS,.07);add(hc,i0+3*SS,.09);
      if(b>=64&&b<80){add(hc,i0+SS*1.5|0,.04);add(hc,i0+SS*3.5|0,.05)}
    }
    if((b>=12&&b<40)||(b>=48&&b<88))add(ho,i0+2*SS,.15);
  }
  for(const[b0,b1]of[[46,48],[63,64]]){const s0=b0*16,s1=b1*16;
    for(let h=s0*2;h<s1*2;h++){const u=(h/2-s0)/(s1-s0);if(h&1&&u<.75)continue;add(sn,h*SS/2|0,(.08+u*u*.38))}}
  for(const b of[8,24,40,48,64,96])add(imp,b*BARS,.6);
}
prog(.84);
// master: sidechain pump, reverb, level, soft clip
{
  const cls=[1116,1188,1277,1356,1422,1491],cb=[],ci=[],cf=[],ap=[],ai=[];
  for(let k=0;k<12;k++){cb.push(new Float32Array(cls[k%6]+(k>5?23:0)));ci.push(0);cf.push(0)}
  for(let k=0;k<4;k++){ap.push(new Float32Array([556,441,579,464][k]));ai.push(0)}
  let sk=1e9,ss=0,sc=0;
  for(let i=0;i<N;i++){
    if(i%(4*SS)==0){const bi=i/(4*SS);if(kickOn(bi>>2,bi&3))sk=0}
    const u=1-sk/12400,g=u>0?1-.74*u*u:1;sk++;
    const x=V[i]*g+E[i]*.04;let wl=0,wr=0;
    for(let k=0;k<12;k++){const B=cb[k],y=B[ci[k]];cf[k]=y*.78+cf[k]*.22;B[ci[k]]=x+cf[k]*.85;if(++ci[k]==B.length)ci[k]=0;if(k<6)wl+=y;else wr+=y}
    for(let k=0;k<4;k++){const B=ap[k],z=k<2?wl:wr,y=B[ai[k]],w=y-z*.5;B[ai[k]]=z+y*.5;if(++ai[k]==B.length)ai[k]=0;if(k<2)wl=w;else wr=w}
    const l=L[i]*g+E[i]+wl*.045,r=R[i]*g+E[i]+wr*.045;L[i]=l;R[i]=r;
    if(i>=56*BARS&&i<64*BARS){ss+=l*l+r*r;sc+=2}
    if(!(i&1048575))prog(.84+.14*i/N);
  }
  const gn=self.GN||.26/Math.sqrt(ss/sc||1);
  for(let i=0;i<N;i++){const fd=Math.min(1,(N-i)/(SR*1.5));L[i]=Math.tanh(L[i]*gn)*fd;R[i]=Math.tanh(R[i]*gn)*fd}
}
postMessage({l:L,r:R},[L.buffer,R.buffer]);
}

// ---------- timeline ----------
const BT=60/140,BAR=4*BT,END=96*BAR+7,Q=new URLSearchParams(location.search);
const ss=(a,b,x)=>{x=Math.min(1,Math.max(0,(x-a)/(b-a)));return x*x*(3-2*x)};
const mix=(a,b,u)=>a+(b-a)*u,sin=Math.sin,cos=Math.cos;
function K(x,k){if(x<=k[0])return k[1];for(let i=2;i<k.length;i+=2)if(x<k[i])return mix(k[i-1],k[i+1],ss(k[i-2],k[i],x));return k[k.length-1]}
const kickOn=(b,q)=>((b>=8&&b<40)||(b>=48&&b<88))&&!(q==3&&(b==23||b==39||b==63||b==79));
const SPD=[0,3,7,5,9,15,23,15,25,8,39,8,40.5,5,47,5,48.5,28,63.5,28,65,20,80,16,96,8,101,3];
const ZT=[0];for(let i=1;i<=(END+2)*20;i++)ZT[i]=ZT[i-1]+K((i-.5)/20/BAR,SPD)/20;
const Z=t=>{t=Math.max(0,Math.min(END+1,t))*20;const i=t|0;return mix(ZT[i],ZT[i+1],t-i)};
const zB=b=>Z(b*BAR);
const HK=[0,1.2,7,1.4,9,2.4,24,2.6,39.3,3,40.3,-3.5,47.2,-4.6,48.2,3.2,50,2.6,63,2.6,66,5,80,9,96,26,100,34];
const PK=[0,.3,8,0,38,-.4,39.6,-5,40.6,2,45,5.5,47.4,7,48.4,0,96,0];
const SUNY=[63,3,72,6.5,80,9.5,96,31,100,40];
const WORDS=[[4,0,"TIDEMARK",14,1],
[8,0,"DROWNED",3],[14,0,"NO",2],[14,2,"SIGNAL",2],[15,0,"NO",2],[15,2,"SHORE",2],[16,0,"CITY",3],[22,0,"STILL",4],[23,0,"HERE",3],
[24,0,"CHROME",3],[30,0,"LIQUID",2],[30,2,"METAL",2],[31,0,"DREAMS",2],[31,2,"1999",2],[32,0,"MERGE",3],[38,0,"HOLD",2],[38,2,"YOUR",2],[39,0,"BREATH",3],
[40,0,"UNDER",4],[44,0,"SILENCE",6],[47,0,"UP",1],[47,1,"UP",1],[47,2,"UP",1],[47,3,"UP",1],
[48,0,"RISE",3],[52,0,"FASTER",3],[56,0,"HIGHER",3],[60,0,"BRIGHTER",3],[63,0,"HERE",1],[63,1,"COMES",1],[63,2,"THE",1],
[64,0,"SUN",4],[72,0,"LAND",3],[76,0,"ALIVE",3],[80,0,"GREETINGS",4],[94.5,0,"TIDEMARK",14,1]];
const GREET="GREETINGS FLY OUT TO   FUTURE CREW  *  ORANGE  *  THE BLACK LOTUS  *  FARBRAUSCH  *  HAUJOBB  *  PULSE  *  KEWLERS  *  MFX  *  ASD  *  AND YOU       CODE + MUSIC + DESIGN: FABLE 5.2       NOTHING LOADED - EVERYTHING COMPUTED       SEE YOU AT SUNRISE";
const FL=[8,16,24,40,48,56,64,80];

// ---------- gl ----------
const cv=document.getElementById("c"),gl=cv.getContext("webgl2",{antialias:false,alpha:false,powerPreference:"high-performance"});
const ui=document.getElementById("ui"),go=document.getElementById("go");
if(!gl){go.textContent="WEBGL2 NEEDED";throw new Error("no webgl2")}
function sh(t,s){const o=gl.createShader(t);gl.shaderSource(o,s);gl.compileShader(o);if(!gl.getShaderParameter(o,gl.COMPILE_STATUS))throw new Error(gl.getShaderInfoLog(o));return o}
const pr=gl.createProgram();
gl.attachShader(pr,sh(gl.VERTEX_SHADER,"#version 300 es\nvoid main(){gl_Position=vec4(vec2(gl_VertexID&1,gl_VertexID>>1)*4.-1.,0,1);}"));
gl.attachShader(pr,sh(gl.FRAGMENT_SHADER,document.getElementById("fs").textContent));
gl.linkProgram(pr);if(!gl.getProgramParameter(pr,gl.LINK_STATUS))throw new Error(gl.getProgramInfoLog(pr));
gl.useProgram(pr);
const U={};for(const n of["uR","uT","uCp","uCt","uSd","uCr","uB","uBB","uRg","uRu","uFx","uTx","uX"])U[n]=gl.getUniformLocation(pr,n);
const tc=document.createElement("canvas");tc.width=1024;tc.height=256;const tx=tc.getContext("2d");
const tex=gl.createTexture();gl.bindTexture(gl.TEXTURE_2D,tex);
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_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);
const FONT=p=>`italic 900 ${p}px "Arial Black","Helvetica Neue",Impact,system-ui,sans-serif`;
function up(){gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,tc);gl.generateMipmap(gl.TEXTURE_2D)}
function clr(){tx.fillStyle="#000";tx.fillRect(0,0,1024,256);tx.fillStyle="#fff";tx.textBaseline="middle"}
let curW=null;
function word(s){if(s===curW)return;curW=s;clr();if(s){tx.textAlign="center";let p=178;tx.font=FONT(p);const w=tx.measureText(s).width;if(w>930)tx.font=FONT(p*930/w);tx.fillText(s,512,136)}up()}
tx.font=FONT(84);const GO=[0];for(const c of GREET)GO.push(GO[GO.length-1]+tx.measureText(c).width+3);
function scroller(u,t){curW=0;clr();tx.textAlign="left";tx.font=FONT(84);const x0=1040-u*(GO[GREET.length]+1080);
  for(let k=0;k<GREET.length;k++){const x=x0+GO[k];if(x>-90&&x<1030)tx.fillText(GREET[k],x,128+40*sin(x*.011+t*4.5))}up()}
word("");

let scale=1;
function size(){const w=innerWidth,h=innerHeight,d=Math.min(devicePixelRatio||1,2),k=Math.min(1,1152/(Math.max(w,h)*d))*d*scale;
  const pw=Math.max(2,Math.round(w*k)),ph=Math.max(2,Math.round(h*k));if(cv.width!=pw||cv.height!=ph){cv.width=pw;cv.height=ph}}
addEventListener("resize",size);size();

const BL=new Float32Array(32);
function frame(t){
  const x=t/BAR,b=t/BT,bi=Math.floor(b),zc=Z(t);
  const ke=x<97&&kickOn(bi>>2,bi&3)?Math.exp(-(b-bi)*5):0;
  // blobs: six formations, cross-faded so they morph
  const w=[1-ss(7,9,x),ss(7,9,x)*(1-ss(23,25,x)),ss(23,25,x)*(1-ss(39,40.5,x)),ss(39,40.5,x)*(1-ss(47,48.5,x)),ss(47,48.5,x)*(1-ss(62.5,64.5,x)),ss(62.5,64.5,x)];
  const ex=1.25+.9*ke+.5*sin(t*.5),grow=ss(63,70,x),sy=K(x,SUNY);
  let mx=0,my=0,mz=0;
  for(let i=0;i<8;i++){
    const a=i*.7854,an=t*2.6+a;
    const P=[[.35*sin(t*1.3+a*3),K(x,[1,-2.5,6,1.6])+.3*cos(t*1.7+a*2),zc+7+.35*cos(t*1.1+a*5),.45],
      [3.2*sin(t*.9+a),.2+2.3*sin(t*1.75+i*.9),zc+8+3*sin(i*1.3+t*.4),.5],
      [ex*sin(t*1.1+i*2.1),2.6+ex*.8*sin(t*1.3+i*1.7),zc+9+ex*cos(t*.9+i*2.9),.72],
      [2.2*cos(a+t*1.2),-4.5+(i-3.5)*.8+.5*sin(t+i),zc+8+2.2*sin(a+t*1.2),.5],
      [3.2*cos(an),2.6+2.6*sin(an),zc+9+i*1.6,.5],
      [.6*sin(t*.7+a*3),sy+.6*cos(t*.9+a*2),zc+16+.6*cos(t*.8+a*5),.6+1.5*grow]];
    for(let j=0;j<4;j++){let v=0;for(let f=0;f<6;f++)v+=P[f][j]*w[f];BL[i*4+j]=v}
    mx+=BL[i*4]/8;my+=BL[i*4+1]/8;mz+=BL[i*4+2]/8;
  }
  let br=0;for(let i=0;i<8;i++)br=Math.max(br,Math.hypot(BL[i*4]-mx,BL[i*4+1]-my,BL[i*4+2]-mz)+BL[i*4+3]);
  // camera: one unbroken flight
  const A=K(x,[8,0,10,1.6,23,1.6,25,0,48.5,0,50,1.1,62,1.1,64,0]);
  let px=A*sin(zc*.045),py=K(x,HK)+.15*sin(t*.8),pz=zc,tX=px*.4,tY=py+K(x,PK),tZ=zc+10;
  const ow=ss(25.5,27.5,x)*(1-ss(36.5,38.5,x)),oa=6.2832*ss(26,38,x),oR=9-2.5*sin(oa/2)**2;
  px=mix(px,oR*sin(oa),ow);py=mix(py,3.2+1.6*sin(oa/2)**2,ow);pz=mix(pz,zc+9-oR*cos(oa),ow);
  tX=mix(tX,0,ow);tY=mix(tY,2.6,ow);tZ=mix(tZ,zc+9,ow);
  const sw=ss(64,69,x)*.85;tX=mix(tX,0,sw);tY=mix(tY,sy,sw);tZ=mix(tZ,zc+16,sw);
  const roll=-A*.13*cos(zc*.045)+.05*sin(t*.6)+6.2832*ss(56,58,x)+(py<0?.1*sin(t*.9):0);
  const foc=K(x,[47.5,1.25,49,.85,63,.85,65,1.25])-.05*ke;
  const fog=K(x,[0,.02,8,.014,40,.014,48,.01,64,.006,96,.004]);
  const el=K(x,[0,-.24,24,-.17,40,-.12,48,-.08,60,-.03,68,.06,80,.2,96,.4]),az=.3,ce=Math.sqrt(1-el*el);
  let fl=0;for(const f of FL)if(x>=f)fl+=.85*Math.exp(-(x-f)*BAR*3.2);
  fl+=ss(94.3,96.3,x)*1.2;
  const fade=(1-ss(97.2,99.4,x))*Math.min(1,t*.6+.25);
  const dist=Math.exp(-Math.abs(x-40)*5)+Math.exp(-Math.abs(x-48.1)*5)+(py<0?.25:0);
  // text
  let ta=0,tz=1,ty=0,tg=.5;
  if(x>=81&&x<94.4){scroller((x-81)/13.4,t);ta=Math.min(1,(x-81)*2)*(1-ss(94,94.4,x));ty=-.52;tz=1;tg=.6}
  else{let cw=null;for(const W of WORDS){const s=W[0]*4+W[1];if(b>=s&&b<s+W[3])cw=W}
    if(cw){const u=b-(cw[0]*4+cw[1]);word(cw[2]);
      if(cw[4]){ta=ss(0,3,u)*(1-ss(cw[3]-3,cw[3],u));tz=.82+.02*u;tg=.9}
      else{ta=Math.min(1,u*10)*(1-ss(cw[3]-.6,cw[3],u));tz=.95+.03*u+.5*Math.exp(-u*7);tg=.5+Math.exp(-u*4)}}}
  gl.viewport(0,0,cv.width,cv.height);
  gl.uniform2f(U.uR,cv.width,cv.height);gl.uniform1f(U.uT,t);
  gl.uniform3f(U.uCp,px,py,pz);gl.uniform3f(U.uCt,tX,tY,tZ);gl.uniform3f(U.uSd,sin(az)*ce,el,cos(az)*ce);
  gl.uniform4f(U.uCr,roll,foc,fog,fade);gl.uniform4fv(U.uB,BL);gl.uniform4f(U.uBB,mx,my,mz,br+1.1);
  gl.uniform4f(U.uRg,zB(50),27,ke,0);gl.uniform4f(U.uRu,zB(3),zB(63),ss(62,70,x),zB(62));
  gl.uniform4f(U.uFx,fl,ke,dist,ke*.5+fl+dist);gl.uniform4f(U.uTx,ta,tz,ty,tg);gl.uniform1i(U.uX,0);
  gl.drawArrays(gl.TRIANGLES,0,3);
}

// ---------- run ----------
let ac=null,t0=0,playing=false,buf=null,clock=null;
const ST=window.DEMO={frames:0,slow:0,scale:1,t:0,done:false,ready:false,fps:0,minFps:999,log:[]};
let lastN=0,accT=0,accN=0,secT=0,secN=0;
function loop(now){
  requestAnimationFrame(loop);
  let t;
  if(Q.has("shot"))t=ST.shot!=null?ST.shot:+Q.get("shot");
  else if(playing){t=clock();if(t>=END){playing=false;ST.done=true;if(ac)ac.suspend();go.textContent="REPLAY";ui.classList.remove("off")}}
  else t=ST.done?END:0;
  ST.t=t;
  frame(playing||Q.has("shot")?t:(ST.done?END:.4+(now*.00025)%(BAR*3)));
  if(playing){
    const dt=now-lastN;ST.frames++;if(dt>50)ST.slow++;
    accT+=dt;accN++;secT+=dt;secN++;
    if(secT>=1000){const f=secN*1000/secT;ST.fps=f;if(ST.frames>90)ST.minFps=Math.min(ST.minFps,f);ST.log.push([t|0,Math.round(f),cv.width,cv.height]);secT=secN=0}
    if(accN>=45){const avg=accT/accN;if(avg>21&&scale>.4){scale=Math.max(.4,scale*.82);ST.scale=scale;size()}accT=accN=0}
  }
  lastN=now;
}
requestAnimationFrame(loop);
function start(){
  const off=+(Q.get("t")||0);
  if(buf){
    ac=ac||new(window.AudioContext||window.webkitAudioContext)();ac.resume();
    const ab=ac.createBuffer(2,buf.l.length,44100);ab.getChannelData(0).set(buf.l);ab.getChannelData(1).set(buf.r);
    const s=ac.createBufferSource();s.buffer=ab;s.connect(ac.destination);t0=ac.currentTime+.08-off;s.start(t0+off,off);
    const p0=performance.now();
    // audio clock drives everything; wall clock only if the context never runs
    clock=()=>ac.state=="running"||ac.currentTime>t0+off?ac.currentTime-t0:(performance.now()-p0)/1000+off-.08;
  }else{const p0=performance.now();clock=()=>(performance.now()-p0)/1000+off}
  ST.done=false;ST.frames=ST.slow=0;playing=true;ui.classList.add("off");
}
go.onclick=start;
addEventListener("keydown",e=>{if(e.key=="f"||e.key=="F"){document.fullscreenElement?document.exitFullscreen():document.documentElement.requestFullscreen&&document.documentElement.requestFullscreen()}});
if(Q.has("shot"))ui.style.display="none";
else if(Q.has("mute")){ST.ready=true;go.disabled=false;go.textContent="START DEMO";if(Q.has("auto"))start()}
else{
  const wk=new Worker(URL.createObjectURL(new Blob(["("+SYN+")()"],{type:"text/javascript"})));
  wk.onmessage=e=>{if(e.data.p!=null)go.textContent="PRECALC "+Math.round(e.data.p*100)+"%";else{buf=e.data;ST.ready=true;go.disabled=false;go.textContent="START DEMO";wk.terminate()}};
  wk.onerror=e=>{go.disabled=false;go.textContent="START (NO SOUND)";ST.ready=true;console.error("synth",e.message)};
}
</script>
</body>
</html>