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 chunky street pigeon in a backwards cap — cream body, slate wings, orange beak and feet — built from a small IK skeleton so its wings and legs actually plant on the floor instead of floating. The look is a dark basement jam: warm spotlight, cardboard mat, halftone dots, and a bird that commits to every move.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<title>BIRD // BREAKS</title>
<style>
:root{
--ink:#e8e3d9;
--dim:#6f6885;
--hot:#ffb347;
--bg:#08070d;
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%;overflow:hidden;background:var(--bg)}
body{
font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
color:var(--ink);
-webkit-font-smoothing:antialiased;
user-select:none;
-webkit-user-select:none;
}
#stage{position:fixed;inset:0;display:block;touch-action:none}
.hud{
position:fixed;top:0;left:0;right:0;
padding:20px 24px;
display:flex;justify-content:space-between;align-items:flex-start;
pointer-events:none;z-index:5;
}
.brand{
font-size:12px;letter-spacing:.34em;color:var(--ink);opacity:.8;
}
.brand span{color:var(--hot);opacity:1}
.move{text-align:right}
.move b{
display:block;font-size:11px;font-weight:500;letter-spacing:.26em;
color:var(--hot);
}
.move small{
display:block;margin-top:5px;font-size:9px;letter-spacing:.2em;color:var(--dim);
}
.dock{
position:fixed;left:50%;bottom:52px;transform:translateX(-50%);
display:flex;align-items:center;gap:14px;
padding:9px 16px;
background:rgba(14,11,22,.8);
border:1px solid rgba(255,255,255,.09);
border-radius:999px;
backdrop-filter:blur(10px);
-webkit-backdrop-filter:blur(10px);
box-shadow:0 12px 44px rgba(0,0,0,.55);
z-index:10;
white-space:nowrap;
}
.btn{
font:inherit;font-size:9.5px;letter-spacing:.18em;
color:var(--ink);
background:rgba(255,255,255,.05);
border:1px solid rgba(255,255,255,.10);
border-radius:999px;
padding:7px 11px;
cursor:pointer;
transition:background .15s,color .15s,border-color .15s;
}
.btn:hover{background:rgba(255,255,255,.11)}
.btn.on{
background:rgba(255,179,71,.16);
border-color:rgba(255,179,71,.55);
color:var(--hot);
}
.grp{display:flex;align-items:center;gap:8px}
.lbl{font-size:9px;letter-spacing:.2em;color:var(--dim)}
.val{font-size:9.5px;letter-spacing:.1em;color:var(--hot);width:22px;text-align:right}
input[type=range]{
-webkit-appearance:none;appearance:none;
width:96px;height:16px;background:transparent;cursor:pointer;
}
input[type=range]::-webkit-slider-runnable-track{
height:2px;background:rgba(255,255,255,.16);border-radius:2px;
}
input[type=range]::-webkit-slider-thumb{
-webkit-appearance:none;appearance:none;
width:11px;height:11px;border-radius:50%;
background:var(--hot);margin-top:-4.5px;
box-shadow:0 0 10px rgba(255,179,71,.6);
}
input[type=range]::-moz-range-track{
height:2px;background:rgba(255,255,255,.16);border-radius:2px;
}
input[type=range]::-moz-range-thumb{
width:11px;height:11px;border:none;border-radius:50%;
background:var(--hot);
}
.sw{display:flex;gap:7px;align-items:center}
.sw button{
width:17px;height:17px;border-radius:50%;padding:0;cursor:pointer;
border:2px solid rgba(255,255,255,.14);
transition:transform .15s,border-color .15s;
}
.sw button.on{border-color:var(--hot);transform:scale(1.18)}
.tl{
position:fixed;left:0;right:0;bottom:0;height:24px;
background:rgba(255,255,255,.025);
cursor:pointer;z-index:11;
border-top:1px solid rgba(255,255,255,.05);
}
.tlfill{
position:absolute;left:0;top:0;bottom:0;width:0%;
background:linear-gradient(90deg,rgba(255,179,71,.18),rgba(255,179,71,.5));
pointer-events:none;
}
.tlmarks{position:absolute;inset:0;pointer-events:none}
.tlmarks i{
position:absolute;top:0;bottom:0;width:1px;
background:rgba(255,255,255,.13);
}
.tlmarks i::after{
content:attr(data-n);
position:absolute;left:4px;top:4px;
font-size:7.5px;letter-spacing:.14em;color:rgba(255,255,255,.28);
font-style:normal;white-space:nowrap;
}
@media (max-width:720px){
.dock{gap:9px;padding:8px 11px;bottom:44px}
input[type=range]{width:64px}
.tlmarks i::after{display:none}
}
</style>
</head>
<body>
<canvas id="stage"></canvas>
<div class="hud">
<div class="brand">BIRD<span>//</span>BREAKS</div>
<div class="move"><b id="moveName">TOPROCK</b><small id="moveSub">BEAT 1 / 20</small></div>
</div>
<div class="dock">
<button id="play" class="btn" title="Play / pause (space)">❚❚</button>
<div class="grp">
<span class="lbl">BPM</span>
<input id="bpm" type="range" min="70" max="140" step="1" value="96">
<span class="val" id="bpmv">96</span>
</div>
<button id="slow" class="btn" title="Slow motion">SLOW</button>
<div class="sw" id="sw"></div>
<button id="crowd" class="btn" title="Bring the crowd in">CROWD</button>
</div>
<div class="tl" id="tl">
<div class="tlfill" id="tlfill"></div>
<div class="tlmarks" id="tlmarks"></div>
</div>
<script>
(function(){
'use strict';
/* ============================================================
BIRD // BREAKS
A 20-beat breaking routine, authored as keyframes on a beat
grid, driven by a 2-bone IK skeleton so limbs plant on the mat.
============================================================ */
var cv = document.getElementById('stage');
var ctx = cv.getContext('2d');
var TAU = Math.PI * 2;
/* ---------- skeleton proportions (world units) ---------- */
var TORSO = 42;
var HEAD_OFF = 22;
var HEAD_R = 14;
var W1 = 26, W2 = 26; // wing upper / lower
var L1 = 26, L2 = 26; // leg upper / lower
var TOTAL = 20; // beats in the routine
/* ---------- palettes ---------- */
var PALS = [
{ name:'PIGEON',
body:'#e9e4d8', body2:'#cbc3b2', belly:'#f8f4ea',
wing:'#9c968a', wing2:'#7c766c', tip:'#615d55',
beak:'#f2a03d', leg:'#e08a3c', cap:'#e0453f', cap2:'#a82f2b',
eye:'#fbf8f0', pupil:'#171320', line:'#2b2733', sw:'#e9e4d8' },
{ name:'CARDINAL',
body:'#e8453c', body2:'#c22f2a', belly:'#f26a5f',
wing:'#a8241f', wing2:'#7d1a17', tip:'#5c1210',
beak:'#f7c948', leg:'#f7c948', cap:'#2b2b3a', cap2:'#191924',
eye:'#fbf8f0', pupil:'#171320', line:'#3a1210', sw:'#e8453c' },
{ name:'CROW',
body:'#4a4a62', body2:'#33334a', belly:'#5a5a76',
wing:'#24243a', wing2:'#181828', tip:'#0e0e18',
beak:'#a2a2bc', leg:'#a2a2bc', cap:'#ffb347', cap2:'#c97f2a',
eye:'#fbf8f0', pupil:'#171320', line:'#141420', sw:'#4a4a62' },
{ name:'TOUCAN',
body:'#243044', body2:'#18202e', belly:'#2e3e54',
wing:'#101822', wing2:'#0a1018', tip:'#060a10',
beak:'#ff8a3d', leg:'#4fc3a1', cap:'#f7e04b', cap2:'#c9b32c',
eye:'#fbf8f0', pupil:'#171320', line:'#0a1018', sw:'#243044' }
];
/* ---------- state ---------- */
var W=0,H=0,DPR=1,S=1,OX=0,OY=0;
var playing = true, bpm = 96, slowmo = false, crowdOn = false, palIdx = 0;
var beat = 0, prevBeat = 0, last = performance.now();
var parts = [];
var bg = document.createElement('canvas');
/* ============================================================
EASING
============================================================ */
var EASE = {
lin: function(t){ return t; },
io: function(t){ return t<0.5 ? 2*t*t : 1-Math.pow(-2*t+2,2)/2; },
in: function(t){ return t*t*t; },
out: function(t){ return 1-Math.pow(1-t,3); },
snap: function(t){ return 1-Math.pow(1-t,5); },
back: function(t){
var c1=1.70158, c3=c1+1;
return 1 + c3*Math.pow(t-1,3) + c1*Math.pow(t-1,2);
}
};
/* ============================================================
POSE DEFINITION
All limb targets are WORLD positions. The IK solves the rest,
so a planted foot stays planted while the hips travel.
============================================================ */
var STAND = {
x:0, y:-50, rot:0,
hx:0, hy:0, hr:0,
fL:{x:-9,y:0}, fR:{x:9,y:0},
hL:{x:-7,y:-42}, hR:{x:7,y:-42},
tail:0.3, wob:0.35, blink:0, beak:0
};
var KF = [];
function K(t, o, e){
var p = {};
for (var k in STAND) p[k] = STAND[k];
if (o) for (var k2 in o) p[k2] = o[k2];
KF.push({ t:t, p:p, e:e || 'io' });
}
/* ------------------------------------------------------------
SECTION 1 — TOPROCK (beats 0 – 3.5)
Weight shifts side to side. Each step: lift, plant, settle.
------------------------------------------------------------ */
K(0.0, {}, 'lin');
K(0.5, { x:-12, y:-44, rot:-0.10, hr:0.10,
fL:{x:-22,y:0}, fR:{x:10,y:-24},
hL:{x:-18,y:-32}, hR:{x:16,y:-48}, wob:0.15 }, 'out');
K(1.0, { x:-12, y:-44, rot:-0.05,
fL:{x:-22,y:0}, fR:{x:14,y:0},
hL:{x:-16,y:-34}, hR:{x:8,y:-38}, wob:0.2 }, 'back');
K(1.5, { x:0, y:-46, rot:0.02,
fL:{x:-14,y:0}, fR:{x:14,y:0},
hL:{x:-8,y:-38}, hR:{x:8,y:-38}, wob:0.3 }, 'io');
K(2.0, { x:12, y:-44, rot:0.10, hr:-0.10,
fR:{x:22,y:0}, fL:{x:-10,y:-24},
hR:{x:18,y:-32}, hL:{x:-16,y:-48}, wob:0.15 }, 'out');
K(2.5, { x:12, y:-44, rot:0.05,
fR:{x:22,y:0}, fL:{x:-14,y:0},
hR:{x:16,y:-34}, hL:{x:-8,y:-38}, wob:0.2 }, 'back');
K(3.0, { x:0, y:-46, rot:-0.02,
fL:{x:-14,y:0}, fR:{x:14,y:0},
hL:{x:-8,y:-38}, hR:{x:8,y:-38}, wob:0.3 }, 'io');
/* anticipation: sink before the drop */
K(3.5, { x:0, y:-34, rot:0.28, hr:0.18,
fL:{x:-12,y:0}, fR:{x:12,y:0},
hL:{x:-8,y:-28}, hR:{x:14,y:-28}, wob:0.1 }, 'in');
/* ------------------------------------------------------------
SECTION 2 — DROP + FOOTWORK (beats 4 – 7)
Hands hit the mat, legs sweep front/back like a CC.
------------------------------------------------------------ */
K(4.0, { x:0, y:-30, rot:0.95,
fL:{x:-16,y:-6}, fR:{x:18,y:-10},
hL:{x:28,y:-6}, hR:{x:40,y:-6}, wob:0.05 }, 'in');
K(4.5, { x:0, y:-28, rot:1.00,
fL:{x:-14,y:-8}, fR:{x:46,y:-14},
hL:{x:26,y:-6}, hR:{x:40,y:-6}, wob:0.05 }, 'out');
K(5.0, { x:0, y:-28, rot:1.00,
fR:{x:-14,y:-8}, fL:{x:46,y:-14},
hL:{x:26,y:-6}, hR:{x:40,y:-6}, wob:0.05 }, 'io');
K(5.5, { x:0, y:-28, rot:1.00,
fL:{x:-14,y:-8}, fR:{x:46,y:-14},
hL:{x:26,y:-6}, hR:{x:40,y:-6}, wob:0.05 }, 'io');
K(6.0, { x:0, y:-28, rot:1.00,
fR:{x:-14,y:-8}, fL:{x:46,y:-14},
hL:{x:26,y:-6}, hR:{x:40,y:-6}, wob:0.05 }, 'io');
K(6.5, { x:0, y:-26, rot:1.05,
fL:{x:-6,y:-14}, fR:{x:10,y:-16},
hL:{x:24,y:-8}, hR:{x:38,y:-8}, wob:0.1 }, 'io');
/* ------------------------------------------------------------
SECTION 3 — WIND-UP + BABY FREEZE (beats 7 – 10)
------------------------------------------------------------ */
K(7.0, { x:-6, y:-28, rot:1.35,
fL:{x:-24,y:-20}, fR:{x:4,y:-24},
hL:{x:10,y:-30}, hR:{x:44,y:0}, wob:0.1 }, 'io');
K(7.5, { x:-10, y:-30, rot:1.80,
fL:{x:-30,y:-34}, fR:{x:-18,y:-42},
hL:{x:6,y:-30}, hR:{x:42,y:0}, wob:0.1 }, 'out');
K(8.0, { x:-10, y:-32, rot:1.85,
fL:{x:-32,y:-36}, fR:{x:-20,y:-44},
hL:{x:4,y:-32}, hR:{x:42,y:0}, wob:0.5 }, 'back');
K(8.5, { x:-10, y:-31, rot:1.83,
fL:{x:-31,y:-35}, fR:{x:-19,y:-43},
hL:{x:5,y:-31}, hR:{x:42,y:0}, wob:0.8 }, 'io');
K(9.5, { x:-10, y:-31, rot:1.83,
fL:{x:-31,y:-35}, fR:{x:-19,y:-43},
hL:{x:5,y:-31}, hR:{x:42,y:0}, wob:1.0 }, 'io');
K(10.0, { x:-10, y:-31, rot:1.83,
fL:{x:-31,y:-35}, fR:{x:-19,y:-43},
hL:{x:5,y:-31}, hR:{x:42,y:0}, wob:1.0 }, 'io');
/* ------------------------------------------------------------
SECTION 4 — HEADSTAND (beats 10.5 – 12.5)
------------------------------------------------------------ */
K(10.5, { x:0, y:-66, rot:2.60,
fL:{x:-24,y:-84}, fR:{x:6,y:-90},
hL:{x:8,y:-22}, hR:{x:34,y:0}, wob:0.2 }, 'in');
K(11.0, { x:0, y:-78, rot:Math.PI,
fL:{x:-22,y:-124}, fR:{x:22,y:-124},
hL:{x:-26,y:-20}, hR:{x:26,y:-20}, wob:0.3 }, 'out');
K(11.5, { x:0, y:-78, rot:Math.PI,
fL:{x:-36,y:-114}, fR:{x:36,y:-114},
hL:{x:-30,y:-16}, hR:{x:30,y:-16}, wob:0.6 }, 'io');
K(12.0, { x:0, y:-78, rot:Math.PI,
fL:{x:-36,y:-114}, fR:{x:36,y:-114},
hL:{x:-30,y:-16}, hR:{x:30,y:-16}, wob:1.0 }, 'io');
K(12.5, { x:0, y:-78, rot:Math.PI,
fL:{x:-36,y:-114}, fR:{x:36,y:-114},
hL:{x:-30,y:-16}, hR:{x:30,y:-16}, wob:1.0 }, 'io');
/* ------------------------------------------------------------
SECTION 5 — FALL OUT (beats 13 – 14.5)
------------------------------------------------------------ */
K(13.0, { x:8, y:-62, rot:2.50,
fL:{x:-24,y:-96}, fR:{x:26,y:-88},
hL:{x:-6,y:-30}, hR:{x:34,y:-6}, wob:0.2 }, 'in');
K(13.5, { x:14, y:-34, rot:1.50,
fL:{x:-14,y:-6}, fR:{x:30,y:-10},
hL:{x:18,y:-8}, hR:{x:44,y:0}, wob:0.15 }, 'in');
K(14.0, { x:8, y:-30, rot:0.60,
fL:{x:-14,y:0}, fR:{x:26,y:0},
hL:{x:0,y:-24}, hR:{x:26,y:-16}, wob:0.1 }, 'io');
/* ------------------------------------------------------------
SECTION 6 — POP UP (beats 14.5 – 16.5)
------------------------------------------------------------ */
K(14.5, { x:4, y:-26, rot:0.35,
fL:{x:-12,y:0}, fR:{x:20,y:0},
hL:{x:-4,y:-24}, hR:{x:22,y:-22}, wob:0.05 }, 'in');
K(15.0, { x:0, y:-64, rot:-0.15,
fL:{x:-10,y:-16}, fR:{x:10,y:-16},
hL:{x:-26,y:-96}, hR:{x:26,y:-96}, wob:0.1 }, 'out');
K(15.5, { x:0, y:-50, rot:0.05,
fL:{x:-12,y:0}, fR:{x:12,y:0},
hL:{x:-14,y:-46}, hR:{x:14,y:-46}, wob:0.3 }, 'back');
/* ------------------------------------------------------------
SECTION 7 — SWAGGER (beats 16.5 – 20)
------------------------------------------------------------ */
K(16.5, { x:-4, y:-48, rot:-0.12, hr:0.15,
fL:{x:-16,y:0}, fR:{x:10,y:0},
hL:{x:-24,y:-58}, hR:{x:6,y:-40}, wob:0.4 }, 'io');
K(17.0, { x:6, y:-48, rot:0.10, hr:-0.10,
fL:{x:-10,y:0}, fR:{x:18,y:0},
hL:{x:-6,y:-40}, hR:{x:34,y:-62}, wob:0.4 }, 'out');
K(17.5, { x:6, y:-48, rot:0.10, hr:-0.10,
fL:{x:-10,y:0}, fR:{x:18,y:0},
hL:{x:-6,y:-40}, hR:{x:34,y:-62}, wob:0.7 }, 'io');
K(18.0, { x:0, y:-42, rot:0.00,
fL:{x:-14,y:0}, fR:{x:14,y:0},
hL:{x:-10,y:-34}, hR:{x:10,y:-34}, wob:0.3 }, 'io');
K(18.5, { x:0, y:-52, rot:-0.20,
fL:{x:-12,y:0}, fR:{x:12,y:0},
hL:{x:-20,y:-56}, hR:{x:20,y:-56}, wob:0.3 }, 'out');
K(19.0, { x:0, y:-50, rot:0.00,
fL:{x:-9,y:0}, fR:{x:9,y:0},
hL:{x:-7,y:-42}, hR:{x:7,y:-42}, wob:0.35 }, 'io');
K(19.5, { x:0, y:-50, rot:0.00,
fL:{x:-9,y:0}, fR:{x:9,y:0},
hL:{x:-7,y:-42}, hR:{x:7,y:-42}, wob:0.35 }, 'io');
K(20.0, {}, 'io');
/* ============================================================
SAMPLING
============================================================ */
function lerpPose(a, b, u){
var o = {};
for (var k in a){
var va = a[k], vb = b[k];
if (typeof va === 'number'){
o[k] = va + (vb - va) * u;
} else if (va && typeof va === 'object'){
o[k] = { x: va.x + (vb.x - va.x) * u, y: va.y + (vb.y - va.y) * u };
} else {
o[k] = va;
}
}
return o;
}
function sample(b){
b = ((b % TOTAL) + TOTAL) % TOTAL;
var i = 0;
while (i < KF.length - 2 && KF[i+1].t <= b) i++;
var a = KF[i], c = KF[i+1];
var u = 0;
if (c.t > a.t) u = (b - a.t) / (c.t - a.t);
u = Math.max(0, Math.min(1, u));
var e = EASE[c.e] || EASE.io;
return lerpPose(a.p, c.p, e(u));
}
/* ============================================================
2-BONE IK
Convention: dir(a) = (sin a, cos a). Returns joint angles.
============================================================ */
function solveIK(tx, ty, l1, l2, sign){
var d = Math.hypot(tx, ty);
var dmax = l1 + l2 - 0.6;
var dmin = Math.abs(l1 - l2) + 0.6;
d = Math.max(dmin, Math.min(dmax, d));
var th = Math.atan2(tx, ty);
var ca = (d*d + l1*l1 - l2*l2) / (2*d*l1);
var cb = (l1*l1 + l2*l2 - d*d) / (2*l1*l2);
var al = Math.acos(Math.max(-1, Math.min(1, ca)));
var be = Math.acos(Math.max(-1, Math.min(1, cb)));
return { a1: th + sign*al, a2: -sign*(Math.PI - be) };
}
function solveLeg(targetWorld, W2L){
var tl = W2L(targetWorld.x, targetWorld.y);
var ik = solveIK(tl.x, -tl.y, L1, L2, 1);
var knee = { x: L1*Math.sin(ik.a1), y: -L1*Math.cos(ik.a1) };
var foot = {
x: knee.x + L2*Math.sin(ik.a1 + ik.a2),
y: knee.y - L2*Math.cos(ik.a1 + ik.a2)
};
return { knee: knee, foot: foot };
}
function solveWing(targetWorld, W2L){
var tl = W2L(targetWorld.x, targetWorld.y);
var ik = solveIK(tl.x, tl.y - TORSO, W1, W2, 1);
var elbow = { x: W1*Math.sin(ik.a1), y: TORSO + W1*Math.cos(ik.a1) };
var hand = {
x: elbow.x + W2*Math.sin(ik.a1 + ik.a2),
y: elbow.y + W2*Math.cos(ik.a1 + ik.a2)
};
return { sh:{x:0,y:TORSO}, elbow: elbow, hand: hand };
}
/* ============================================================
BIRD DRAWING (all in torso-local space, +y up)
============================================================ */
function drawWing(w, col, tipCol, alpha){
ctx.globalAlpha = alpha;
ctx.lineCap = 'round';
ctx.strokeStyle = col;
ctx.lineWidth = 13;
ctx.beginPath();
ctx.moveTo(w.sh.x, w.sh.y);
ctx.lineTo(w.elbow.x, w.elbow.y);
ctx.stroke();
ctx.lineWidth = 10;
ctx.beginPath();
ctx.moveTo(w.elbow.x, w.elbow.y);
ctx.lineTo(w.hand.x, w.hand.y);
ctx.stroke();
var ang = Math.atan2(w.hand.y - w.elbow.y, w.hand.x - w.elbow.x);
ctx.save();
ctx.translate(w.hand.x, w.hand.y);
ctx.rotate(ang);
ctx.fillStyle = tipCol;
ctx.beginPath();
ctx.ellipse(3, 0, 9, 6, 0, 0, TAU);
ctx.fill();
ctx.restore();
ctx.globalAlpha = 1;
}
function drawLeg(l, col, alpha){
ctx.globalAlpha = alpha;
ctx.lineCap = 'round';
ctx.strokeStyle = col;
ctx.lineWidth = 6.5;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(l.knee.x, l.knee.y);
ctx.stroke();
ctx.lineWidth = 5;
ctx.beginPath();
ctx.moveTo(l.knee.x, l.knee.y);
ctx.lineTo(l.foot.x, l.foot.y);
ctx.stroke();
ctx.lineWidth = 3.4;
ctx.beginPath();
ctx.moveTo(l.foot.x, l.foot.y);
ctx.lineTo(l.foot.x + 10, l.foot.y - 1.5);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(l.foot.x, l.foot.y);
ctx.lineTo(l.foot.x + 8, l.foot.y + 4.5);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(l.foot.x, l.foot.y);
ctx.lineTo(l.foot.x - 7, l.foot.y + 2.5);
ctx.stroke();
ctx.globalAlpha = 1;
}
function drawTail(pal){
ctx.beginPath();
ctx.moveTo(-2, 4);
ctx.quadraticCurveTo(-26, 11, -38, -2);
ctx.quadraticCurveTo(-24, -9, -2, -8);
ctx.closePath();
ctx.fillStyle = pal.wing2;
ctx.fill();
ctx.lineWidth = 2.5;
ctx.strokeStyle = pal.line;
ctx.stroke();
}
function drawBody(pal){
ctx.beginPath();
ctx.moveTo(0, -10);
ctx.bezierCurveTo(-17, -8, -21, 12, -17, 30);
ctx.bezierCurveTo(-14, 44, -8, 50, 0, 50);
ctx.bezierCurveTo(8, 50, 14, 44, 17, 30);
ctx.bezierCurveTo(21, 12, 17, -8, 0, -10);
ctx.closePath();
ctx.fillStyle = pal.body;
ctx.fill();
ctx.lineWidth = 2.6;
ctx.strokeStyle = pal.line;
ctx.stroke();
/* belly */
ctx.beginPath();
ctx.ellipse(7, 20, 9, 17, -0.10, 0, TAU);
ctx.fillStyle = pal.belly;
ctx.globalAlpha = 0.8;
ctx.fill();
ctx.globalAlpha = 1;
/* back shading */
ctx.beginPath();
ctx.ellipse(-9, 24, 6, 15, 0.12, 0, TAU);
ctx.fillStyle = pal.body2;
ctx.globalAlpha = 0.55;
ctx.fill();
ctx.globalAlpha = 1;
}
function drawHead(p, pal){
var hx = p.hx;
var hy = TORSO + HEAD_OFF + p.hy;
ctx.save();
ctx.translate(hx, hy);
ctx.rotate(-p.hr);
ctx.translate(-hx, -hy);
/* neck */
ctx.beginPath();
ctx.moveTo(-8, TORSO - 6);
ctx.lineTo(-8, hy - 4);
ctx.lineTo(8, hy - 4);
ctx.lineTo(8, TORSO - 6);
ctx.closePath();
ctx.fillStyle = pal.body;
ctx.fill();
/* skull */
ctx.beginPath();
ctx.ellipse(hx, hy, HEAD_R + 1, HEAD_R, 0, 0, TAU);
ctx.fillStyle = pal.body;
ctx.fill();
ctx.lineWidth = 2.6;
ctx.strokeStyle = pal.line;
ctx.stroke();
/* beak */
ctx.beginPath();
ctx.moveTo(hx + 10, hy + 4);
ctx.lineTo(hx + 28, hy - 1);
ctx.lineTo(hx + 10, hy - 5);
ctx.closePath();
ctx.fillStyle = pal.beak;
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = pal.line;
ctx.stroke();
/* eye */
if (p.blink > 0.5){
ctx.beginPath();
ctx.moveTo(hx + 1, hy + 4);
ctx.lineTo(hx + 9, hy + 4);
ctx.lineWidth = 2.4;
ctx.strokeStyle = pal.pupil;
ctx.lineCap = 'round';
ctx.stroke();
} else {
ctx.beginPath();
ctx.arc(hx + 4.5, hy + 4.5, 4.8, 0, TAU);
ctx.fillStyle = pal.eye;
ctx.fill();
ctx.beginPath();
ctx.arc(hx + 6, hy + 4.5, 2.5, 0, TAU);
ctx.fillStyle = pal.pupil;
ctx.fill();
}
/* cap dome */
ctx.beginPath();
ctx.moveTo(hx - 14, hy + 3);
ctx.bezierCurveTo(hx - 13, hy + 21, hx + 13, hy + 21, hx + 14, hy + 3);
ctx.closePath();
ctx.fillStyle = pal.cap;
ctx.fill();
ctx.lineWidth = 2.6;
ctx.strokeStyle = pal.line;
ctx.stroke();
/* backwards brim */
ctx.beginPath();
ctx.moveTo(hx - 13, hy + 4);
ctx.quadraticCurveTo(hx - 30, hy + 8, hx - 31, hy - 1);
ctx.quadraticCurveTo(hx - 24, hy - 5, hx - 13, hy - 1);
ctx.closePath();
ctx.fillStyle = pal.cap