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 crow in a backwards snapback and a gold chain, drawn as a bold screen-printed mascot — flat indigo shapes, amber beak and feet, a cyan eye — running a set on a flattened cardboard box under a streetlight. The look is late-night block-party: warm cardboard, cool spotlight, halftone grain, and a boombox keeping time.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>B·BIRD — a crow breaks it down</title>
<style>
:root{
--cy:#37e2d2; --mg:#ff3f6f; --ink:#0c0b14;
--gold:#ffd35a;
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{
background:#0a0913; overflow:hidden;
font-family:"Arial Black","Arial Bold",Gadget,system-ui,sans-serif;
color:#efeaff; -webkit-font-smoothing:antialiased;
user-select:none; -webkit-user-select:none;
}
#c{position:fixed; inset:0; display:block; width:100%; height:100%;}
#title{
position:fixed; top:18px; left:22px; z-index:5; pointer-events:none;
line-height:.9;
}
#title b{
display:block; font-size:clamp(26px,5vw,54px); letter-spacing:-1px;
color:#fff; text-shadow:0 0 18px rgba(55,226,210,.35), 3px 3px 0 var(--mg);
}
#title span{
display:block; margin-top:6px; font-size:clamp(9px,1.4vw,12px);
letter-spacing:3px; text-transform:uppercase; color:var(--cy);
font-family:ui-monospace,"SF Mono",Menlo,Consolas,monospace; font-weight:400;
}
#hud{
position:fixed; left:0; right:0; bottom:0; z-index:6;
display:flex; flex-wrap:wrap; align-items:center; gap:10px 14px;
padding:12px 18px calc(12px + env(safe-area-inset-bottom));
background:linear-gradient(to top, rgba(10,9,19,.92), rgba(10,9,19,.55) 70%, transparent);
backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
border-top:1px solid rgba(55,226,210,.18);
}
#moves{display:flex; flex-wrap:wrap; gap:8px}
button{
font-family:inherit; font-size:12px; letter-spacing:1px; text-transform:uppercase;
color:#efeaff; background:rgba(255,255,255,.04);
border:2px solid rgba(255,255,255,.16); border-radius:999px;
padding:9px 14px; cursor:pointer; transition:transform .08s, background .15s, border-color .15s, color .15s;
white-space:nowrap;
}
button:hover{border-color:var(--cy); color:#fff; background:rgba(55,226,210,.10)}
button:active{transform:translateY(1px) scale(.97)}
button.on{background:var(--mg); border-color:var(--mg); color:#fff; box-shadow:0 0 16px rgba(255,63,111,.5)}
button.flash{animation:fl .3s ease}
@keyframes fl{0%{box-shadow:0 0 0 0 var(--cy)}100%{box-shadow:0 0 0 14px rgba(55,226,210,0)}}
#right{margin-left:auto; display:flex; align-items:center; gap:14px; flex-wrap:wrap}
#tempo{display:flex; align-items:center; gap:9px;
font-family:ui-monospace,Menlo,monospace; font-size:11px; letter-spacing:1px;
text-transform:uppercase; color:#c9c3e8}
#tempo b{color:var(--gold); font-size:14px; min-width:2.4ch; display:inline-block; text-align:right}
input[type=range]{
-webkit-appearance:none; appearance:none; width:130px; height:4px; border-radius:4px;
background:linear-gradient(90deg,var(--cy),var(--mg)); outline:none; cursor:pointer;
}
input[type=range]::-webkit-slider-thumb{
-webkit-appearance:none; width:16px; height:16px; border-radius:50%;
background:#fff; border:2px solid var(--ink); box-shadow:0 0 8px rgba(55,226,210,.7);
}
input[type=range]::-moz-range-thumb{
width:16px; height:16px; border:2px solid var(--ink); border-radius:50%; background:#fff;
}
#hint{
position:fixed; right:18px; top:20px; z-index:5; pointer-events:none;
font-family:ui-monospace,Menlo,monospace; font-size:10px; letter-spacing:1px;
color:rgba(239,234,255,.4); text-align:right; line-height:1.7; text-transform:uppercase;
}
@media (max-width:560px){ #hint{display:none} #right{margin-left:0} }
</style>
</head>
<body>
<canvas id="c"></canvas>
<div id="title"><b>B·BIRD</b><span>a crow breaks it down on cardboard</span></div>
<div id="hint">click the floor<br>to call a random move</div>
<div id="hud">
<div id="moves">
<button data-move="toprock">Toprock</button>
<button data-move="sixstep">6-Step</button>
<button data-move="windmill">Windmill</button>
<button data-move="babyfreeze">Baby Freeze</button>
<button data-move="chair">Chair</button>
<button data-move="kickup">Kick-Up</button>
</div>
<div id="right">
<label id="tempo">Tempo <input id="bpm" type="range" min="70" max="140" value="104"><b id="bpmval">104</b> BPM</label>
<button id="hype">Hype: Off</button>
</div>
</div>
<script>
(function(){
'use strict';
/* ============================================================
canvas / world
============================================================ */
const cv = document.getElementById('c');
const ctx = cv.getContext('2d');
let W=0,H=0,DPR=1,S=1,centerX=0,groundY=0;
const COL = {
bodyDark:'#141320', bodyMid:'#232139', bodyLite:'#3a3763', belly:'#2c2a4c',
beak:'#f4a63a', beakDark:'#c6781f', foot:'#e8933a', footDark:'#a9661d',
eyeWhite:'#f2fbff', iris:'#37e2d2', pupil:'#0a0a12',
cap:'#ff3f6f', capDark:'#cf2a55', gold:'#ffd35a'
};
/* ============================================================
math helpers
============================================================ */
const clamp=(v,a,b)=>v<a?a:v>b?b:v;
const lerp=(a,b,t)=>a+(b-a)*t;
const easeOutCubic=t=>1-Math.pow(1-t,3);
const easeInCubic=t=>t*t*t;
const easeInOutCubic=t=>t<.5?4*t*t*t:1-Math.pow(-2*t+2,3)/2;
const dirUp = a=>({x:Math.sin(a), y:-Math.cos(a)});
const dirDown= a=>({x:Math.sin(a), y: Math.cos(a)});
/* ============================================================
state
============================================================ */
const state = { bpm:104, beat:0, hype:false, paused:false };
const MOVE_BEATS = { toprock:4, drop:1, sixstep:3, windmill:4, babyfreeze:2.5, chair:2.5, kickup:1.25 };
const AUTO = ['toprock','drop','sixstep','windmill','sixstep','babyfreeze','chair','kickup'];
let autoIndex=0, manualPending=false;
let cur = { move:'toprock', start:0 };
/* ============================================================
pose system
============================================================ */
function basePose(){ return {
x:0, y:70, rot:0, bodyLean:0, squash:0,
headTilt:0, headTurn:0, beak:0,
wRa:0.12, wRb:0.35, wLa:-0.12, wLb:0.35,
lRa:0.06, lRb:0.12, lLa:-0.06, lLb:0.12,
tail:0, tailSpread:0.35
}; }
/* --- TOPROCK: weight drops into the beat, floats back up --- */
function poseToprock(u, lb){
const p=basePose();
const ph=((lb%1)+1)%1;
let c;
if(ph<0.30) c=1; // seated in the weight
else if(ph<0.70) c=1-easeInOutCubic((ph-0.30)/0.40); // float up
else c=easeInCubic((ph-0.70)/0.30); // accelerate down into next beat
p.y = 70 - c*11;
p.lRa=0.06+c*0.5; p.lRb=0.12+c*0.7;
p.lLa=-0.06-c*0.5; p.lLb=0.12+c*0.7;
p.squash = ph<0.20 ? (1-ph/0.20)*0.16 : 0;
const sway=Math.sin(lb*Math.PI/2);
p.x = sway*6;
const sw=Math.sin(lb*Math.PI);
p.wRa=0.12+sw*0.95; p.wRb=0.35+Math.max(0,sw)*0.5;
p.wLa=-0.12-sw*0.95; p.wLb=0.35+Math.max(0,-sw)*0.5;
p.headTilt=c*0.22; p.headTurn=sway*0.4; p.beak=c*0.5;
p.tail=-sway*0.2;
return p;
}
/* --- DROP: accelerate to the floor, land with squash --- */
function poseDrop(u){
const p=basePose(), e=easeInCubic(u);
p.y=70-e*42;
p.bodyLean=e*0.55;
p.lRa=0.06+e*0.64; p.lRb=0.12+e*0.58;
p.lLa=-0.06-e*0.76; p.lLb=0.12+e*0.58;
p.wRa=0.12+e*0.38; p.wRb=0.35-e*0.2;
p.wLa=-0.12-e*0.58; p.wLb=0.35+e*0.15;
p.headTilt=e*0.15;
p.squash = u>0.82 ? (u-0.82)/0.18*0.22 : 0;
return p;
}
/* --- SIX-STEP: floor footwork, weight on the near wing --- */
function poseSixstep(u, lb){
const p=basePose();
p.y=28;
const a=lb*Math.PI, step=Math.sin(a);
p.bodyLean=0.55+Math.sin(a)*0.10;
p.wRa=0.5; p.wRb=0.15;
p.wLa=-0.7-step*0.3; p.wLb=0.5;
p.lRa=0.7+step*0.6; p.lRb=0.7-step*0.3;
p.lLa=0.7-step*0.6; p.lLb=0.7+step*0.3;
p.headTilt=0.15; p.headTurn=step*0.5;
p.tail=step*0.2;
return p;
}
/* --- WINDMILL: on the back, legs helicopter, body rocks --- */
function poseWindmill(u, lb){
const p=basePose();
p.y=38; p.bodyLean=-1.4;
p.rot=0.12*Math.sin(lb*Math.PI*0.5);
const spin=lb*Math.PI*2*0.85, sw=Math.sin(spin);
p.lRa=2.2+sw*1.0; p.lRb=0.25+Math.max(0,-sw)*0.5;
p.lLa=2.2-sw*1.0; p.lLb=0.25+Math.max(0,sw)*0.5;
p.wRa=1.25; p.wRb=0.3;
p.wLa=-0.6; p.wLb=0.5;
p.headTilt=-0.2; p.headTurn=sw*0.6; p.beak=0.4;
p.tail=sw*0.3; p.tailSpread=0.7;
return p;
}
/* --- BABY FREEZE: butt high, head/hand down, legs tucked, tremble --- */
function poseBabyfreeze(u, lb){
const p=basePose(), e=u<0.35?easeOutCubic(u/0.35):1;
p.y=70+e*10;
p.bodyLean=e*2.5;
p.headTilt=e*0.2;
p.wRa=0.35; p.wRb=0.15;
p.wLa=-1.4; p.wLb=0.5;
p.lRa=e*2.5; p.lRb=e*1.4;
p.lLa=e*2.2; p.lLb=e*1.7;
p.tail=-e*0.4; p.tailSpread=0.6;
if(u>0.35){ const tr=Math.min(1,(u-0.35)*4);
p.x+=Math.sin(lb*40)*0.7*tr; p.bodyLean+=Math.sin(lb*33)*0.012*tr; }
return p;
}
/* --- CHAIR FREEZE: elbow planted, seated on air, held --- */
function poseChair(u, lb){
const p=basePose(), e=u<0.35?easeOutCubic(u/0.35):1;
p.y=70-e*30;
p.bodyLean=e*1.0; p.rot=-e*0.08;
p.wRa=0.3+e*0.4; p.wRb=0.1;
p.wLa=-e*1.7; p.wLb=0.4;
p.lRa=e*1.5; p.lRb=e*1.3;
p.lLa=e*1.2; p.lLb=e*1.6;
p.tail=-e*0.4; p.tailSpread=0.6;
p.headTilt=e*0.25; p.headTurn=-0.3;
if(u>0.35){ const tr=Math.min(1,(u-0.35)*4); p.x+=Math.sin(lb*38)*0.6*tr; }
return p;
}
/* --- KICK-UP: kick through, hop, settle onto the feet --- */
function poseKickup(u){
const p=basePose(), e=easeOutCubic(u);
p.y=28+e*42;
p.bodyLean=0.55*(1-e);
p.lRa=0.7*(1-e)+0.06*e + Math.sin(u*Math.PI)*0.5;
p.lRb=0.7*(1-e)+0.12*e;
p.lLa=0.7*(1-e)-0.06*e;
p.lLb=0.7*(1-e)+0.12*e;
p.wRa=0.5*(1-e)+0.12*e; p.wRb=0.3;
p.wLa=-0.7*(1-e)-0.12*e; p.wLb=0.4;
p.headTilt=0.15*(1-e);
if(u>0.72){ const h=(u-0.72)/0.28; p.y+=Math.sin(h*Math.PI)*7; }
p.squash = u>0.90 ? (u-0.90)/0.10*0.16 : 0;
return p;
}
function targetPose(){
const lb=state.beat-cur.start;
const beats=MOVE_BEATS[cur.move]||1;
const u=clamp(lb/beats,0,1);
switch(cur.move){
case 'toprock': return poseToprock(u,lb);
case 'drop': return poseDrop(u);
case 'sixstep': return poseSixstep(u,lb);
case 'windmill': return poseWindmill(u,lb);
case 'babyfreeze':return poseBabyfreeze(u,lb);
case 'chair': return poseChair(u,lb);
case 'kickup': return poseKickup(u);
}
return basePose();
}
/* ============================================================
scheduler
============================================================ */
function advance(){
if(manualPending){ manualPending=false; autoIndex=0; cur={move:'toprock', start:state.beat}; }
else { autoIndex=(autoIndex+1)%AUTO.length; cur={move:AUTO[autoIndex], start:state.beat}; }
}
function trigger(move){
if(!(move in MOVE_BEATS)) return;
cur={move, start:state.beat}; manualPending=true;
}
/* ============================================================
blending + secondary (follow-through) motion
============================================================ */
let disp=basePose(), fromPose=basePose(), blend=1, lastMove=null, didImpact=false;
const BLEND_BEATS=0.22;
let prevTorsoAng=0, angVel=0, headLag=0, tailLag=0, prevY=70, yVel=0, headYlag=0;
function updatePose(dbeat){
const tgt=targetPose();
if(cur.move!==lastMove){ fromPose=Object.assign({},disp); blend=0; lastMove=cur.move; didImpact=false; }
blend=Math.min(1, blend + dbeat/BLEND_BEATS);
const b=easeOutCubic(blend);
for(const k in tgt){ disp[k]=fromPose[k]+(tgt[k]-fromPose[k])*b; }
// impact effects
const lb=state.beat-cur.start, beats=MOVE_BEATS[cur.move]||1, u=clamp(lb/beats,0,1);
if(!didImpact){
if((cur.move==='drop'&&u>0.85)||(cur.move==='kickup'&&u>0.90)||(cur.move==='windmill'&&u>0.04)){
didImpact=true;
spawnDust(centerX+disp.x*S, groundY-4*S, 14, 3);
shake(state.hype?9:5);
}
}
}
function updateSecondary(dt){
const torsoAng=disp.rot+disp.bodyLean;
let dA=torsoAng-prevTorsoAng;
while(dA> Math.PI) dA-=2*Math.PI;
while(dA<-Math.PI) dA+=2*Math.PI;
prevTorsoAng=torsoAng;
const instA=dt>0?dA/dt:0;
angVel += (instA-angVel)*Math.min(1,dt*9);
headLag += (clamp(-angVel*0.05,-0.45,0.45)-headLag)*Math.min(1,dt*11);
tailLag += (clamp(-angVel*0.09,-0.60,0.60)-tailLag)*Math.min(1,dt*8);
const instY=dt>0?(disp.y-prevY)/dt:0; prevY=disp.y;
yVel += (instY-yVel)*Math.min(1,dt*9);
headYlag += (clamp(-yVel*0.02,-5,5)-headYlag)*Math.min(1,dt*11);
}
/* ============================================================
particles / rings / shake
============================================================ */
let parts=[], rings=[], shakeAmt=0;
function shake(a){ shakeAmt=Math.max(shakeAmt,a); }
function spawnDust(x,y,n,pow){
for(let i=0;i<n;i++){
const a=Math.random()*Math.PI*2, sp=(0.4+Math.random())*pow;
parts.push({x,y,vx:Math.cos(a)*sp,vy:Math.sin(a)*sp-1,life:1,
size:(2+Math.random()*4)*S, col:Math.random()<0.5?'#d9c9a3':'#b5763c'});
}
}
function updateParts(dt){
for(const p of parts){ p.x+=p.vx*dt*60; p.y+=p.vy*dt*60; p.vy+=0.15*dt*60; p.life-=dt*1.4; }
parts=parts.filter(p=>p.life>0);
for(const r of rings){ r.r+=dt*140*S; r.life-=dt*1.8; }
rings=rings.filter(r=>r.life>0);
shakeAmt*=Math.max(0,1-dt*7); if(shakeAmt<0.2) shakeAmt=0;
}
/* ============================================================
geometry helpers
============================================================ */
function wingPts(sh, ang, bend){
const U=30,F=28;
const elbow={x:sh.x+Math.sin(ang)*U, y:sh.y+Math.cos(ang)*U};
const a2=ang-bend;
const tip={x:elbow.x+Math.sin(a2)*F, y:elbow.y+Math.cos(a2)*F};
return {sh,elbow,tip,a2};
}
function legPts(hip, ang, bend){
const T=34,S2=34;
const knee={x:hip.x+Math.sin(ang)*T, y:hip.y+Math.cos(ang)*T};
const a2=ang-bend;
const ankle={x:knee.x+Math.sin(a2)*S2, y:knee.y+Math.cos(a2)*S2};
return {hip,knee,ankle,a2};
}
function roundRect(x,y,w,h,r){
ctx.beginPath();
ctx.moveTo(x+r,y);
ctx.arcTo(x+w,y,x+w,y+h,r); ctx.arcTo(x+w,y+h,x,y+h,r);
ctx.arcTo(x,y+h,x,y,r); ctx.arcTo(x,y,x+w,y,r); ctx.closePath();
}
/* ============================================================
bird rendering
============================================================ */
function drawWing(w, col){
ctx.strokeStyle=col; ctx.lineCap='round'; ctx.lineJoin='round';
ctx.lineWidth=17; ctx.beginPath(); ctx.moveTo(w.sh.x,w.sh.y); ctx.lineTo(w.elbow.x,w.elbow.y); ctx.stroke();
ctx.lineWidth=12; ctx.beginPath(); ctx.moveTo(w.elbow.x,w.elbow.y); ctx.lineTo(w.tip.x,w.tip.y); ctx.stroke();
for(let i=0;i<3;i++){
const aa=w.a2+(i-1)*0.4, len=15-Math.abs(i-1)*3;
const px=Math.cos(aa)*5, py=-Math.sin(aa)*5;
ctx.fillStyle=col; ctx.beginPath();
ctx.moveTo(w.tip.x+px,w.tip.y+py); ctx.lineTo(w.tip.x-px,w.tip.y-py);
ctx.lineTo(w.tip.x+Math.sin(aa)*len, w.tip.y+Math.cos(aa)*len);
ctx.closePath(); ctx.fill();
}
}
function drawLeg(l, thighCol, shinCol){
ctx.lineCap='round'; ctx.lineJoin='round';
ctx.strokeStyle=thighCol; ctx.lineWidth=14;
ctx.beginPath(); ctx.moveTo(l.hip.x,l.hip.y); ctx.lineTo(l.knee.x,l.knee.y); ctx.stroke();
ctx.strokeStyle=shinCol; ctx.lineWidth=7;
ctx.beginPath(); ctx.moveTo(l.knee.x,l.knee.y); ctx.lineTo(l.ankle.x,l.ankle.y); ctx.stroke();
const fa=l.a2+1.35; ctx.lineWidth=5;
for(let i=-1;i<=1;i++){ const aa=fa+i*0.42;
ctx.beginPath(); ctx.moveTo(l.ankle.x,l.ankle.y);
ctx.lineTo(l.ankle.x+Math.sin(aa)*13, l.ankle.y+Math.cos(aa)*13); ctx.stroke(); }
}
function drawTorso(pelvis, chest, perp){
ctx.lineCap='round'; ctx.lineJoin='round';
ctx.strokeStyle=COL.bodyMid; ctx.lineWidth=38;
ctx.beginPath(); ctx.moveTo(pelvis.x,pelvis.y); ctx.lineTo(chest.x,chest.y); ctx.stroke();
ctx.strokeStyle=COL.belly; ctx.lineWidth=20;
ctx.beginPath(); ctx.moveTo(pelvis.x+perp.x*7,pelvis.y+perp.y*7); ctx.lineTo(chest.x+perp.x*7,chest.y+perp.y*7); ctx.stroke();
ctx.fillStyle=COL.bodyLite;
ctx.beginPath(); ctx.arc(chest.x+perp.x*6, chest.y+perp.y*6, 8, 0, Math.PI*2); ctx.fill();
}
function drawChain(chest, perp, tDir){
const A={x:chest.x+perp.x*9-tDir.x*2, y:chest.y+perp.y*9-tDir.y*2};
const B={x:chest.x-perp.x*9-tDir.x*2, y:chest.y-perp.y*9-tDir.y*2};
const C={x:chest.x-tDir.x*16, y:chest.y-tDir.y*16};
ctx.strokeStyle=COL.gold; ctx.lineWidth=2.5; ctx.lineCap='round';
ctx.beginPath(); ctx.moveTo(A.x,A.y); ctx.quadraticCurveTo(C.x,C.y,B.x,B.y); ctx.stroke();
for(let i=1;i<6;i++){ const t=i/6, it=1-t;
const x=it*it*A.x+2*it*t*C.x+t*t*B.x, y=it*it*A.y+2*it*t*C.y+t*t*B.y;
ctx.fillStyle=COL.gold; ctx.beginPath(); ctx.arc(x,y,2.2,0,Math.PI*2); ctx.fill(); }
const px=0.25*A.x+0.5*C.x+0.25*B.x, py=0.25*A.y+0.5*C.y+0.25*B.y;
ctx.save(); ctx.translate(px,py); ctx.rotate(Math.PI/4); ctx.fillStyle=COL.gold; ctx.fillRect(-3,-3,6,6); ctx.restore();
}
function drawHeadNeck(chest, headC, hDir, fwd, p){
// neck
ctx.strokeStyle=COL.bodyMid; ctx.lineWidth=22; ctx.lineCap='round';
ctx.beginPath(); ctx.moveTo(chest.x,chest.y);
ctx.lineTo(headC.x-hDir.x*12, headC.y-hDir.y*12); ctx.stroke();
// head
ctx.fillStyle=COL.bodyMid; ctx.beginPath(); ctx.arc(headC.x,headC.y,19,0,Math.PI*2); ctx.fill();
// sheen
ctx.strokeStyle=COL.bodyLite; ctx.lineWidth=4; ctx.lineCap='round';
const ha=Math.atan2(hDir.y,hDir.x);
ctx.beginPath(); ctx.arc(headC.x,headC.y,15, ha-1.0, ha-0.2); ctx.stroke();
// beak
const bl=18, open=p.beak*7;
const bB={x:headC.x+fwd.x*12, y:headC.y+fwd.y*12};
const tp={x:headC.x+fwd.x*(12+bl), y:headC.y+fwd.y*(12+bl)};
ctx.fillStyle=COL.beak; ctx.beginPath();
ctx.moveTo(bB.x+hDir.x*7, bB.y+hDir.y*7);
ctx.lineTo(tp.x+hDir.x*open, tp.y+hDir.y*open);
ctx.lineTo(bB.x-hDir.x*3, bB.y-hDir.y*3); ctx.closePath(); ctx.fill();
ctx.fillStyle=COL.beakDark; ctx.beginPath();
ctx.moveTo(bB.x-hDir.x*3, bB.y-hDir.y*3);
ctx.lineTo(tp.x-hDir.x*open, tp.y-hDir.y*open);
ctx.lineTo(bB.x-hDir.x*8, bB.y-hDir.y*8); ctx.closePath(); ctx.fill();
// eye
const ex=headC.x+fwd.x*8+hDir.x*2+fwd.x*p.headTurn*3;
const ey=headC.y+fwd.y*8+hDir.y*2+fwd.y*p.headTurn*3;
ctx.fillStyle=COL.eyeWhite; ctx.beginPath(); ctx.arc(ex,ey,6.5,0,Math.PI*2); ctx.fill();
ctx.fillStyle=COL.iris; ctx.beginPath(); ctx.arc(ex+fwd.x*1.5,ey+fwd.y*1.5,4,0,Math.PI*2); ctx.fill();
ctx.fillStyle=COL.pupil; ctx.beginPath(); ctx.arc(ex+fwd.x*2.3,ey+fwd.y*2.3,2.4,0,Math.PI*2); ctx.fill();
ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(ex-1.6,ey-1.6,1.2,0,Math.PI*2); ctx.fill();
// backwards cap
ctx.fillStyle=COL.cap; ctx.beginPath(); ctx.moveTo(headC.x,headC.y);
ctx.arc(headC.x,headC.y,20, ha-Math.PI*0.42, ha+Math.PI*0.42); ctx.closePath(); ctx.fill();
ctx.save();
ctx.translate(headC.x-fwd.x*13+hDir.x*3, headC.y-fwd.y*13+hDir.y*3);
ctx.rotate(Math.atan2(fwd.y,fwd.x));
ctx.fillStyle=COL.capDark; ctx.beginPath(); ctx.ellipse(0,0,12,4.5,0,0,Math.PI*2); ctx.fill();
ctx.restore();
ctx.fillStyle=COL.capDark; ctx.beginPath(); ctx.arc(headC.x+hDir.x*18, headC.y+hDir.y*18, 3, 0, Math.PI*2); ctx.fill();
}
function tailFeathers(base, ang, spread, col){
for(let i=0;i<5;i++){
const f=(i/4-0.5), aa=ang+f*spread, len=34-Math.abs(f)*10;
const px=Math.cos(aa)*6, py=-Math.sin(aa)*6;
ctx.fillStyle=col; ctx.beginPath();
ctx.moveTo(base.x+px*0.4, base.y+py*0.4);
ctx.lineTo(base.x-px*0.4, base.y-py*0.4);
ctx.lineTo(base.x+Math.sin(aa)*len, base.y+Math.cos(aa)*len);
ctx.closePath(); ctx.fill();
}
}
function drawShadow(p){
const grounded=clamp(1-(p.y-30)/80, 0.3, 1);
const rx=(34+10*grounded)*S;
const x=centerX+p.x*S, y=groundY+2;
ctx.save(); ctx.translate(x,y); ctx.scale(1,0.28);
const g=ctx.createRadialGradient(0,0,0,0,0,rx);
g.addColorStop(0,'rgba(0,0,0,'+(0.42*grounded)+')');
g.addColorStop(1,'rgba(0,0,0,0)');
ctx.fillStyle=g; ctx.beginPath(); ctx.arc(0,0,rx,0,Math.PI*2); ctx.fill();
ctx.restore();
}
function drawBird(){
const p=disp;
drawShadow(p);
ctx.save();
ctx.translate(centerX+p.x*S, groundY-p.y*S);
ctx.scale(S,S);
ctx.rotate(p.rot);
if(p.squash>0.001) ctx.scale(1+p.squash*0.5, 1-p.squash*0.5);
const TL=58;
const tDir=dirUp(p.bodyLean), perp={x:-tDir.y, y:tDir.x};
const pelvis={x:0,y:0};
const chest={x:tDir.x*TL, y:tDir.y*TL};
const shC={x:chest.x-tDir.x*6, y:chest.y-tDir.y*6};
const shR={x:shC.x+perp.x*11, y:shC.y+perp.y*11};
const shL={x:shC.x-perp.x*11, y:shC.y-perp.y*11};
const headAng=p.bodyLean+p.headTilt+headLag;
const hDir=dirUp(headAng), fwd={x:-hDir.y, y:hDir.x};
const headC={x:chest.x+hDir.x*34, y:chest.y+hDir.y*34+headYlag};
const wR=wingPts(shR,p.wRa,p.wRb), wL=wingPts(shL,p.wLa,p.wLb);
const lR=legPts({x:6,y:2},p.lRa,p.lRb), lL=legPts({x:-6,y:2},p.lLa,p.lLb);
const tailAng=-0.7+p.tail+tailLag+p.bodyLean*0.25;
tailFeathers({x:-2,y:-2}, tailAng, 0.5+p.tailSpread, COL.bodyDark);
drawWing(wL, COL.bodyDark);
drawLeg(lL, COL.bodyDark, COL.footDark);
drawTorso(pelvis, chest, perp);
drawLeg(lR, COL.bodyMid, COL.foot);
drawChain(chest, perp, tDir);
drawHeadNeck(chest, headC, hDir, fwd, p);
drawWing(wR, COL.bodyMid);
ctx.restore();
}
/* ============================================================
scene
============================================================ */
function beatPulse(){ const ph=((state.beat%1)+1)%1; return Math.pow(1-ph,2); }
function drawSpotlight(){
const pulse=beatPulse(), cx=centerX, topY=-20, spread=260*S;
const g=ctx.createRadialGradient(cx,topY,10,cx,groundY,spread*1.7);
const a=0.10+pulse*0.10+(state.hype?0.06:0);
const col=state.hype?'255,63,111':'255,240,210';
g.addColorStop(0,'rgba('+col+','+a+')'); g.addColorStop(1,'rgba('+col+',0)');
ctx.fillStyle=g;
ctx.beginPath(); ctx.moveTo(cx-30,topY); ctx.lineTo(cx+30,topY);
ctx.lineTo(cx+spread,groundY+50); ctx.lineTo(cx-spread,groundY+50); ctx.closePath(); ctx.fill();
}
function dotGrid(x0,y0,w,h,step){
ctx.fillStyle='rgba(120,110,200,0.10)';
for(let y=y0;y<y0+h;y+=step) for(let x=x0;x<x0+w;x+=step){
const dx=(x-(x0+w/2))/(w/2), dy=(y-(y0+h/2))/(h/2), d=Math.sqrt(dx*dx+dy*dy);
if(d<1){ ctx.beginPath(); ctx.arc(x,y,(1-d)*step*0.3,0,Math.PI*2); ctx.fill(); }
}
}
function drawBackdrop(){
const g=ctx.createRadialGradient(centerX,groundY-90*S,10,centerX,groundY-90*S,270*S);
g.addColorStop(0,'rgba(90,80,180,0.18)'); g.addColorStop(1,'rgba(90,80,180,0)');
ctx.fillStyle=g; ctx.fillRect(0,0,W,H);
dotGrid(0,0,220*S,220*S,16*S);
dotGrid(W-220*S,0,220*S,220*S,16*S);
}
function drawCardboard(){
const cx=centerX, gy=groundY, w=330*S;
const front=gy+64*S, back=gy-6*S;
ctx.fillStyle='#b5763c';
ctx.beginPath(); ctx.moveTo(cx-w,front); ctx.lineTo(cx+w,front);
ctx.lineTo(cx+w*0.86,back); ctx.lineTo(cx-w*0.86,back); ctx.closePath(); ctx.fill();
ctx.fillStyle='#8f5a2c';
ctx.beginPath(); ctx.moveTo(cx-w,front); ctx.lineTo(cx+w,front);
ctx.lineTo(cx+w,front+14*S); ctx.lineTo(cx-w,front+14*S); ctx.closePath(); ctx.fill();
// center flap line
ctx.strokeStyle='rgba(60,40,25,0.45)'; ctx.lineWidth=2*S;
ctx.beginPath(); ctx.moveTo(cx,back); ctx.lineTo(cx,front); ctx.stroke();
// tape
ctx.fillStyle='rgba(232,222,190,0.45)';
ctx.save(); ctx.translate(cx-w*0.55, gy+24*S); ctx.rotate(-0.2); ctx.fillRect(-24*S,-7*S,48*S,14*S); ctx.restore();
ctx.save(); ctx.translate(cx+w*0.5, gy+30*S); ctx.rotate(0.25); ctx.fillRect(-24*S,-7*S,48*S,14*S); ctx.restore();
// marker star
ctx.strokeStyle='rgba(40,30,55,0.5)'; ctx.lineWidth=2.5*S; ctx.lineCap='round';
const sx=cx+w*0.34, sy=gy+22*S, r=12*S;
ctx.beginPath();
for(let i=0;i<5;i++){ const a=-Math.PI/2+i*Math.PI*4/5;
const x=sx+Math.cos(a)*r, y=sy+Math.sin(a)*r; i?ctx.lineTo(x,y):ctx.moveTo(x,y); }
ctx.closePath(); ctx.stroke();
}
function drawBoombox(){
const bx=centerX-250*S, by=groundY-4*S, w=92*S, h=54*S, pulse=beatPulse();
ctx.save(); ctx.translate(bx,by);
ctx.fillStyle='#2a2740'; roundRect(-w/2,-h,w,h,6*S); ctx.fill();
ctx.fillStyle='#3a3757'; roundRect(-w/2,-h,w,10*S,6*S); ctx.fill();
for(const sx of [-w*0.28, w*0.28]){
const r=(13+pulse*3)*S;
ctx.fillStyle='#15131f'; ctx.beginPath(); ctx.arc(sx,-h*0.5,r,0,Math.PI*2); ctx.fill();
ctx.fillStyle='#4a4770'; ctx.beginPath(); ctx.arc(sx,-h*0.5,r*0.5,0,Math.PI*2); ctx.fill();
ctx.fillStyle='#15131f'; ctx.beginPath(); ctx.arc(sx,-h*0.5,r*0.2,0,Math.PI*2); ctx.fill();
}
ctx.strokeStyle='#4a4770'; ctx.lineWidth=3*S; ctx.beginPath(); ctx.arc(0,-h,16*S,Math.PI,0); ctx.stroke();
ctx.strokeStyle='#8a86b0'; ctx.lineWidth=2*S; ctx.beginPath(); ctx.moveTo(w*0.4,-h); ctx.lineTo(w*0.56,-h-24*S); ctx.stroke();
ctx.fillStyle=state.hype?'#ff3f6f':'#37e2d2';
ctx.beginPath(); ctx.arc(-w*0.4,-h*0.78,2.6*S,0,Math.PI*2); ctx.fill();
ctx.restore();
}
function drawSpeedLines(){
const spinning = cur.move==='windmill';
if(!spinning) return;
const cx=centerX+disp.x*S, cy=groundY-disp.y*S, r=64*S;
const spin=(state.beat-cur.start)*Math.PI*2*0.85;
ctx.save(); ctx.translate(cx,cy);
ctx.strokeStyle='rgba(55,226,210,0.22)'; ctx.lineWidth=3*S; ctx.lineCap='round';
for(let i=0;i<3;i++){ const a0=spin-i*0.5; ctx.beginPath(); ctx.arc(0,0,r,a0,a0+0.9); ctx.stroke(); }
ctx.restore();
}
function drawParts(){
for(const p of parts){ ctx.globalAlpha=Math.max(0,p.life); ctx.fillStyle=p.col;
ctx.beginPath(); ctx.arc(p.x,p.y,p.size,0,Math.PI*2); ctx.fill(); }
ctx.globalAlpha=1;
for(const r of rings){ ctx.globalAlpha=Math.max(0,r.life)*0.5; ctx.strokeStyle='#37e2d2';
ctx.lineWidth=2; ctx.beginPath(); ctx.ellipse(r.x,r.y,r.r,r.r*0.3,0,0,Math.PI*2); ctx.stroke(); }
ctx.globalAlpha=1;
}
/* ============================================================
noise + vignette
============================================================ */
let noisePattern=null;
function buildNoise(){
const n=document.createElement('canvas'); n.width=n.height=128;
const nx=n.getContext('2d'), img=nx.createImageData(128,128);
for(let i=0;i<img.data.length;i+=4){ const v=118+Math.random()*24;
img.data[i]=img.data[i+1]=img.data[i+2]=v; img.data[i+3]=255; }
nx.putImageData(img,0,0);
noisePattern=ctx.createPattern(n,'repeat');
}
function drawNoise(){
if(!noisePattern) return;
ctx.save(); ctx.globalAlpha=0.4; ctx.globalCompositeOperation='overlay';
ctx.fillStyle=noisePattern; ctx.fillRect(0,0,W,H); ctx.restore();
}
function drawVignette(){
const g=ctx.createRadialGradient(W/2,H*0.45,Math.min(W,H)*0.3, W/2,H*0.5,Math.max(W,H)*0.75);
g.addColorStop(0,'rgba(0,0,0,0)'); g.addColorStop(1,'rgba(0,0,0,0.55)');
ctx.fillStyle=g; ctx.fillRect(0,0,W,H);
}
/* ============================================================
main draw
============================================================ */
function draw(){
ctx.setTransform(DPR,0,0,DPR,0,0);
ctx.clearRect(0,0,W,H);
const ox=(Math.random()-0.5)*shakeAmt, oy=(Math.random()-0.5)*shakeAmt;
ctx.save(); ctx.translate(ox,oy);
drawBackdrop();
drawSpotlight();
drawCardboard();
drawBoombox();
drawSpeedLines();
drawBird();
drawParts();
ctx.restore();
drawNoise();
drawVignette();
}
/* ============================================================
loop
============================================================ */
let last=performance.now(), lastBeatInt=0;
function frame(now){
let dt=(now-last)/1000; last=now; if(dt>0.05) dt=0.05;
if(!state.paused){
const dbeat=dt*state.bpm/60;
state.beat += dbeat;
if(state.beat-cur.start >= (MOVE_BEATS[cur.move]||1)) advance();
updatePose(dbeat);
updateSecondary(dt);
updateParts(dt);
const bi=Math.floor(state.beat);
if(bi!==lastBeatInt){ lastBeatInt=bi;
rings.push({x:centerX+disp.x*S, y:groundY, r:10*S, life:1});
if(state.hype){ spawnDust(centerX+disp.x*S, groundY-4*S, 5, 2); shake(3); }
}
}
draw();
requestAnimationFrame(frame);
}
/* ============================================================
resize
============================================================ */
function resize(){
DPR=Math.min(window.devicePixelRatio||1, 2);
W=window.innerWidth; H=window.innerHeight;
cv.width=Math.floor(W*DPR); cv.height=Math.floor(H*DPR);
cv.style.width=W+'px'; cv.style.height=H+'px';
ctx.setTransform(DPR,0,0,DPR,0,0);
S=Math.max(0.45, Math.min(W/860, H/560));
centerX=W*0.5; groundY=H*0.80;
}
window.addEventListener('resize', resize);
/* ============================================================
controls
============================================================ */
const bpmInput=document.getElementById('bpm');
const bpmVal=document.getElementById('bpmval');
const hypeBtn=document.getElementById('hype');
const moveBtns=[...document.querySelectorAll('#moves button')];
function flashBtn(move){ const b=moveBtns.find(x=>x.dataset.move===move); if(b){ b.classList.remove('flash'); void b.offsetWidth; b.classList.add('flash'); } }
function setHype(on){ state.hype=on; hypeBtn.textContent='Hype: '+(on?'On':'Off'); hypeBtn.classList.toggle('on',on); }
moveBtns.forEach(b=> b.addEventListener('click',()=>{ trigger(b.dataset.move); flashBtn(b.dataset.move); b.blur(); }));
bpmInput.addEventListener('input',()=>{ state.bpm=+bpmInput.value; bpmVal.textContent=state.bpm; });
hypeBtn.addEventListener('click',()=>{ setHype(!state.hype); hypeBtn.blur(); });
cv.addEventListener('pointerdown',()=>{
const pool=['windmill','babyfreeze','chair','sixstep','toprock'];
trigger(pool[(Math.random()*pool.length)|0]);
});
window.addEventListener('keydown',e=>{
if(e.target && e.target.tagName==='INPUT') return;
const map={'1':'toprock','2':'sixstep','3':'windmill','4':'babyfreeze','5':'chair','6':'kickup'};
if(map[e.key]){ trigger(map[e.key]); flashBtn(map[e.key]); }
else if(e.key===' '){ e.preventDefault(); setHype(!state.hype); }
else if(e.key==='p'||e.key==='P'){ state.paused=!state.paused; }
});
/* ============================================================
go
============================================================ */
resize();
buildNoise();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>