27:18Gemini 4 Hydrogen: Cheaper Tokens, More Expensive Answers?
Nov 13, 2026 · YouTube
18:40ERNIE 5.5 Flash Preview vs GLM 5.3 Flash: Baidu's new model is very impressive!
Nov 11, 2026 · YouTube
36:22Fable 5.2 robbing me blind, plus: Nex AGI's N2.6 vs GLM 5.3 Flash
Nov 11, 2026 · YouTube
14:03Gemini 4 Carbon: Can it handle my challenges? (First look with one-shot coding tests)
Nov 10, 2026 · YouTube
47:51Ranking major U.S. Open-Weights Models. (Where does Ripple 65B fit in?)
Nov 6, 2026 · YouTube
10:57"Crouton Alpha": the latest super-efficient coding model by Meta?
Nov 3, 2026 · YouTube
38:58Testing Particle-1: a post-trained DeepSeek V4.1 Flash specializing in coding and efficiency
Oct 28, 2026 · YouTube
22:14GPT-6.1 Terra Pro vs Terra: How Much Better Is Pro?
Oct 27, 2026 · YouTube
29:55Kimi K3.1 Pro Looked Incredible. Then I Kept Testing.
Oct 26, 2026 · YouTube
31:28Is this company reselling GPT Sol 6.1 at a loss (for non-agentic/text-only)?🤔 Is this sustainable?
Oct 20, 2026 · YouTube
17:58Nemotron 3.5 Super VL vs Opus 5.5: Testing NVIDIA’s new coding juggernaut
Oct 15, 2026 · YouTube
16:04Testing a *massive* new open-weights model 🤯: Muse Spark Open (vs. Nemotron, Opus 5.5, and more)
Oct 15, 2026 · YouTube
16:43Gemini 4 Argon: Great 🐈 Animation, but a broken escalator. (Coding challenge vs Opus 5.5, Sol 6.1)
Oct 13, 2026 · YouTube
16:37Meta's new "Muse Wonder" AI is impressive... but can it compete with Anthropic's models?
Oct 12, 2026 · YouTube
11:12WOW... Haiku 5.5 is very impressive. RIP OpenAI?
Oct 7, 2026 · YouTube
22:47Mistral Large 4 Is Cheap. But Is It Good Enough to Compete?
Oct 6, 2026 · YouTube
22:02Is Pareto 26.10 Preview Just Routing to GPT Sol? I Tested It Against Sol 6.1 Pro
Oct 1, 2026 · YouTube
11:22I Tested Sonnet 5.5 Against Opus 5.5. Is It Actually Cheaper?
Sep 28, 2026 · YouTube
30:00Did Elon Musk's team "train the gay away"... and make Grok 4.7 worse?
Sep 26, 2026 · YouTube
19:21I Tested the Stealth "Space Bunny Alpha" Model Before It Went Offline
Sep 23, 2026 · YouTube
10:58So... how much better is Claude Opus 5.5 vs. GPT-6 Sol, really?
Sep 23, 2026 · YouTube
9:38Claude Opus 5.5... the BEST new model for coding!! 🤩
Sep 22, 2026 · YouTube
29:00Grok 4.7: improved, but GLM 5.3 FlashX beat it anyway (benchmark against Gemini, Sol, etc.)
Sep 22, 2026 · YouTube
You make short animated pieces for the web.
Build me a single HTML page: a bird, breakdancing.
data: URIs are fine.localStorage, sessionStorage, cookies or indexedDB — sandboxed frame, they throw.html block.Two sentences: what the bird is, and the look you went for.
The single fenced html block.
Three bullets.
A fat city pigeon on a square of flattened cardboard: iridescent neck, tracksuit stripes where the wing bars should be, feathered "pants," and the head-bob that pigeons already do on the beat. It is drawn as a three-drum risograph print (blue, fluoro pink, yellow) with knockouts, grain, and plates that slip out of register harder the faster he moves, so spins smear like cheap zine ink.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Pigeon Breaks vol.1</title>
<style>
html,body{margin:0;height:100%;background:#f3ebda;overflow:hidden;font-family:"Arial Black","Helvetica Neue",Arial,sans-serif;color:#1d5bb8}
canvas{position:fixed;inset:0;width:100%;height:100%;display:block;cursor:crosshair}
#ui{position:fixed;left:0;right:0;bottom:0;display:flex;flex-wrap:wrap;gap:8px 18px;align-items:center;justify-content:center;padding:10px 12px 12px;background:rgba(243,235,218,.9);border-top:2px dashed #1d5bb8}
.grp{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.lbl{font-size:11px;letter-spacing:.14em;margin-right:2px}
button{font:inherit;font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:#1d5bb8;background:#f8f2e4;border:2px solid #1d5bb8;padding:6px 9px 5px;cursor:pointer;box-shadow:3px 2px 0 #ff4f9e;border-radius:2px}
button:active{transform:translate(2px,2px);box-shadow:1px 0 0 #ff4f9e}
button.active{background:#1d5bb8;color:#f8f2e4}
button.queued{animation:blink .5s steps(2) infinite}
@keyframes blink{50%{background:#ff4f9e;color:#fff;border-color:#ff4f9e}}
kbd{font-family:ui-monospace,Menlo,monospace;font-size:10px;margin-left:5px;opacity:.6}
label{font-size:11px;letter-spacing:.1em;display:flex;align-items:center;gap:6px}
input[type=range]{accent-color:#ff4f9e;width:104px}
output{font-family:ui-monospace,Menlo,monospace;font-size:11px;min-width:22px}
#hype{width:84px;height:12px;border:2px solid #1d5bb8;position:relative;background:#f8f2e4}
#hype i{position:absolute;left:0;top:0;bottom:0;width:0;background:#ff4f9e}
</style>
</head>
<body>
<canvas id="c"></canvas>
<div id="ui">
<div class="grp"><span class="lbl">CALL IT</span>
<button data-m="toprock">Toprock<kbd>1</kbd></button>
<button data-m="footwork">Six-step<kbd>2</kbd></button>
<button data-m="windmill">Windmill<kbd>3</kbd></button>
<button data-m="headspin">Headspin<kbd>4</kbd></button>
<button data-m="freeze">Freeze<kbd>5</kbd></button>
<button id="auto" class="active">Let him cook<kbd>A</kbd></button>
</div>
<div class="grp">
<label>TEMPO <input id="bpm" type="range" min="84" max="126" value="98"><output id="bpmo">98</output></label>
<label>CRUMBS EATEN <input id="crumbs" type="range" min="0" max="10" value="3"><output id="cro">3</output></label>
</div>
<div class="grp">
<button id="clap">Clap<kbd>space</kbd></button>
<span class="lbl">HYPE</span><div id="hype"><i></i></div>
<button id="snd">Sound: off<kbd>M</kbd></button>
</div>
</div>
<script>
(()=>{
const TAU=Math.PI*2, PI=Math.PI;
const cv=document.getElementById('c'), ctx=cv.getContext('2d');
const INK=['#1d5bb8','#ff4f9e','#ffd21f'];
const plates=INK.map(col=>{const c=document.createElement('canvas');return {c,x:c.getContext('2d'),col,pat:null,ox:0,oy:0};});
const BASEOFF=[[0,0],[1.7,-1.1],[-1.3,1.5]];
let W,H,DPR,SC,GY,paper;
// ---------- textures ----------
const grain=(()=>{const g=document.createElement('canvas');g.width=g.height=256;const x=g.getContext('2d');const id=x.createImageData(256,256);
for(let i=0;i<id.data.length;i+=4){const r=Math.random();id.data[i+3]=r<0.05?180+Math.random()*75:r<0.4?Math.random()*60:0;}
x.putImageData(id,0,0);return g;})();
function makePaper(){
paper=document.createElement('canvas');paper.width=cv.width;paper.height=cv.height;
const x=paper.getContext('2d');x.fillStyle='#f3ebda';x.fillRect(0,0,paper.width,paper.height);
for(let i=0;i<1600;i++){x.strokeStyle=`rgba(110,80,40,${Math.random()*0.07})`;x.lineWidth=Math.random()*1.2;
const px=Math.random()*paper.width,py=Math.random()*paper.height,a=Math.random()*TAU,l=4+Math.random()*16;
x.beginPath();x.moveTo(px,py);x.lineTo(px+Math.cos(a)*l,py+Math.sin(a)*l);x.stroke();}
for(let i=0;i<900;i++){x.fillStyle=`rgba(60,40,20,${Math.random()*0.12})`;x.fillRect(Math.random()*paper.width,Math.random()*paper.height,1,1);}
}
function resize(){
DPR=Math.min(window.devicePixelRatio||1,1.5);W=innerWidth;H=innerHeight;
cv.width=W*DPR|0;cv.height=H*DPR|0;
plates.forEach(p=>{p.c.width=cv.width;p.c.height=cv.height;p.pat=p.x.createPattern(grain,'repeat');});
const uiH=document.getElementById('ui').offsetHeight, avail=Math.max(200,H-uiH);
SC=Math.min(W/760,avail/560);GY=avail*0.74;makePaper();
}
addEventListener('resize',resize);
// ---------- static paths ----------
const DOTS=new Path2D();
for(let gy=-240,row=0;gy<=240;gy+=10,row++)for(let gx=-240+(row%2)*5;gx<=240;gx+=10){
const d=Math.hypot(gx,gy)/235;if(d>1)continue;const r=4.8*Math.pow(1-d,0.75);if(r<0.45)continue;
DOTS.moveTo(gx+r,gy);DOTS.arc(gx,gy,r,0,TAU);}
const BOARD=new Path2D();BOARD.moveTo(-168,24);BOARD.lineTo(162,27);BOARD.lineTo(128,-14);BOARD.lineTo(-132,-16);BOARD.closePath();
const CREASE=new Path2D();CREASE.moveTo(-8,-15);CREASE.lineTo(-14,25);
const TAPE1=new Path2D();TAPE1.moveTo(-160,14);TAPE1.lineTo(-142,26);TAPE1.lineTo(-134,20);TAPE1.lineTo(-152,8);TAPE1.closePath();
const TAPE2=new Path2D();TAPE2.moveTo(112,-16);TAPE2.lineTo(134,-4);TAPE2.lineTo(140,-10);TAPE2.lineTo(120,-21);TAPE2.closePath();
// ---------- state ----------
let bpm=98,crumbs=3,hype=0,auto=true,facing=1,sndOn=false;
let beat=0,move=null,keys=[],keyIdx=0,moveStart=0,level='top',request=null,pendingDown=null,stayMove='toprock',groundCount=0;
let label='TOPROCK',prevLabel='',labelPop=0,freezeOn=false,shakeT=0,lookT=0,time=0,boilT=0,lastDust=-1;
let rootXL=0,rootYL=-60,contactX=0,prevRX=0,prevRY=0,prevVy=0,velX=0,velY=0;
const spin={on:false,target:0,acc:3};
const lift={y:0,v:0};
const fx=[];
const mass=()=>0.75+crumbs*0.065;
const faceYaw=v=>facing>0?v:PI-v;
const STAND={x:0,rot:0.04,neck:0.15,bob:0,wna:-0.35,wne:0.55,wns:0.2,wfa:-0.35,wfe:0.55,wfs:0.2,lna:0.35,lnk:-0.6,lns:0.08,lfa:0.25,lfk:-0.5,lfs:0.08,tail:-0.9,fan:0.45,sq:1,anc:0,wob:0};
const S={};for(const k in STAND)S[k]={v:STAND[k],vel:0,t:STAND[k],k:200,z:0.6};
S.yaw={v:0.45,vel:0,t:0.45,k:160,z:0.6};S.hy={v:0,vel:0,t:0,k:260,z:0.7};
const FW={x:0,rot:0.35,neck:0.25,bob:2,sq:0.96,anc:0,wob:0,tail:-0.6,fan:0.6,wna:0.35,wne:0.1,wns:0.35,wfa:1.1,wfe:1.1,wfs:0.3,lna:1.5,lnk:-0.4,lns:0.3,lfa:0.9,lfk:-2.2,lfs:0.2};
const NP={wna:0.35,wne:0.1,wns:0.35,wfa:1.2,wfe:1.2,wfs:0.3},FP={wna:1.2,wne:1.2,wns:0.3,wfa:0.35,wfe:0.1,wfs:0.35};
const SIX=[
{...NP,lna:1.5,lnk:-0.3,lns:0.35,lfa:0.9,lfk:-2.2,lfs:0.15,rot:0.35},
{...NP,lna:1.0,lnk:-1.9,lns:-0.35,lfa:1.4,lfk:-0.4,lfs:0.45,rot:0.3},
{...FP,lna:0.2,lnk:-2.5,lns:0.1,lfa:1.6,lfk:-0.3,lfs:0.3,rot:0.15},
{...FP,lna:1.5,lnk:-0.3,lns:0.5,lfa:0.3,lfk:-2.4,lfs:0.1,rot:0.25},
{...NP,lna:0.7,lnk:-2.0,lns:0.1,lfa:1.5,lfk:-0.2,lfs:-0.35,rot:0.35},
{...NP,lna:1.6,lnk:-0.6,lns:0.3,lfa:1.6,lfk:-0.6,lfs:0.3,rot:0.45}];
const FWEND={...FW,...SIX[5]};
const NAMES={toprock:'TOPROCK',footwork:'SIX-STEP',windmill:'WINDMILLS',headspin:'HEADSPIN',freeze:'FREEZE'};
// ---------- moves ----------
const MOVES={
toprock:{len:8,cuts:[2,4,6],lvl:'top',label(){return 'TOPROCK'},keys(){
const ks=[],A=1+0.6*hype;
for(let i=0;i<8;i++){const s=i%2?1:-1;
if(i===3||i===7){
ks.push({at:i,k:420,z:0.4,p:{x:s*8,rot:-0.14,yaw:faceYaw(0.35),neck:-0.25,bob:-4,sq:1.02,wna:1.6,wne:2.9,wns:0.4,wfa:1.8,wfe:2.8,wfs:0.4,lna:0.2,lnk:-0.35,lfa:-0.15,lfk:-0.3,lns:0.2,lfs:0.2,tail:-0.7,fan:0.9,anc:0,wob:0}});
ks.push({at:i+0.5,k:160,z:0.6,p:{sq:1,bob:3,neck:0.15}});continue;}
ks.push({at:i,k:330,z:0.42,p:{x:s*16*A,rot:0.13,yaw:faceYaw(0.45+s*0.32),neck:0.35,bob:8,sq:0.93,
lna:s>0?1.15:0.5,lnk:s>0?-0.35:-1.05,lfa:s>0?0.5:1.15,lfk:s>0?-1.05:-0.35,lns:0.1,lfs:0.1,
wna:s>0?-0.8:1.0,wne:s>0?0.9:1.5,wfa:s>0?1.0:-0.8,wfe:s>0?1.5:0.9,wns:0.3,wfs:0.3,tail:-0.75,fan:0.5,anc:0,wob:0}});
ks.push({at:i+0.5,k:150,z:0.6,jump:150,p:{rot:-0.02,neck:0.02,bob:-6,sq:1.04,lna:0.3,lnk:-0.45,lfa:0.28,lfk:-0.45,wna:0.3,wne:1.3,wfa:0.3,wfe:1.3}});
}return ks;}},
drop:{len:2,lvl:'ground',label(){return 'GO DOWN'},keys(){return [
{at:0,k:220,z:0.5,jump:300,p:{...STAND,yaw:faceYaw(0.45),sq:1.08,rot:-0.12,wna:2.6,wne:0.2,wfa:2.4,wfe:0.3,wns:0.5,wfs:0.5,lna:0.1,lnk:-0.1,lfa:0,lfk:-0.1,bob:-4,neck:-0.1}},
{at:0.55,k:460,z:0.34,p:{rot:0.55,sq:0.88,wna:0.5,wne:0.2,wns:0.35,wfa:0.7,wfe:0.3,wfs:0.35,lna:1.6,lnk:-2.4,lfa:1.4,lfk:-2.3,bob:5,neck:0.5,tail:-0.4,fan:0.9}},
{at:1.25,k:200,z:0.6,p:{...FW}}];}},
footwork:{len:6,cuts:[3],label(){return 'SIX-STEP'},keys(){const ks=[];
for(let c=0;c<2;c++)for(let i=0;i<6;i++)ks.push({at:c*3+i*0.5,k:340,z:0.5,dyaw:-TAU/6,p:{...FW,...SIX[i],neck:0.2+0.12*(i%2),bob:i%2?-3:4,sq:i%2?1:0.95}});
return ks;}},
windmill:{len:8,label(){return 'WINDMILLS'},keys(){
const WM={x:0,anc:0,neck:0.1,bob:0,tail:0.1,fan:1.1,sq:1,wna:1.6,wne:2.5,wns:0.25,wfa:1.6,wfe:2.5,wfs:0.25,lna:0.15,lnk:-0.05,lns:0.65,lfa:0.15,lfk:-0.05,lfs:0.65};
return [
{at:0,k:200,z:0.5,spin:5,acc:2.5,p:{rot:1.1,lna:1.3,lnk:-0.3,lns:0.6,lfa:1.0,lfk:-0.4,lfs:0.6,wna:0.4,wne:0.2,wns:0.3,sq:0.95}},
{at:0.75,k:140,z:0.55,spin:11,acc:3,p:{...WM,rot:1.62,wob:1}},
{at:6,k:160,z:0.6,spin:0,p:{rot:1.1,wob:0,lnk:-1.6,lfk:-1.8,lns:0.3,lfs:0.3}},
{at:7.1,k:220,z:0.55,p:{...FWEND}}];}},
headspin:{len:8,label(){return 'HEADSPIN'},keys(){return [
{at:0,k:200,z:0.55,spin:0,p:{yaw:faceYaw(0.5),rot:2.45,anc:1,neck:0.05,bob:0,sq:1,wna:2.5,wne:0.6,wns:0.6,wfa:2.5,wfe:0.6,wfs:0.6,lna:1.7,lnk:-2.4,lns:0.2,lfa:1.6,lfk:-2.3,lfs:0.2,tail:-0.2,fan:0.8,wob:0}},
{at:1,k:110,z:0.45,spin:6,acc:3,p:{rot:PI,lna:0.05,lnk:-0.1,lns:0.4,lfa:-0.05,lfk:-0.1,lfs:0.4,wns:1.2,wna:2.2,wne:0.4,wfs:1.2,wfa:2.2,wfe:0.4,fan:1.2,tail:0}},
{at:3,k:260,z:0.5,spin:14,acc:2.2,label:'DRILL',p:{wna:0.8,wne:2.5,wns:0.15,wfa:0.8,wfe:2.5,wfs:0.15,lns:0.06,lfs:0.06,fan:0.3}},
{at:5.5,k:200,z:0.5,spin:4,acc:3,label:'HEADSPIN',p:{lns:0.7,lfs:0.7,wns:1.0,wna:2.4,wne:0.4,wfs:1.0,wfa:2.4,wfe:0.4}},
{at:6.5,k:150,z:0.55,spin:0,p:{rot:2.4,lna:1.6,lnk:-2.3,lfa:1.5,lfk:-2.2,wna:2.5,wne:0.6,wfa:2.5,wfe:0.6}},
{at:7.25,k:180,z:0.6,p:{...FWEND,anc:0}}];}},
freeze:{len:4,v:0,label(){return this.v?'SPLIT FREEZE':'AIR CHAIR'},keys(){
this.v=Math.random()<0.5?0:1;
const coil={at:0,k:260,z:0.55,p:{...FW,rot:0.8,sq:0.9,lna:1.8,lnk:-2.5,lfa:1.7,lfk:-2.5,wna:0.6,wne:0.3,wfa:0.6,wfe:0.3,bob:-3}};
const A={rot:2.15,neck:-0.35,bob:-2,sq:1,yaw:faceYaw(0.3),wna:2.95,wne:0.05,wns:0.25,wfa:-0.5,wfe:0.9,wfs:1.0,lna:1.35,lnk:-1.9,lns:0.35,lfa:0.5,lfk:-1.5,lfs:0.55,tail:0.4,fan:1.3};
const B={rot:2.9,neck:-0.2,bob:0,sq:1,yaw:faceYaw(0.25),wna:2.7,wne:0.2,wns:0.45,wfa:2.7,wfe:0.2,wfs:0.45,lna:1.25,lnk:-0.05,lns:0.25,lfa:-1.1,lfk:-0.1,lfs:0.25,tail:0.8,fan:1.3};
return [coil,{at:0.75,k:820,z:0.36,freeze:true,p:this.v?B:A},{at:3.2,k:220,z:0.6,freeze:false,p:{...FWEND}}];}},
getup:{len:2,lvl:'top',label(){return 'GET UP'},keys(){
facing=Math.random()<0.5?1:-1;return [
{at:0,k:260,z:0.55,spin:0,p:{...FW,yaw:faceYaw(0.45),rot:0.25,lna:1.9,lnk:-2.6,lfa:1.7,lfk:-2.5,lns:0.15,lfs:0.15,wna:0.2,wne:0.05,wfa:0.2,wfe:0.05,sq:0.9,neck:0.4}},
{at:0.6,k:230,z:0.5,jump:420,p:{...STAND,yaw:faceYaw(0.45),sq:1.1,wna:2.8,wne:0.2,wfa:2.7,wfe:0.25,wns:0.6,wfs:0.6,lna:0.05,lnk:-0.05,lfa:-0.05,lfk:-0.1,bob:-5}},
{at:1.35,k:300,z:0.42,fx:'shake',p:{...STAND,yaw:faceYaw(0.45)}}];}}
};
const GROUND=new Set(['footwork','windmill','headspin','freeze']);
function startMove(name,at){move=MOVES[name];move.name=name;moveStart=at;keys=move.keys();keyIdx=0;label=move.label();if(move.lvl)level=move.lvl;updateButtons();}
function groundPick(cur){
const w={footwork:1.3,windmill:0.9+hype*1.2,headspin:0.5+hype*1.8,freeze:0.35};if(cur&&w[cur])w[cur]*=0.25;
let tot=0;for(const k in w)tot+=w[k];let r=Math.random()*tot;for(const k in w){r-=w[k];if(r<=0)return k;}return 'footwork';}
function autoPick(cur){
if(level==='top')return groundPick(null);
groundCount++;if(cur==='freeze')return 'toprock';if(groundCount>=4)return 'freeze';return groundPick(cur);}
function plan(){
const cur=move.name;
if(cur==='drop'){const n=pendingDown||'footwork';pendingDown=null;return n;}
if(cur==='getup')return 'toprock';
let want=request;request=null;
if(!want)want=auto?autoPick(cur):stayMove;
if(level==='top'&&GROUND.has(want)){pendingDown=want;groundCount=0;return 'drop';}
if(level==='ground'&&want==='toprock')return 'getup';
return want;}
function applyKey(k){
const kk=k.k||200,zz=k.z||0.55;
for(const p in k.p){const s=S[p];if(!s)continue;s.t=k.p[p];s.k=kk;s.z=zz;}
if(k.dyaw){S.yaw.t+=k.dyaw*facing;S.yaw.k=kk;S.yaw.z=zz;}
if(k.spin!==undefined){spin.on=k.spin>0;spin.target=k.spin;spin.acc=k.acc||3;}
if(k.jump&&lift.y<1)lift.v=k.jump*(1+0.35*hype)/Math.sqrt(mass());
if(k.freeze!==undefined)freezeOn=k.freeze;
if(k.fx==='shake'){shakeT=0.5;feathers(6);}
if(k.label)label=k.label;}
// ---------- particles ----------
function dust(p){if(time-lastDust<0.14)return;lastDust=time;const n=4+Math.min(8,p/70)|0;
for(let i=0;i<n;i++)fx.push({t:'d',x:contactX+(Math.random()-0.5)*34,y:-2,vx:(Math.random()-0.5)*190,vy:-Math.random()*50-10,r:3+Math.random()*4,life:1,dec:1.5+Math.random()});}
function feathers(n){for(let i=0;i<n;i++)fx.push({t:'f',x:rootXL+(Math.random()-0.5)*40,y:rootYL+(Math.random()-0.5)*30,vx:(Math.random()-0.5)*140,vy:-Math.random()*130-40,a:Math.random()*TAU,va:(Math.random()-0.5)*8,life:1,dec:0.33});}
// ---------- simulation ----------
function step(h){
time+=h;beat+=h*bpm/60;
const lead=0.09*bpm/60;
for(let g=0;g<4;g++){
while(keyIdx<keys.length&&beat+lead>=moveStart+keys[keyIdx].at)applyKey(keys[keyIdx++]);
let end=moveStart+move.len;
if(request&&move.cuts){for(const c of move.cuts){const b=moveStart+c;if(b>=beat+lead-1e-3&&b<end){end=b;break;}}}
if(beat+lead>=end)startMove(plan(),end);else break;
}
const m=mass(),zm=1.25-0.3*m;
for(const key in S){if(key==='yaw')continue;const s=S[key];const w=Math.sqrt(s.k/m);
s.vel+=(w*w*(s.t-s.v)-2*s.z*zm*w*s.vel)*h;s.v+=s.vel*h;}
const y=S.yaw;
if(spin.on){const tg=spin.target*facing*(1+0.6*hype)/Math.sqrt(m);y.vel+=(tg-y.vel)*Math.min(1,spin.acc*h/m);}
else if(Math.abs(y.vel)>3){y.vel*=Math.exp(-2.5*h*m);}
else{const tg=y.t+TAU*Math.round((y.v-y.t)/TAU);const w=Math.sqrt(y.k/m);y.vel+=(w*w*(tg-y.v)-2*y.z*zm*w*y.vel)*h;}
y.v+=y.vel*h;
if(lift.y>0||lift.v>0){lift.v-=1700*h;lift.y+=lift.v*h;
if(lift.y<=0){const imp=-lift.v;lift.y=0;lift.v=0;if(imp>120){S.sq.vel-=imp*0.007*m;dust(imp);}}}
if(lookT>0){lookT-=h;if(lookT<=0)S.hy.t=0;}
if(shakeT>0)shakeT=Math.max(0,shakeT-h);
hype=Math.max(0,hype-h*0.05);
if(Math.abs(y.vel)>9&&Math.random()<h*1.5*(0.3+hype))feathers(1);
for(let i=fx.length-1;i>=0;i--){const p=fx[i];p.life-=p.dec*h;if(p.life<=0){fx.splice(i,1);continue;}
if(p.t==='d'){p.x+=p.vx*h;p.y+=p.vy*h;p.vx*=Math.exp(-4*h);p.vy*=Math.exp(-3*h);}
else if(p.t==='f'){p.vy+=140*h;p.vx*=Math.exp(-2*h);p.vy*=Math.exp(-2.2*h);p.x+=(p.vx+Math.sin(time*3+p.a)*30)*h;p.y+=p.vy*h;p.a+=p.va*h;if(p.y>0){p.y=0;p.vx=0;p.va=0;}}}
}
// ---------- display list ----------
const DL=[];
const F=(path,ink,ko=true)=>DL.push({path,ink,ko});
const Ln=(path,lw,ink,ko=true)=>DL.push({path,lw,ink,ko});
function line(pts){const p=new Path2D();p.moveTo(pts[0][0],pts[0][1]);for(let i=1;i<pts.length;i++)p.lineTo(pts[i][0],pts[i][1]);return p;}
function circ(x,y,r){const p=new Path2D();p.arc(x,y,Math.max(0.1,r),0,TAU);return p;}
function ell(x,y,rx,ry,r){const p=new Path2D();p.ellipse(x,y,Math.max(0.1,rx),Math.max(0.1,ry),r||0,0,TAU);return p;}
function taper(pts,ws){const n=pts.length,L=[],R=[];
for(let i=0;i<n;i++){const a=pts[Math.max(0,i-1)],b=pts[Math.min(n-1,i+1)];const dx=b[0]-a[0],dy=b[1]-a[1];const l=Math.hypot(dx,dy)||1;
const nx=-dy/l*ws[i]/2,ny=dx/l*ws[i]/2;L.push([pts[i][0]+nx,pts[i][1]+ny]);R.push([pts[i][0]-nx,pts[i][1]-ny]);}
const p=new Path2D();p.moveTo(L[0][0],L[0][1]);for(let i=1;i<n;i++)p.lineTo(L[i][0],L[i][1]);for(let i=n-1;i>=0;i--)p.lineTo(R[i][0],R[i][1]);p.closePath();return p;}
function star(x,y,r1,r2,n){const p=new Path2D();for(let i=0;i<n*2;i++){const r=i%2?r2:r1,a=i/(n*2)*TAU-PI/2;if(i)p.lineTo(x+Math.cos(a)*r,y+Math.sin(a)*r);else p.moveTo(x+Math.cos(a)*r,y+Math.sin(a)*r);}p.closePath();return p;}
// ---------- the bird ----------
function buildBird(dt){
const v=k=>S[k].v,m=mass(),bw=0.84+0.055*crumbs;
let rot=v('rot')+v('wob')*0.28*Math.sin(S.yaw.v),trem=0;
if(freezeOn){const a=0.008+0.022*(m-0.75);trem=a*(Math.sin(time*37)+0.7*Math.sin(time*23.3));rot+=trem;}
const sk=shakeT/0.5;if(shakeT>0)rot+=Math.sin(time*62)*0.07*sk;
const yaw=S.yaw.v+(shakeT>0?Math.sin(time*48)*0.25*sk:0);
const cy=Math.cos(yaw),sy=Math.sin(yaw),cr=Math.cos(rot),sr=Math.sin(rot);
const sq=v('sq'),sqx=1/Math.sqrt(Math.max(0.5,sq));
const pr=q=>{const xr=q[0]*cy+q[2]*sy,d=-q[0]*sy+q[2]*cy;return [xr*cr-q[1]*sr,xr*sr+q[1]*cr,d];};
const ad=(a,b,s)=>[a[0]+b[0]*s,a[1]+b[1]*s,a[2]+b[2]*s];
const dv=(a,s,side)=>[Math.sin(a)*Math.cos(s),Math.cos(a)*Math.cos(s),Math.sin(s)*side];
const legs=[],wings=[];
for(const side of [1,-1]){
const P=side>0?'ln':'lf';
const a=v(P+'a')+(freezeOn?trem*3*side:0),k=v(P+'k'),s=v(P+'s');
const hip=[2*sqx,20*sq,9*side*bw],knee=ad(hip,dv(a,s,side),19),ank=ad(knee,dv(a+k,s,side),19);
const toes=[-0.5,0,0.5].map(t=>ad(ank,dv(a+k+1.5,s*0.5+t,side),10));
const back=ad(ank,dv(a+k-1.3,s*0.5,side),6),thigh=ad(hip,dv(a,s,side),9);
legs.push({side,p:[hip,thigh,knee,ank,...toes,back].map(pr)});
const Wn=side>0?'wn':'wf';
const wa=v(Wn+'a')+(freezeOn?trem*2:0),we=v(Wn+'e'),ws=v(Wn+'s');
const sh=[5*sqx,-12*sq,15*side*bw],el=ad(sh,dv(wa,ws,side),21),tip=ad(el,dv(wa+we,ws*1.15,side),21);
const prim=[];for(let i=0;i<4;i++){const b=ad(el,dv(wa+we,ws*1.15,side),9+i*4);prim.push(b,ad(b,dv(wa+we+0.15-i*0.3,ws*1.2,side),19-i*2));}
wings.push({side,p:[sh,el,tip,...prim].map(pr)});
}
const neck=v('neck'),hy=S.hy.v,nod=neck*0.5+0.12;
const nb=[10*sqx,-24*sq,0];
const Hc=[nb[0]+18*Math.sin(neck)+v('bob'),nb[1]-18*Math.cos(neck),0];
const fw=[Math.cos(nod)*Math.cos(hy),Math.sin(nod),-Math.cos(nod)*Math.sin(hy)],sd=[Math.sin(hy),0,Math.cos(hy)];
const hd={Hc:pr(Hc),tip:pr(ad(ad(Hc,fw,21),[0,1,0],2)),bt:pr(ad(ad(Hc,fw,8),[0,1,0],-3.5)),bb:pr(ad(ad(Hc,fw,8),[0,1,0],3.5)),
eyes:[1,-1].map(s=>pr(ad(ad(ad(Hc,fw,4),sd,8*s),[0,1,0],-3))),chest:pr([9*sqx,-6*sq,0]),mid:pr([11*sqx,-18*sq,0]),nb:pr(nb)};
const tl=v('tail'),fan=v('fan'),T0=[-13*sqx,16*sq,0];
const tails=[-1,0,1].map(i=>{const e=ad(ad(T0,dv(tl,0,1),30),[0,0,1],i*fan*11);const q=[T0[0]+(e[0]-T0[0])*0.72,T0[1]+(e[1]-T0[1])*0.72,T0[2]+(e[2]-T0[2])*0.72];return [pr(T0),pr(q),pr(e)];});
const rxs=Math.hypot(24*bw*sqx*cy,20*bw*sy),ry=30*sq;
let maxY=Math.sqrt((rxs*sr)**2+(ry*cr)**2),cx=0;
const cand=(p,r)=>{if(p[1]+r>maxY){maxY=p[1]+r;cx=p[0];}};
legs.forEach(l=>l.p.slice(2).forEach(p=>cand(p,2.5)));
wings.forEach(w=>w.p.slice(1).forEach(p=>cand(p,2.5)));
tails.forEach(t=>cand(t[2],4));
cand(hd.Hc,12.5);cand(hd.tip,1);
const rootY=-maxY-lift.y,rootX=S.x.v-v('anc')*hd.Hc[0];
contactX=rootX+cx;
if(dt>0){velX=(rootX-prevRX)/dt;const vy=(rootY-prevRY)/dt;velY=vy;
if(prevVy>230&&vy<60&&lift.y<=0){dust(prevVy*0.8);S.sq.vel-=prevVy*0.002*m;}prevVy=vy;}
prevRX=rootX;prevRY=rootY;rootXL=rootX;rootYL=rootY;
const T=p=>[p[0]+rootX,p[1]+rootY];
const shs=Math.max(0.35,1-lift.y/160);
F(ell(contactX*0.5+rootX*0.5,3,58*bw*shs,7*shs),[0.35,0.12,0],false);
const KEY=[0.9,0,0];
const drawWing=w=>{
const p=w.p.map(T),ink=w.side>0?[0.4,0,0.06]:[0.56,0.02,0.08];
for(let i=0;i<4;i++)Ln(line([p[3+i*2],p[4+i*2]]),5,[0.72,0.04,0.04]);
F(circ(p[0][0],p[0][1],7.5),ink);F(circ(p[1][0],p[1][1],6),ink);
const tp=taper([p[0],p[1],p[2]],[16,12,6]);F(tp,ink);
const dx=p[2][0]-p[1][0],dy=p[2][1]-p[1][1],l=Math.hypot(dx,dy)||1,nx=-dy/l,ny=dx/l;
for(const o of [2.6,-2.6])Ln(line([[p[1][0]+dx*0.15+nx*o,p[1][1]+dy*0.15+ny*o],[p[1][0]+dx*0.9+nx*o*0.7,p[1][1]+dy*0.9+ny*o*0.7]]),2.1,[1,0.1,0]);
Ln(tp,1.2,KEY,false);};
const drawLeg=l=>{
const p=l.p.map(T),ink=l.side>0?[0.05,0.75,0.6]:[0.22,0.8,0.6];
Ln(line([p[0],p[2],p[3]]),5.5,ink);
for(let i=4;i<8;i++)Ln(line([p[3],p[i]]),3,ink);
Ln(line([p[0],p[1]]),13*bw,l.side>0?[0.42,0.02,0.05]:[0.5,0.02,0.06]);};
const drawTail=()=>{tails.forEach(t=>{const a=T(t[0]),q=T(t[1]),e=T(t[2]);Ln(line([a,e]),9,[0.48,0.02,0.06]);Ln(line([q,e]),9.5,[0.95,0.1,0.02]);});};
const drawBody=()=>{const b=ell(rootX,rootY,rxs,ry,rot);F(b,[0.36,0.02,0.05]);Ln(b,1.4,KEY,false);};
const drawHead=()=>{
const c=T(hd.chest),hc=T(hd.Hc),md=T(hd.mid);
Ln(line([c,hc]),22*bw,[0.62,0.05,0.75]);
Ln(line([c,md]),13*bw,[0.5,0.75,0.05]);
const beakP=new Path2D(),bt=T(hd.bt),tp=T(hd.tip),bb=T(hd.bb);beakP.moveTo(bt[0],bt[1]);beakP.lineTo(tp[0],tp[1]);beakP.lineTo(bb[0],bb[1]);beakP.closePath();
const beakBehind=hd.tip[2]<hd.Hc[2];
const beak=()=>{F(beakP,[0.8,0.25,0.15]);F(circ(bt[0],bt[1],3.2),[0,0,0]);};
if(beakBehind)beak();
const hcir=circ(hc[0],hc[1],12.5);F(hcir,[0.6,0.05,0.08]);Ln(hcir,1.2,KEY,false);
if(!beakBehind)beak();
hd.eyes.forEach(e=>{if(e[2]>hd.Hc[2]+3){const q=T(e);F(circ(q[0],q[1],3.6),[0,0.85,1]);F(circ(q[0]+fw[0]*0.6,q[1],1.7),[1,0.6,0.2]);}});};
const parts=[];
wings.forEach(w=>parts.push({d:w.p[0][2]+(w.side>0?2:-2),f:()=>drawWing(w)}));
legs.forEach(l=>parts.push({d:l.p[0][2]+(l.side>0?1:-1),f:()=>drawLeg(l)}));
parts.push({d:tails[1][0][2]-3,f:drawTail});
parts.push({d:0,f:drawBody});
parts.push({d:hd.nb[2]+10-18*sy,f:drawHead});
parts.sort((a,b)=>a.d-b.d).forEach(p=>p.f());
}
// ---------- audio ----------
let ac=null,master,nbuf,lastStep=-1;
const KICK={0:1,6:0.55,10:0.9},SNR={4:0.8,12:0.8,7:0.18,9:0.12,15:0.22};
const BASS=[{0:82.4,3:82.4,6:98,10:110,14:98},{0:82.4,3:82.4,6:73.4,8:82.4,12:61.7}];
function initAudio(){ac=new (window.AudioContext||window.webkitAudioContext)();
const comp=ac.createDynamicsCompressor();comp.threshold.value=-14;comp.ratio.value=4;
master=ac.createGain();master.gain.value=0.55;master.connect(comp);comp.connect(ac.destination);
nbuf=ac.createBuffer(1,ac.sampleRate,ac.sampleRate);const d=nbuf.getChannelData(0);for(let i=0;i<d.length;i++)d[i]=Math.random()*2-1;}
function env(g,t,a,dec){g.gain.setValueAtTime(0.0001,t);g.gain.exponentialRampToValueAtTime(Math.max(0.0002,a),t+0.003);g.gain.exponentialRampToValueAtTime(0.0001,t+dec);}
function noise(t,dec,type,freq,q,amp){const s=ac.createBufferSource();s.buffer=nbuf;const f=ac.createBiquadFilter();f.type=type;f.frequency.value=freq;f.Q.value=q;
const g=ac.createGain();env(g,t,amp,dec);s.connect(f);f.connect(g);g.connect(master);s.start(t,Math.random()*0.5);s.stop(t+dec+0.05);}
function kick(t,a){const o=ac.createOscillator(),g=ac.createGain();o.frequency.setValueAtTime(160,t);o.frequency.exponentialRampToValueAtTime(45,t+0.12);env(g,t,a,0.38);o.connect(g);g.connect(master);o.start(t);o.stop(t+0.42);}
function snare(t,a){noise(t,0.19,'bandpass',1900,0.8,a);const o=ac.createOscillator(),g=ac.createGain();o.type='triangle';o.frequency.setValueAtTime(210,t);o.frequency.exponentialRampToValueAtTime(160,t+0.06);env(g,t,a*0.6,0.09);o.connect(g);g.connect(master);o.start(t);o.stop(t+0.12);}
function hat(t,a,open){noise(t,open?0.22:0.035,'highpass',7500,0.7,a);}
function bass(t,f,dur){const o=ac.createOscillator(),fl=ac.createBiquadFilter(),g=ac.createGain();o.type='sawtooth';o.frequency.value=f;fl.type='lowpass';
fl.frequency.setValueAtTime(900,t);fl.frequency.exponentialRampToValueAtTime(180,t+dur);fl.Q.value=6;env(g,t,0.26,dur);o.connect(fl);fl.connect(g);g.connect(master);o.start(t);o.stop(t+dur+0.05);}
function playStep(s,t){const i=((s%16)+16)%16,bar=Math.floor(s/16)%2;
if(KICK[i])kick(t,KICK[i]);if(SNR[i])snare(t,SNR[i]);if(i%2===0)hat(t,i===14?0.12:0.14,i===14);else hat(t,0.04,false);
const bn=BASS[bar][i];if(bn)bass(t,bn,0.36*60/bpm);}
function scheduleAudio(){if(!sndOn||!ac)return;const spb=60/bpm,horizon=beat+0.15/spb;
while(true){const s=lastStep+1;if(s/4>horizon)break;lastStep=s;playStep(s,ac.currentTime+Math.max(0,(s/4-beat)*spb));}}
function clapSound(){if(!ac||!sndOn)return;const t=ac.currentTime;for(let i=0;i<3;i++)noise(t+i*0.011,0.05+i*0.03,'bandpass',1200,1.2,0.5);}
// ---------- compose + print ----------
function compose(dt){
DL.length=0;
const left=-(W/2)/SC,top=-GY/SC,right=(W/2)/SC;
const s4=beat*4;let ke=0;for(let i=0;i<16;i++){const st=Math.floor(s4)-i,k=KICK[((st%16)+16)%16];if(k){ke=k*Math.exp(-(s4-st)*0.9);break;}}
DL.push({path:DOTS,tf:[0,-125,0.8+0.3*hype+0.05*ke],ink:[0,0.12,0.9],ko:false});
if(label!==prevLabel){prevLabel=label;labelPop=1;}labelPop=Math.max(0,labelPop-dt*4);
DL.push({text:'PIGEON BREAKS',x:left+24,y:top+34,font:'22px "Arial Black",Impact,sans-serif',ink:[1,0,0],ko:false});
DL.push({text:'vol.1 — no crumbs, no glory · tap anywhere on the beat',x:left+24,y:top+50,font:'10px ui-monospace,Menlo,monospace',ink:[0.85,0,0],ko:false});
const fs=Math.min(104,(right-left-40)/(label.length*0.8+0.5));
DL.push({text:label,x:left+18,y:top+62+fs*0.95,rot:-0.035,font:`${fs*(1+0.03*ke+0.12*labelPop)}px "Arial Black",Impact,sans-serif`,ink:[0,0.92,0.08],ko:false});
const sub=request?`next → ${NAMES[request]}`:pendingDown?`going down → ${NAMES[pendingDown]}`:auto?'freestyle · he is cooking':`on call · looping ${NAMES[stayMove]}`;
DL.push({text:sub,x:left+26,y:top+62+fs*0.95+20,font:'12px ui-monospace,Menlo,monospace',ink:[0.9,0,0],ko:false});
const cb=Math.floor(beat)%4;
for(let i=0;i<4;i++)F(circ(right-100+i*20,top+30,i===cb?6.5:3.5),i===cb?[0,1,0.2]:[0.9,0,0],false);
DL.push({text:bpm+' BPM',x:right-24,y:top+54,align:'right',font:'11px ui-monospace,Menlo,monospace',ink:[0.9,0,0],ko:false});
F(BOARD,[0.05,0.3,0.55]);Ln(BOARD,1.3,[0.55,0.05,0],false);Ln(CREASE,1,[0.3,0.05,0],false);
F(TAPE1,[0.14,0.02,0.18],false);F(TAPE2,[0.14,0.02,0.18],false);
DL.push({text:'↑ THIS SIDE UP',x:40,y:20,rot:0.01,font:'8px "Arial Black",sans-serif',ink:[0.55,0,0],ko:false});
buildBird(dt);
for(const p of fx){const L=p.life;
if(p.t==='d')F(circ(p.x,p.y,p.r*(1+(1-L)*1.5)),[0,0.3*L,0.55*L],false);
else if(p.t==='f')F(ell(p.x,p.y,5,1.8,p.a),[0.5,0,0.05]);
else if(p.t==='c'){const ink=p.good?[0,L,0.3*L]:[0.8*L,0,0];F(star(p.x,p.y,10+(1-L)*16,4+(1-L)*6,7),ink,false);
DL.push({text:p.good?'YEAH!':'off beat',x:p.x,y:p.y-26-(1-L)*10,align:'center',font:'12px "Arial Black",sans-serif',ink,ko:false});}}
// plates
boilT+=dt;if(boilT>0.09){boilT=0;plates.forEach((p,i)=>{p.ox=BASEOFF[i][0]+(Math.random()-0.5)*1.2;p.oy=BASEOFF[i][1]+(Math.random()-0.5)*1.2;p.gx=Math.random()*256;p.gy=Math.random()*256;});}
const clampv=(v,a)=>Math.max(-a,Math.min(a,v));
const smx=clampv(-velX*0.012+S.yaw.vel*0.45,9),smy=clampv(-velY*0.012,9);
plates.forEach((pl,i)=>{
const c=pl.x;c.setTransform(1,0,0,1,0,0);c.globalCompositeOperation='source-over';c.globalAlpha=1;c.clearRect(0,0,pl.c.width,pl.c.height);
c.setTransform(DPR*SC,0,0,DPR*SC,DPR*W/2,DPR*GY);c.fillStyle=c.strokeStyle=pl.col;c.lineCap=c.lineJoin='round';
for(const e of DL){const a=e.ink[i];
if(e.ko!==false){c.globalCompositeOperation='destination-out';c.globalAlpha=1;drawE(c,e);}
if(a>0.004){c.globalCompositeOperation='source-over';c.globalAlpha=Math.min(1,a);drawE(c,e);}}
c.setTransform(1,0,0,1,pl.gx||0,pl.gy||0);c.globalCompositeOperation='destination-out';c.globalAlpha=1;c.fillStyle=pl.pat;c.fillRect(-(pl.gx||0),-(pl.gy||0),pl.c.width,pl.c.height);
});
ctx.setTransform(1,0,0,1,0,0);ctx.globalCompositeOperation='source-over';ctx.globalAlpha=1;ctx.drawImage(paper,0,0);
ctx.globalCompositeOperation='multiply';
plates.forEach((pl,i)=>{let ox=pl.ox,oy=pl.oy;if(i===1){ox+=smx;oy+=smy;}if(i===2){ox-=smx*0.45;oy-=smy*0.45;}ctx.drawImage(pl.c,ox*DPR,oy*DPR);});
ctx.globalCompositeOperation='source-over';
}
function drawE(c,e){
if(e.text!==undefined){c.save();c.translate(e.x,e.y);if(e.rot)c.rotate(e.rot);c.font=e.font;c.textAlign=e.align||'left';c.fillText(e.text,0,0);c.restore();return;}
if(e.tf){c.save();c.translate(e.tf[0],e.tf[1]);c.scale(e.tf[2],e.tf[2]);}
if(e.lw){c.lineWidth=e.lw;c.stroke(e.path);}else c.fill(e.path);
if(e.tf)c.restore();
}
// ---------- controls ----------
const btns=[...document.querySelectorAll('button[data-m]')],autoB=document.getElementById('auto'),sndB=document.getElementById('snd'),hypeI=document.querySelector('#hype i');
let btnState='';
function updateButtons(){
const cur=move?move.name:'';const st=cur+'|'+request+'|'+pendingDown+'|'+auto;if(st===btnState)return;btnState=st;
btns.forEach(b=>{const m=b.dataset.m;b.classList.toggle('active',cur===m||(cur==='drop'&&pendingDown===m)||(cur==='getup'&&m==='toprock'));b.classList.toggle('queued',request===m);});
autoB.classList.toggle('active',auto);}
function call(m){request=m;if(!auto)stayMove=m;updateButtons();}
btns.forEach(b=>b.addEventListener('click',()=>{call(b.dataset.m);b.blur();}));
function toggleAuto(){auto=!auto;if(!auto){const n=move.name;stayMove=n==='drop'?(pendingDown||'footwork'):n==='getup'?'toprock':n;}btnState='';updateButtons();}
autoB.addEventListener('click',()=>{toggleAuto();autoB.blur();});
const bpmI=document.getElementById('bpm'),bpmO=document.getElementById('bpmo');bpmI.addEventListener('input',()=>{bpm=+bpmI.value;bpmO.textContent=bpm;});
const crI=document.getElementById('crumbs'),crO=document.getElementById('cro');crI.addEventListener('input',()=>{crumbs=+crI.value;crO.textContent=crumbs;});
function toggleSound(){if(!ac)initAudio();if(ac.state==='suspended')ac.resume();sndOn=!sndOn;lastStep=Math.floor(beat*4);sndB.firstChild.textContent='Sound: '+(sndOn?'on':'off');}
sndB.addEventListener('click',()=>{toggleSound();sndB.blur();});
function clap(wx,wy){
const ph=beat-Math.round(beat),good=Math.abs(ph)<0.16;
if(good){hype=Math.min(1,hype+0.13);S.bob.vel+=80;}
else{hype=Math.max(0,hype-0.04);if(Math.abs(S.yaw.vel)<3){const want=-PI/2-S.yaw.v;S.hy.t=Math.max(-1.6,Math.min(1.6,Math.atan2(Math.sin(want),Math.cos(want))));lookT=0.6;}}
fx.push({t:'c',x:wx,y:wy,good,life:1,dec:1.8});clapSound();}
document.getElementById('clap').addEventListener('click',e=>{clap(rootXL+(Math.random()-0.5)*80,rootYL-90);e.currentTarget.blur();});
cv.addEventListener('pointerdown',e=>{clap((e.clientX-W/2)/SC,(e.clientY-GY)/SC);});
addEventListener('keydown',e=>{
if(e.code==='Space'){e.preventDefault();clap(rootXL+(Math.random()-0.5)*80,rootYL-90);return;}
const n='12345'.indexOf(e.key);if(n>=0)call(['toprock','footwork','windmill','headspin','freeze'][n]);
if(e.key==='a'||e.key==='A')toggleAuto();if(e.key==='m'||e.key==='M')toggleSound();});
// ---------- go ----------
resize();
startMove('toprock',0);
let last=performance.now();
function frame(now){
const dt=Math.min(0.05,Math.max(0,(now-last)/1000));last=now;
const n=Math.max(1,Math.ceil(dt/0.0042));for(let i=0;i<n;i++)step(dt/n);
scheduleAudio();updateButtons();hypeI.style.width=(hype*100).toFixed(1)+'%';
compose(dt);requestAnimationFrame(frame);}
requestAnimationFrame(frame);
})();
</script>
</body>
</html>