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.
The dancer is a kiwi — round, flightless, and built around a long ivory beak that doubles as a third limb for freezes and spins. The look is risograph-poster meets charcoal sketchbook: warm paper, a taped-down cardboard dance mat, speckled brown feathers, dust puffs, and a tiny boombox keeping time.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KIWI BREAKS — a tiny breakdancing bird</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
:root{
--ink:#2b1c12; --paper:#f3e6cf; --paper2:#ecd9b8;
--red:#d94f30; --teal:#1f7a6d; --gold:#e9a13b; --brown:#6b4a2f;
}
html,body{height:100%}
body{
font-family: ui-rounded, "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
background:
radial-gradient(circle at 15% 20%, rgba(217,79,48,.14) 0 90px, transparent 91px),
radial-gradient(circle at 88% 12%, rgba(31,122,109,.16) 0 70px, transparent 71px),
repeating-linear-gradient(0deg, rgba(43,28,18,.025) 0 2px, transparent 2px 5px),
linear-gradient(160deg, var(--paper), var(--paper2));
color:var(--ink);
min-height:100vh;
display:flex; flex-direction:column; align-items:center;
padding:18px 14px 26px;
overflow-x:hidden;
}
header{ text-align:center; margin-bottom:10px; position:relative; z-index:2; }
h1{
font-size:clamp(30px,6vw,54px); letter-spacing:.04em; line-height:.95;
font-weight:900; text-transform:uppercase;
transform:rotate(-1.2deg);
text-shadow:3px 3px 0 var(--gold), 6px 6px 0 rgba(43,28,18,.15);
}
h1 .alt{ color:var(--red); }
.sub{ margin-top:6px; font-size:clamp(12px,2.4vw,15px); opacity:.8; font-style:italic; }
.beatlights{ display:flex; gap:7px; justify-content:center; margin-top:9px; }
.beatlights i{
width:13px;height:13px;border-radius:50%;display:block;
background:#fff8; border:2px solid var(--ink); transition:all .08s;
}
.beatlights i.on{ background:var(--red); transform:scale(1.35); box-shadow:0 0 0 4px rgba(217,79,48,.2); }
.beatlights i.on.b2{ background:var(--teal); box-shadow:0 0 0 4px rgba(31,122,109,.2); }
main{
width:min(1060px,100%);
display:grid; grid-template-columns: 1fr 248px; gap:14px; align-items:stretch;
}
@media (max-width:760px){ main{ grid-template-columns:1fr; } }
.stagewrap{
position:relative; border:3px solid var(--ink); border-radius:18px; overflow:hidden;
box-shadow:7px 8px 0 rgba(43,28,18,.85);
background:#f7ecd4; min-height:340px;
}
canvas{ display:block; width:100%; height:100%; min-height:340px; cursor:pointer; touch-action:manipulation; }
.movetag{
position:absolute; left:12px; top:10px; z-index:3;
background:var(--ink); color:#ffe9bd; padding:6px 12px; border-radius:999px;
font-weight:800; letter-spacing:.08em; font-size:12px; text-transform:uppercase;
transform:rotate(-1.5deg); pointer-events:none;
box-shadow:3px 3px 0 rgba(0,0,0,.25);
}
.movetag b{ color:var(--gold); }
.nexttag{
position:absolute; right:12px; top:10px; z-index:3;
background:#fff9; border:2px solid var(--ink); padding:5px 10px; border-radius:999px;
font-size:11px; font-weight:700; pointer-events:none;
}
aside{
border:3px solid var(--ink); border-radius:18px; padding:14px 14px 12px;
background:linear-gradient(180deg,#fff6df,#f6e3bd);
box-shadow:7px 8px 0 rgba(43,28,18,.85);
display:flex; flex-direction:column; gap:12px;
}
aside h2{ font-size:13px; letter-spacing:.14em; text-transform:uppercase; border-bottom:2px dashed #2b1c1255; padding-bottom:6px; }
.discrow{ display:flex; align-items:center; gap:12px; }
#disc{
width:74px;height:74px;border-radius:50%;flex:0 0 auto;cursor:grab;
background:
radial-gradient(circle, #f3e6cf 0 10px, var(--red) 11px 16px, #1c1410 17px 34px, #2b1c12 35px);
border:3px solid var(--ink); position:relative; touch-action:none;
box-shadow:3px 3px 0 rgba(43,28,18,.4);
}
#disc:active{cursor:grabbing}
#disc::after{ content:""; position:absolute; left:50%; top:6px; width:5px;height:14px;background:var(--gold);border-radius:3px; transform:translateX(-50%); border:1.5px solid var(--ink);}
.discrow p{ font-size:11.5px; line-height:1.45; opacity:.85; }
.btns{ display:grid; grid-template-columns:1fr 1fr; gap:7px; }
button{
font:inherit; font-weight:800; font-size:12.5px; letter-spacing:.02em;
padding:9px 6px; border-radius:11px; border:2.5px solid var(--ink);
background:#fff8e8; color:var(--ink); cursor:pointer;
box-shadow:3px 3px 0 var(--ink); transition:transform .06s, box-shadow .06s, background .15s;
}
button:hover{ background:#ffe9bd; }
button:active{ transform:translate(3px,3px); box-shadow:0 0 0 var(--ink); }
button.wide{ grid-column:1/-1; }
button.accent{ background:var(--red); color:#fff3da; }
button.teal{ background:var(--teal); color:#eafffa; }
button.held{ background:var(--gold); transform:translate(3px,3px); box-shadow:0 0 0 var(--ink); }
label.slider{ display:block; font-size:12px; font-weight:800; letter-spacing:.05em; }
label.slider span{ float:right; background:var(--ink); color:#ffe9bd; border-radius:6px; padding:1px 7px; font-size:11px; }
input[type=range]{ width:100%; margin-top:6px; accent-color:var(--red); height:22px; cursor:pointer; }
.row{ display:flex; align-items:center; justify-content:space-between; gap:8px; font-size:12.5px; font-weight:700; }
.switch{ position:relative; width:46px; height:25px; flex:0 0 auto; }
.switch input{ opacity:0; width:100%; height:100%; position:absolute; cursor:pointer; z-index:2; margin:0;}
.switch i{ position:absolute; inset:0; background:#fff8e8; border:2.5px solid var(--ink); border-radius:999px; display:block; }
.switch i::after{ content:""; position:absolute; top:2px; left:2px; width:16px;height:16px;border-radius:50%; background:var(--ink); transition:left .15s, background .15s; }
.switch input:checked + i{ background:#bfe6dd; }
.switch input:checked + i::after{ left:23px; background:var(--teal); }
.hint{ font-size:11px; line-height:1.5; opacity:.75; border-top:2px dashed #2b1c1255; padding-top:8px; }
footer{ margin-top:14px; font-size:11.5px; opacity:.65; text-align:center; max-width:700px; line-height:1.5;}
kbd{ background:var(--ink); color:#ffe9bd; border-radius:4px; padding:0 5px; font-family:inherit; font-size:10.5px;}
</style>
</head>
<body>
<header>
<h1>Kiwi <span class="alt">Breaks</span></h1>
<p class="sub">one small flightless bird, one piece of cardboard, absolutely no chill</p>
<div class="beatlights" id="beatlights"><i></i><i></i><i></i><i></i></div>
</header>
<main>
<div class="stagewrap" id="stagewrap">
<div class="movetag">Now: <b id="movename">Toprock</b></div>
<div class="nexttag" id="nexttag">next: auto</div>
<canvas id="c"></canvas>
</div>
<aside>
<h2>DJ Booth</h2>
<div class="discrow">
<div id="disc" title="Drag to scratch"></div>
<p><b>Scratch the record.</b><br>Drag the vinyl in a circle. Flick it hard and let go — she'll answer with a backspin.</p>
</div>
<div class="btns">
<button data-move="toprock">Toprock</button>
<button data-move="sixstep">6-Step</button>
<button data-move="windmill">Windmill</button>
<button data-move="flare">Flare</button>
<button data-move="backspin">Backspin</button>
<button data-move="beakspin">Beak Spin</button>
<button data-move="beakfreeze" class="accent wide">★ Beak Freeze</button>
<button id="holdfreeze" class="wide">Hold the freeze — press & hold</button>
</div>
<div>
<label class="slider">Tempo <span id="bpmval">96 BPM</span></label>
<input type="range" id="bpm" min="68" max="138" value="96">
</div>
<div>
<label class="slider">Energy <span id="energyval">100%</span></label>
<input type="range" id="energy" min="55" max="155" value="100">
</div>
<div class="row"><span>Auto-choreography</span>
<span class="switch"><input type="checkbox" id="auto" checked><i></i></span>
</div>
<div class="row"><span>Beat (kick + hat)</span>
<span class="switch"><input type="checkbox" id="sound"><i></i></span>
</div>
<p class="hint">Click the stage to hype the crowd. Move your mouse — she's watching you. <kbd>Space</kbd> = freeze, <kbd>N</kbd> = next move.</p>
</aside>
</main>
<footer>No birds were spun against their will. The kiwi insists she can do the beak spin again, faster, and would like a bigger piece of cardboard.</footer>
<script>
(() => {
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
const wrap = document.getElementById('stagewrap');
const moveNameEl = document.getElementById('movename');
const nextTagEl = document.getElementById('nexttag');
const beatDots = [...document.querySelectorAll('#beatlights i')];
let W = 800, H = 460, DPR = 1;
function resize(){
const r = wrap.getBoundingClientRect();
DPR = Math.min(window.devicePixelRatio || 1, 2);
W = Math.max(320, r.width); H = Math.max(340, r.height);
canvas.width = W * DPR; canvas.height = H * DPR;
canvas.style.height = H + 'px';
ctx.setTransform(DPR,0,0,DPR,0,0);
}
new ResizeObserver(resize).observe(wrap); resize();
// ---------- controls state ----------
let BPM = 96, ENERGY = 1.0, AUTO = true, SOUND = false;
let queued = null, holdFreeze = false, scratchVel = 0, scratchAngle = 0;
const mouse = {x: 0.5, y: 0.4, inside:false};
const bpmEl = document.getElementById('bpm'), energyEl = document.getElementById('energy');
bpmEl.addEventListener('input', () => { BPM = +bpmEl.value; document.getElementById('bpmval').textContent = BPM + ' BPM'; });
energyEl.addEventListener('input', () => { ENERGY = +energyEl.value/100; document.getElementById('energyval').textContent = energyEl.value + '%'; });
document.getElementById('auto').addEventListener('change', e => { AUTO = e.target.checked; updateNextTag(); });
document.getElementById('sound').addEventListener('change', e => { SOUND = e.target.checked; if (SOUND) initAudio(); });
document.querySelectorAll('[data-move]').forEach(b => {
b.addEventListener('click', () => { queued = b.dataset.move; updateNextTag(); blipUI(); });
});
const holdBtn = document.getElementById('holdfreeze');
const setHold = v => { holdFreeze = v; holdBtn.classList.toggle('held', v); if (v && current.name !== 'beakfreeze') { queued = 'beakfreeze'; updateNextTag(); } };
holdBtn.addEventListener('pointerdown', e => { e.preventDefault(); setHold(true); });
['pointerup','pointerleave','pointercancel'].forEach(ev => holdBtn.addEventListener(ev, () => setHold(false)));
function updateNextTag(){ nextTagEl.textContent = 'next: ' + (queued ? label(queued) : (AUTO ? 'auto' : 'toprock groove')); }
function label(n){ return MOVES[n] ? MOVES[n].label : n; }
window.addEventListener('keydown', e => {
if (e.code === 'Space'){ e.preventDefault(); setHold(true); }
if (e.code === 'KeyN'){ queued = pickNext(); updateNextTag(); }
});
window.addEventListener('keyup', e => { if (e.code === 'Space') setHold(false); });
canvas.addEventListener('pointermove', e => {
const r = canvas.getBoundingClientRect();
mouse.x = (e.clientX - r.left)/r.width; mouse.y = (e.clientY - r.top)/r.height; mouse.inside = true;
});
canvas.addEventListener('pointerleave', () => mouse.inside = false);
canvas.addEventListener('pointerdown', e => {
const r = canvas.getBoundingClientRect();
spawnFeathers(e.clientX - r.left, e.clientY - r.top, 10);
cheer = 1; squashVel += 3.2; kickFlash = 1;
});
// vinyl scratch
const disc = document.getElementById('disc');
let discDrag = null;
function discAngle(e){
const r = disc.getBoundingClientRect();
return Math.atan2(e.clientY - (r.top + r.height/2), e.clientX - (r.left + r.width/2));
}
disc.addEventListener('pointerdown', e => { disc.setPointerCapture(e.pointerId); discDrag = {a: discAngle(e), t: performance.now(), v:0}; });
disc.addEventListener('pointermove', e => {
if (!discDrag) return;
const a = discAngle(e); let d = a - discDrag.a;
if (d > Math.PI) d -= Math.PI*2; if (d < -Math.PI) d += Math.PI*2;
const now = performance.now(), dt = Math.max(1, now - discDrag.t);
discDrag.v = d / dt * 1000; discDrag.a = a; discDrag.t = now;
scratchAngle += d; scratchVel = discDrag.v;
if (Math.abs(d) > 0.12 && Math.random() < .5) scratchTick();
});
disc.addEventListener('pointerup', () => {
if (discDrag && Math.abs(discDrag.v) > 5){ queued = 'backspin'; updateNextTag(); cheer = Math.max(cheer, .7); }
discDrag = null;
});
// ---------- tiny synth ----------
let AC = null, noiseBuf = null;
function initAudio(){
if (AC) { AC.resume(); return; }
try{
AC = new (window.AudioContext || window.webkitAudioContext)();
noiseBuf = AC.createBuffer(1, AC.sampleRate * .1, AC.sampleRate);
const d = noiseBuf.getChannelData(0);
for (let i=0;i<d.length;i++) d[i] = (Math.random()*2-1) * (1 - i/d.length);
}catch(e){ AC = null; }
}
function kick(){
if (!SOUND || !AC) return;
const t = AC.currentTime, o = AC.createOscillator(), g = AC.createGain();
o.type='sine'; o.frequency.setValueAtTime(150,t); o.frequency.exponentialRampToValueAtTime(42,t+.12);
g.gain.setValueAtTime(.5,t); g.gain.exponentialRampToValueAtTime(.001,t+.16);
o.connect(g).connect(AC.destination); o.start(t); o.stop(t+.18);
}
function hat(open){
if (!SOUND || !AC) return;
const t = AC.currentTime, s = AC.createBufferSource(), g = AC.createGain(), f = AC.createBiquadFilter();
s.buffer = noiseBuf; f.type='highpass'; f.frequency.value = 7000;
g.gain.setValueAtTime(.14,t); g.gain.exponentialRampToValueAtTime(.001,t+(open?.14:.05));
s.connect(f).connect(g).connect(AC.destination); s.start(t);
}
function scratchTick(){
if (!SOUND || !AC) return;
const t = AC.currentTime, s = AC.createBufferSource(), g = AC.createGain(), f = AC.createBiquadFilter();
s.buffer = noiseBuf; s.playbackRate.value = .6 + Math.random()*.8;
f.type='bandpass'; f.frequency.value = 900 + Math.random()*1600;
g.gain.setValueAtTime(.12,t); g.gain.exponentialRampToValueAtTime(.001,t+.07);
s.connect(f).connect(g).connect(AC.destination); s.start(t);
}
function blipUI(){ if (SOUND){ initAudio(); hat(false); } }
// ---------- pose / choreography ----------
const DEF = { x:0, y:-58, rot:0, sx:1, sy:1, hx:34, hy:-34, beak:.38, fL:[-25,57], fR:[27,57], wL:[-35,4], wR:[37,13], tail:-.25 };
const K = (t, o, ease) => Object.assign({t, ease: ease||'out'}, o);
const MOVES = {
toprock: { label:'Toprock', dur:4, impacts:[2.5], keys:[
K(0,{}), K(.5,{y:-66,fR:[46,50],fL:[-18,57],rot:-.05},'snap'),
K(1,{y:-46,fR:[8,57],fL:[-34,52],rot:.07,hx:28},'drop'),
K(1.5,{y:-64,fL:[-48,48],fR:[20,57],rot:-.06},'snap'),
K(2,{y:-88,fL:[-14,34],fR:[16,32],wL:[-44,-8],wR:[46,-6]},'up'),
K(2.5,{y:-40,fL:[-36,57],fR:[38,57],sx:1.14,sy:.82},'drop'),
K(3,{y:-60,fL:[-40,55],fR:[30,50],rot:.09},'back'),
K(3.5,{y:-52,fR:[48,54],rot:-.04},'snap'),
K(4,{})
]},
sixstep: { label:'6-Step', dur:6, impacts:[1,2,3,4,5], keys:[
K(0,{y:-34,fL:[-44,44],fR:[30,46],wL:[-40,34],wR:[30,20],hx:22,hy:-16,rot:-.12}),
K(1,{y:-30,fL:[-58,30],fR:[-6,48],wL:[-42,36],rot:.14,hx:16,hy:-10},'snap'),
K(2,{y:-32,fL:[-20,48],fR:[58,26],wR:[44,32],rot:-.16,hx:26,hy:-12},'snap'),
K(3,{y:-28,fL:[52,34],fR:[16,48],wL:[-44,30],rot:.18,hx:18,hy:-8},'snap'),
K(4,{y:-32,fL:[8,48],fR:[-56,30],wR:[42,34],rot:-.14,hx:28,hy:-12},'snap'),
K(5,{y:-36,fL:[-50,40],fR:[44,42],wL:[-38,32],rot:.1,hx:24,hy:-14},'snap'),
K(6,{y:-58})
]},
windmill: { label:'Windmill', dur:4, impacts:[1,2,3], keys:[
K(0,{y:-44,fL:[-40,50],fR:[36,50],hx:24,hy:-20},'in'),
K(.6,{y:-52,rot:-1.5,fL:[-50,10],fR:[40,-16],hx:10,hy:-30},'in'),
K(1.4,{y:-66,rot:-3.1,fL:[-56,-6],fR:[56,-10],hx:-6,hy:-34},'snap'),
K(2.2,{y:-54,rot:-4.7,fL:[-44,14],fR:[48,20],hx:8,hy:-28},'snap'),
K(3,{y:-64,rot:-6.28,fL:[-54,0],fR:[54,4],hx:20,hy:-32},'out'),
K(3.6,{y:-42,fL:[-36,56],fR:[38,56],sx:1.12,sy:.84,rot:-6.28},'drop'),
K(4,{rot:-6.28})
]},
flare: { label:'Flare', dur:4, impacts:[1,2,3], keys:[
K(0,{y:-36,wL:[-42,38],wR:[42,36],fL:[-46,42],fR:[46,42],hx:22,hy:-18}),
K(1,{y:-40,rot:-.38,fL:[-66,6],fR:[58,34],wL:[-44,38],wR:[40,38]},'snap'),
K(2,{y:-38,rot:.34,fL:[60,30],fR:[-64,4],hx:28},'snap'),
K(3,{y:-40,rot:-.3,fL:[-62,10],fR:[62,28]},'snap'),
K(4,{y:-58})
]},
backspin: { label:'Backspin', dur:3, impacts:[2.4], keys:[
K(0,{y:-44,fL:[-36,50],fR:[36,50]},'in'),
K(.5,{y:-36,rot:-.7,fL:[-12,26],fR:[14,24],hx:16,hy:-22,sx:.92,sy:.9},'in'),
K(1.8,{y:-40,rot:-7.2,fL:[-16,20],fR:[18,18],hx:10,hy:-20,sx:.9,sy:.88},'spin'),
K(2.4,{y:-38,rot:-9.4,fL:[-30,48],fR:[32,48],sx:1.15,sy:.8},'drop'),
K(3,{rot:-9.42})
]},
beakspin: { label:'Beak Spin', dur:4, impacts:[3.4], keys:[
K(0,{y:-42,fL:[-38,50],fR:[38,50],hx:26,hy:-18},'in'),
K(.7,{y:-96,rot:2.9,fL:[-30,50],fR:[34,50],wR:[44,20],hx:30,hy:-30,beak:.9},'up'),
K(2.8,{y:-98,rot:2.9-11.5,fL:[-52,44],fR:[54,40],hx:30,hy:-30,beak:.9},'spin'),
K(3.4,{y:-44,rot:2.9-12.57,fL:[-38,56],fR:[40,56],sx:1.16,sy:.8},'drop'),
K(4,{rot:2.9-12.57})
]},
beakfreeze: { label:'Beak Freeze ★', dur:3.6, freeze:[1,2.5], impacts:[2.9], keys:[
K(0,{y:-44,fL:[-38,52],fR:[38,52],hx:26,hy:-20},'in'),
K(.55,{y:-70,rot:.5,fL:[-20,40],fR:[24,36]},'up'),
K(1,{y:-100,rot:2.82,fL:[-56,38],fR:[30,58],wL:[-46,26],wR:[40,-6],hx:32,hy:-28,beak:.95},'back'),
K(2.5,{y:-100,rot:2.86,fL:[-58,36],fR:[34,60],wL:[-46,26],wR:[42,-8],hx:32,hy:-28,beak:.95},'hold'),
K(2.9,{y:-46,rot:3.6,fL:[-34,56],fR:[36,56],sx:1.15,sy:.82},'drop'),
K(3.6,{rot:6.28})
]},
recovery: { label:'…catching her breath', dur:2, impacts:[], keys:[
K(0,{y:-48,sx:1.08,sy:.92}),
K(.5,{y:-56,fR:[30,52]},'snap'),
K(1,{y:-50,fR:[27,57],hx:30,hy:-30}),
K(1.5,{y:-57,fL:[-28,53]},'snap'),
K(2,{})
]}
};
const ORDER = ['toprock','sixstep','windmill','toprock','flare','beakspin','sixstep','backspin','beakfreeze','windmill','flare'];
let orderIdx = 0;
function pickNext(){
if (!AUTO && !queued) return 'toprock';
orderIdx = (orderIdx + 1 + Math.floor(Math.random()*2)) % ORDER.length;
let n = ORDER[orderIdx];
if (n === current.name) n = ORDER[(orderIdx+1)%ORDER.length];
return n;
}
let current = { name:'toprock', t:0 };
let totalBeats = 0, lastBeatInt = -1, squash = 0, squashVel = 0, cheer = 0, kickFlash = 0;
let prevRot = 0, rotVel = 0, featherLag = 0;
const firedImpacts = new Set();
function ease(name, u){
u = Math.min(1, Math.max(0, u));
switch(name){
case 'in': return u*u;
case 'drop': return u*u*u;
case 'up': return 1-Math.pow(1-u,3);
case 'snap': return 1-Math.pow(1-u,4);
case 'back': { const c=1.9; return 1 + (c+1)*Math.pow(u-1,3) + c*Math.pow(u-1,2); }
case 'spin': return 1-Math.pow(1-u,3); // fast launch, momentum decay
case 'hold': return u<.5 ? u*1.4 : .7 + (u-.5)*.6;
default: return 1-Math.pow(1-u,3);
}
}
const lerp = (a,b,u) => a+(b-a)*u;
function samplePose(name, t){
const mv = MOVES[name], keys = mv.keys;
let a = keys[0], b = keys[keys.length-1];
for (let i=0;i<keys.length-1;i++){ if (t >= keys[i].t && t <= keys[i+1].t){ a=keys[i]; b=keys[i+1]; break; } }
if (t <= keys[0].t){ a=b=keys[0]; }
const span = Math.max(.0001, b.t-a.t), u = ease(b.ease, (t-a.t)/span);
const P = {};
for (const k of ['x','y','rot','sx','sy','hx','hy','beak','tail']){
P[k] = lerp(a[k]!==undefined?a[k]:DEF[k], b[k]!==undefined?b[k]:DEF[k], u);
}
for (const k of ['fL','fR','wL','wR']){
const A = a[k]||DEF[k], B = b[k]||DEF[k];
P[k] = [lerp(A[0],B[0],u), lerp(A[1],B[1],u)];
}
return P;
}
// ---------- particles ----------
const dust = [], feathersP = [];
function spawnDust(x,y,n,power){
for (let i=0;i<n;i++) dust.push({x:x+(Math.random()-.5)*30, y, vx:(Math.random()-.5)*90*power, vy:-Math.random()*55*power, r:2+Math.random()*4.5, life:1});
}
function spawnFeathers(x,y,n){
for (let i=0;i<n;i++) feathersP.push({x,y,vx:(Math.random()-.5)*160,vy:-40-Math.random()*130,rot:Math.random()*6,vr:(Math.random()-.5)*8,life:1,hue:Math.random()<.3?'#e9a13b':'#8a6844'});
}
// seeded feather speckles on the body
let seed = 7; const rnd = () => (seed = (seed*16807)%2147483647) / 2147483647;
const speckles = [];
for (let i=0;i<80;i++){ const a=rnd()*Math.PI*2, rr=Math.sqrt(rnd()); speckles.push({x:Math.cos(a)*rr*46, y:Math.sin(a)*rr*30, s:2+rnd()*3.4, a:rnd()*3}); }
// crowd birds
const crowd = [];
for (let i=0;i<9;i++) crowd.push({x:.06+i*.11, side:i%2, ph:Math.random()*6, col:['#2b1c12','#1f7a6d','#d94f30','#6b4a2f'][i%4]});
// ---------- drawing ----------
function leg(hip, foot, col){
const mx=(hip[0]+foot[0])/2, my=(hip[1]+foot[1])/2;
const dx=foot[0]-hip[0], dy=foot[1]-hip[1], len=Math.hypot(dx,dy)||1;
const knee=[mx + (-dy/len)*10, my + (dx/len)*6 + 4];
ctx.strokeStyle=col; ctx.lineWidth=8.5; ctx.lineCap='round'; ctx.lineJoin='round';
ctx.beginPath(); ctx.moveTo(hip[0],hip[1]); ctx.lineTo(knee[0],knee[1]); ctx.lineTo(foot[0],foot[1]); ctx.stroke();
ctx.lineWidth=3.4;
for (const a of [-0.5,0,0.5]){ ctx.beginPath(); ctx.moveTo(foot[0],foot[1]); ctx.lineTo(foot[0]+Math.cos(a+.4)*11, foot[1]+3+Math.sin(a)*3); ctx.stroke(); }
}
function wing(sh, tip, col, flip){
ctx.fillStyle=col;
ctx.beginPath();
ctx.moveTo(sh[0], sh[1]-6);
ctx.quadraticCurveTo((sh[0]+tip[0])/2 + (flip?-9:9), (sh[1]+tip[1])/2, tip[0], tip[1]);
ctx.quadraticCurveTo((sh[0]+tip[0])/2 + (flip?5:-5), (sh[1]+tip[1])/2 + 12, sh[0], sh[1]+8);
ctx.closePath(); ctx.fill();
}
function drawBird(p, groundY, cx){
const spinning = Math.abs(rotVel) > 3.2;
ctx.save();
ctx.translate(cx + p.x, groundY + p.y);
ctx.rotate(p.rot);
ctx.scale(p.sx * (1+squash*.5), p.sy * (1-squash*.6));
if (spinning){
ctx.strokeStyle='rgba(43,28,18,.18)'; ctx.lineWidth=3;
for (let i=0;i<3;i++){ ctx.beginPath(); ctx.arc(0,0,58+i*9, totalBeats*4+i*2, totalBeats*4+i*2+2.2); ctx.stroke(); }
}
// tail feathers (lag behind rotation)
ctx.strokeStyle='#54382a'; ctx.lineWidth=5; ctx.lineCap='round';
for (let i=-1;i<=1;i++){
ctx.beginPath(); ctx.moveTo(-42, 8+i*5);
ctx.quadraticCurveTo(-66, 4+i*10 + featherLag*14, -78, -4+i*13 + featherLag*22); ctx.stroke();
}
leg([-16,26], p.fL, '#7d5a38'); // far leg
wing([-8,-4], p.wL, '#54382a', true); // far wing
// body
ctx.fillStyle='#6b4a2f';
ctx.beginPath(); ctx.ellipse(0,0,54,40,0,0,Math.PI*2); ctx.fill();
ctx.fillStyle='#86603c';
ctx.beginPath(); ctx.ellipse(6,10,38,26,.2,0,Math.PI*2); ctx.fill();
ctx.fillStyle='rgba(43,28,18,.35)';
for (const s of speckles){ ctx.save(); ctx.translate(s.x,s.y); ctx.rotate(s.a); ctx.beginPath(); ctx.ellipse(0,0,s.s,s.s*.45,0,0,Math.PI*2); ctx.fill(); ctx.restore(); }
leg([18,26], p.fR, '#966f42'); // near leg
wing([6,0], p.wR, '#7d5a38', false); // near wing
// head
const hx=p.hx, hy=p.hy;
ctx.fillStyle='#77552f';
ctx.beginPath(); ctx.arc(hx,hy,21,0,Math.PI*2); ctx.fill();
ctx.strokeStyle='#54382a'; ctx.lineWidth=3.5;
for (let i=0;i<3;i++){ ctx.beginPath(); ctx.moveTo(hx-6+i*6, hy-18); ctx.quadraticCurveTo(hx-2+i*6, hy-27 - (i%2)*3 + featherLag*8, hx+4+i*5, hy-24); ctx.stroke(); }
// beak — long, slightly down-curved, nostril at the tip like a real kiwi
ctx.save(); ctx.translate(hx+14, hy+3); ctx.rotate(p.beak);
ctx.fillStyle='#e8d9b0';
ctx.beginPath(); ctx.moveTo(0,-4.5); ctx.quadraticCurveTo(34,-2.5, 52, 3.5); ctx.quadraticCurveTo(34, 4.5, 0, 4.5); ctx.closePath(); ctx.fill();
ctx.fillStyle='#2b1c12'; ctx.beginPath(); ctx.arc(47,2.6,1.8,0,Math.PI*2); ctx.fill();
ctx.restore();
// eye — follows your mouse
const px = mouse.inside ? (mouse.x-.5)*5 : Math.sin(totalBeats*.7)*2;
const py = mouse.inside ? (mouse.y-.5)*4 : 0;
ctx.fillStyle='#fff8e8'; ctx.beginPath(); ctx.arc(hx+7,hy-4,6.4,0,Math.PI*2); ctx.fill();
ctx.fillStyle='#1c120a'; ctx.beginPath(); ctx.arc(hx+7+px,hy-4+py,3.2,0,Math.PI*2); ctx.fill();
ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(hx+8.4+px,hy-5.4+py,1.1,0,Math.PI*2); ctx.fill();
// determined brow
ctx.strokeStyle='#2b1c12'; ctx.lineWidth=2.6;
ctx.beginPath(); ctx.moveTo(hx,hy-12); ctx.lineTo(hx+13,hy-9.5); ctx.stroke();
ctx.restore();
}
function draw(now){
ctx.clearRect(0,0,W,H);
const groundY = H*.72, cx = W*.46;
// sunburst
ctx.save(); ctx.translate(cx, groundY-70); ctx.rotate(Math.sin(totalBeats*.12)*.05);
for (let i=0;i<16;i++){ ctx.fillStyle = i%2 ? 'rgba(233,161,59,.10)' : 'rgba(217,79,48,.07)';
ctx.beginPath(); ctx.moveTo(0,0); ctx.arc(0,0, Math.max(W,H), i*Math.PI/8, (i+.55)*Math.PI/8); ctx.closePath(); ctx.fill(); }
ctx.restore();
// wall scribble
ctx.fillStyle='rgba(43,28,18,.75)'; ctx.font='900 13px Trebuchet MS'; ctx.save();
ctx.translate(16,30); ctx.rotate(-.03); ctx.fillText('NO FLYING. JUST BREAKIN’.',0,0); ctx.restore();
// cardboard mat
ctx.fillStyle='#c99a5e'; ctx.strokeStyle='#2b1c12'; ctx.lineWidth=3;
ctx.beginPath();
ctx.moveTo(cx-230, groundY+8); ctx.lineTo(cx+230, groundY+8); ctx.lineTo(cx+268, groundY+66); ctx.lineTo(cx-268, groundY+66); ctx.closePath();
ctx.fill(); ctx.stroke();
ctx.strokeStyle='rgba(43,28,18,.25)'; ctx.lineWidth=1.5;
for (let i=-3;i<=3;i++){ ctx.beginPath(); ctx.moveTo(cx+i*62, groundY+10); ctx.lineTo(cx+i*70, groundY+64); ctx.stroke(); }
ctx.fillStyle='#e9dcc0'; // tape corners
for (const s of [-1,1]){ ctx.save(); ctx.translate(cx+s*238, groundY+8); ctx.rotate(s*.5); ctx.fillRect(-22,-7,44,14); ctx.strokeRect(-22,-7,44,14); ctx.restore(); }
ctx.strokeStyle='rgba(217,79,48,.5)'; ctx.lineWidth=2.5;
ctx.beginPath(); ctx.ellipse(cx, groundY+34, 168, 20, 0, 0, Math.PI*2); ctx.stroke();
// boombox
const bb = 1 + kickFlash*.06;
ctx.save(); ctx.translate(52, groundY-38); ctx.scale(bb,bb); ctx.rotate(-.03);
ctx.fillStyle='#2b1c12'; ctx.fillRect(-38,-26,76,52);
ctx.fillStyle='#f3e6cf'; ctx.fillRect(-34,-22,68,44);
for (const s of [-1,1]){ ctx.fillStyle='#2b1c12'; ctx.beginPath(); ctx.arc(s*18,4,12+kickFlash*2,0,Math.PI*2); ctx.fill();
ctx.fillStyle='#d94f30'; ctx.beginPath(); ctx.arc(s*18,4,5,0,Math.PI*2); ctx.fill(); }
ctx.fillStyle='#1f7a6d'; ctx.fillRect(-26,-19,52,9);
ctx.restore();
// crowd birds
for (const c of crowd){
const bx = c.x * W, bob = Math.abs(Math.sin(totalBeats*Math.PI + c.ph)) * (5 + cheer*13);
const by = H - 14 - bob;
ctx.fillStyle=c.col;
ctx.beginPath(); ctx.arc(bx, by, 15, Math.PI, 0); ctx.fill();
ctx.beginPath(); ctx.arc(bx+4, by-13, 8.5, 0, Math.PI*2); ctx.fill();
ctx.fillStyle='#e9a13b';
ctx.beginPath(); ctx.moveTo(bx+11,by-14); ctx.lineTo(bx+21,by-11); ctx.lineTo(bx+11,by-8); ctx.closePath(); ctx.fill();
ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(bx+6,by-15,2.4,0,Math.PI*2); ctx.fill();
}
// current pose (+ secondary motion)
let p = samplePose(current.name, current.t);
const mv = MOVES[current.name];
// energy scales height & travel
p.y = -58 + (p.y + 58) * (p.y < -58 ? ENERGY : .7 + ENERGY*.3);
p.x *= ENERGY;
// freeze wobble — balance is hard on a beak
if (mv.freeze && current.t > mv.freeze[0] && current.t < mv.freeze[1]){
p.rot += Math.sin(now*.012)*.022 + Math.sin(now*.031)*.01;
p.y += Math.sin(now*.02)*1.2;
}
if (current.name === 'recovery'){ p.sy += Math.sin(now*.006)*.025; p.y += Math.sin(now*.006)*1.6; } // breathing
if (current.name === 'toprock' || current.name === 'recovery') p.y -= Math.abs(Math.sin(totalBeats*Math.PI))*3.5;
// shadow
const airH = Math.max(0, (groundY + p.y) - (groundY - 58));
const shScale = Math.max(.45, 1 - Math.abs(p.y+58)/260);
ctx.fillStyle='rgba(43,28,18,.28)';
ctx.beginPath(); ctx.ellipse(cx + p.x, groundY+16, 62*shScale, 11*shScale, 0, 0, Math.PI*2); ctx.fill();
drawBird(p, groundY, cx);
// particles
for (let i=dust.length-1;i>=0;i--){ const d=dust[i]; d.x+=d.vx*.016; d.y+=d.vy*.016; d.vy+=140*.016; d.life-=.03;
if (d.life<=0){ dust.splice(i,1); continue; }
ctx.fillStyle=`rgba(160,125,85,${d.life*.55})`; ctx.beginPath(); ctx.arc(d.x,d.y,d.r,0,Math.PI*2); ctx.fill(); }
for (let i=feathersP.length-1;i>=0;i--){ const f=feathersP[i]; f.x+=f.vx*.016; f.y+=f.vy*.016; f.vy+=190*.016; f.rot+=f.vr*.016; f.life-=.014;
if (f.life<=0 || f.y > H){ feathersP.splice(i,1); continue; }
ctx.save(); ctx.translate(f.x,f.y); ctx.rotate(f.rot); ctx.globalAlpha=Math.min(1,f.life);
ctx.fillStyle=f.hue; ctx.beginPath(); ctx.ellipse(0,0,7,3,0,0,Math.PI*2); ctx.fill(); ctx.restore(); ctx.globalAlpha=1; }
// scratch streaks
if (Math.abs(scratchVel) > 1.2){
ctx.strokeStyle='rgba(31,122,109,.5)'; ctx.lineWidth=2.5;
for (let i=0;i<3;i++){ ctx.beginPath(); ctx.arc(cx, groundY-60, 84+i*13, now*.01+i, now*.01+i+1.1); ctx.stroke(); }
}
disc.style.transform = `rotate(${scratchAngle + totalBeats*.9}rad)`;
}
// ---------- main loop ----------
let last = performance.now();
function frame(now){
let dt = Math.min(.05, (now-last)/1000); last = now;
const frozen = holdFreeze && MOVES[current.name].freeze && current.t >= MOVES[current.name].freeze[0]-.05 && current.t <= MOVES[current.name].freeze[1]+.4;
const beatDt = frozen ? 0 : dt * BPM/60;
totalBeats += beatDt;
current.t += beatDt;
// beat events
const bi = Math.floor(totalBeats);
if (bi !== lastBeatInt){
lastBeatInt = bi;
beatDots.forEach((d,i)=>{ d.className = (i === bi%4) ? ('on' + (i%2?' b2':'')) : ''; });
kick(); if (bi%2===1) hat(bi%4===3);
kickFlash = 1;
}
kickFlash *= .88; cheer *= .985;
scratchVel *= .94;
// impacts -> dust + squash impulse (this is where the weight lands)
const mv = MOVES[current.name];
for (const im of mv.impacts){
const key = current.name + ':' + im;
if (current.t >= im && !firedImpacts.has(key)){
firedImpacts.add(key);
const p = samplePose(current.name, current.t);
spawnDust(W*.46 + p.x, H*.72 + 12, 9, ENERGY);
squashVel -= 2.6; hat(true);
if (current.name==='beakfreeze' || current.name==='beakspin') spawnFeathers(W*.46+p.x, H*.72-40, 5);
}
}
// move finished -> recovery, then next
if (current.t >= mv.dur){
firedImpacts.clear();
let next;
if (current.name === 'recovery'){ next = queued || pickNext(); queued = null; }
else if (current.name === 'beakfreeze' || current.name === 'windmill' || current.name === 'beakspin') next = 'recovery';
else if (queued){ next = queued; queued = null; }
else next = 'recovery';
current = { name: next, t: 0 };
moveNameEl.textContent = label(next);
updateNextTag();
if (next !== 'recovery' && next !== 'toprock') cheer = Math.max(cheer, .45);
}
// squash spring (overshoot & settle)
squashVel += (-squash*90 - squashVel*9) * dt;
squash += squashVel * dt;
// rotation velocity -> feather lag
const pNow = samplePose(current.name, current.t);
rotVel = (pNow.rot - prevRot) / Math.max(dt,.001); prevRot = pNow.rot;
featherLag += ((Math.max(-1.4, Math.min(1.4, -rotVel*.16))) - featherLag) * Math.min(1, dt*7);
draw(now);
requestAnimationFrame(frame);
}
moveNameEl.textContent = label(current.name);
updateNextTag();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>
Space) freezes time mid-beak-freeze, the tempo and energy sliders change the beat clock and how high/wide everything travels, auto-choreography can be handed over to manual requests, the optional kick-and-hat synth locks to the tempo, dragging the vinyl scratches and a hard flick cues a backspin, and clicking the stage hypes the crowd while her eyes follow your cursor everywhere.