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
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
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
You make short animated pieces for the web, and you have spent time on the part of the internet that makes cats famous.
Build me a single HTML page: a cat in a living room that lifts off the floor and spins in the air when you click it.
Make this the Oiia Cat — the spinning cat meme, the one whose loop is written out as "oiia oiia oiiaiooooiai". Match the original's look and feel, not a generic rotation:
Build toward what that meme feels like at full speed: too fast, too loud, and funnier every time it goes further.
data: URIs are fine.localStorage, sessionStorage, cookies or indexedDB — sandboxed frame, they throw.html block.The single fenced html block.
Five bullets.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Oiia Cat</title>
<style>
* { box-sizing: border-box; }
html, body {
width: 100%; height: 100%; margin: 0; padding: 0;
overflow: hidden; background: #050505;
font-family: system-ui, -apple-system, sans-serif;
}
#roomSvg, #catSvg {
position: absolute; top: 0; left: 0;
width: 100%; height: 100%; display: block;
}
#roomSvg { z-index: 0; }
#catSvg { z-index: 2; pointer-events: none; }
#whirl {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
z-index: 1; pointer-events: none; opacity: 0;
transition: opacity .6s ease;
}
body.stage3 #whirl { opacity: .4; }
body.stage4 #whirl { opacity: .85; }
#roomStatic, #roomProps {
transform-box: view-box;
transform-origin: 50% 50%;
will-change: transform, filter, opacity;
}
#cat3d {
transform-box: fill-box;
transform-origin: 50% 50%;
will-change: transform;
}
#clickpad { cursor: pointer; }
#vignette {
position: fixed; inset: 0; pointer-events: none; z-index: 3;
background: radial-gradient(ellipse at center, transparent 52%, rgba(0,0,0,.42) 100%);
}
#grain {
position: fixed; inset: 0; pointer-events: none; z-index: 4;
opacity: .07; background-size: 200px 200px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='0.06'/%3E%3C/svg%3E");
}
#muteBtn {
position: fixed; top: 14px; right: 14px; z-index: 6;
width: 46px; height: 46px; border: 0; border-radius: 50%;
background: rgba(250,245,235,.78); color: #1d140e;
font-size: 22px; cursor: pointer; line-height: 1;
box-shadow: 0 2px 12px rgba(0,0,0,.35);
backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
transition: transform .15s ease, background .15s ease;
}
#muteBtn:hover { transform: scale(1.07); background: rgba(255,252,245,.95); }
</style>
</head>
<body class="stage0">
<svg id="roomSvg" viewBox="0 0 960 600" preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="wallGrad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#b7a17e"/>
<stop offset="0.65" stop-color="#cdb896"/>
<stop offset="1" stop-color="#eadbc0"/>
</linearGradient>
<linearGradient id="wallShade" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#392917" stop-opacity="0.32"/>
<stop offset="0.65" stop-color="#392917" stop-opacity="0"/>
<stop offset="1" stop-color="#fff" stop-opacity="0.12"/>
</linearGradient>
<linearGradient id="floorGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#805f44"/>
<stop offset="0.5" stop-color="#6e4f38"/>
<stop offset="1" stop-color="#573d2b"/>
</linearGradient>
<linearGradient id="floorLight" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#e8c891" stop-opacity="0.28"/>
<stop offset="1" stop-color="#e8c891" stop-opacity="0"/>
</linearGradient>
<linearGradient id="skyGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#8fb6d6"/>
<stop offset="1" stop-color="#e8f1f6"/>
</linearGradient>
<linearGradient id="sunRay" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#ffe3a0" stop-opacity="0.55"/>
<stop offset="1" stop-color="#ffe3a0" stop-opacity="0"/>
</linearGradient>
<radialGradient id="rugGrad" cx="0.5" cy="0.5" r="0.5">
<stop offset="0" stop-color="#b75646"/>
<stop offset="0.7" stop-color="#8c3b34"/>
<stop offset="1" stop-color="#632b26"/>
</radialGradient>
<linearGradient id="sofaGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#7a5941"/>
<stop offset="1" stop-color="#543c2c"/>
</linearGradient>
<filter id="shadowBlur" x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur stdDeviation="8"/>
</filter>
<filter id="softBlur" x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur stdDeviation="5"/>
</filter>
</defs>
<g id="roomStatic">
<rect x="0" y="0" width="960" height="340" fill="url(#wallGrad)"/>
<rect x="0" y="0" width="960" height="340" fill="url(#wallShade)"/>
<rect x="0" y="330" width="960" height="12" fill="#6a503e"/>
<rect x="0" y="342" width="960" height="6" fill="#4f382a"/>
<polygon points="0,348 960,348 960,600 0,600" fill="url(#floorGrad)"/>
<polygon points="0,348 960,348 960,600 0,600" fill="url(#floorLight)"/>
<path d="M480 348 L-150 600 M480 348 L80 600 M480 348 L290 600 M480 348 L500 600 M480 348 L710 600 M480 348 L920 600 M480 348 L1130 600" stroke="#3a281d" stroke-width="1.5" opacity="0.14" fill="none"/>
<path d="M0 400 L960 400 M0 455 L960 455 M0 515 L960 515 M0 565 L960 565" stroke="#3a281d" stroke-width="1.5" opacity="0.12" fill="none"/>
<ellipse cx="480" cy="530" rx="320" ry="80" fill="url(#rugGrad)"/>
<ellipse cx="480" cy="530" rx="270" ry="66" fill="none" stroke="#f3d8a8" stroke-width="6" stroke-dasharray="14 8" opacity="0.55"/>
<g id="window">
<rect x="688" y="52" width="220" height="250" rx="4" fill="#f0e5d2"/>
<rect x="696" y="60" width="204" height="234" fill="url(#skyGrad)"/>
<circle cx="786" cy="140" r="18" fill="#fff3c4" opacity="0.7"/>
<path d="M736 294 C738 226 760 188 784 202 C806 176 844 196 862 232 C876 256 884 282 886 294 Z" fill="#57734a" opacity="0.55"/>
<rect x="782" y="60" width="8" height="234" fill="#f0e5d2" opacity="0.8"/>
<rect x="696" y="170" width="204" height="8" fill="#f0e5d2" opacity="0.8"/>
<rect x="684" y="42" width="228" height="14" fill="#e4d7c2"/>
<rect x="682" y="300" width="232" height="14" fill="#6b503f"/>
<rect x="688" y="296" width="220" height="18" fill="#d2bfa3"/>
</g>
<path d="M715 305 L920 380 L940 470 L880 600 L560 600 L720 430 Z" fill="url(#sunRay)" filter="url(#softBlur)" opacity="0.7"/>
<g id="roomProps">
<g id="shelf">
<rect x="48" y="118" width="190" height="242" rx="4" fill="#5d452f"/>
<rect x="60" y="132" width="166" height="220" fill="#292019"/>
<rect x="68" y="142" width="14" height="52" fill="#b46b4e"/>
<rect x="86" y="142" width="16" height="40" fill="#6b8f6a"/>
<rect x="106" y="142" width="18" height="48" fill="#7d6a9a"/>
<rect x="128" y="142" width="12" height="38" fill="#c6a15b"/>
<rect x="144" y="142" width="14" height="46" fill="#5f798e"/>
<rect x="162" y="142" width="15" height="42" fill="#a95343"/>
<rect x="182" y="142" width="14" height="36" fill="#6f5138"/>
<rect x="66" y="200" width="154" height="10" fill="#4a3829"/>
<rect x="72" y="214" width="14" height="48" fill="#c6a15b"/>
<rect x="90" y="214" width="16" height="42" fill="#5f798e"/>
<rect x="110" y="214" width="13" height="50" fill="#b46b4e"/>
<rect x="127" y="214" width="18" height="38" fill="#7d6a9a"/>
<rect x="149" y="214" width="14" height="45" fill="#6f5138"/>
<rect x="167" y="214" width="15" height="40" fill="#a95343"/>
<rect x="186" y="214" width="10" height="38" fill="#6b8f6a"/>
<rect x="66" y="268" width="154" height="10" fill="#4a3829"/>
<rect x="72" y="282" width="16" height="48" fill="#5f798e"/>
<rect x="92" y="282" width="13" height="40" fill="#b46b4e"/>
<rect x="109" y="282" width="15" height="46" fill="#c6a15b"/>
<rect x="128" y="282" width="14" height="38" fill="#6b8f6a"/>
<rect x="146" y="282" width="17" height="44" fill="#7d6a9a"/>
<rect x="167" y="282" width="12" height="42" fill="#a95343"/>
<rect x="184" y="282" width="14" height="38" fill="#6f5138"/>
<rect x="66" y="334" width="154" height="10" fill="#4a3829"/>
<rect x="72" y="348" width="12" height="18" fill="#b46b4e"/>
<rect x="88" y="348" width="14" height="18" fill="#6b8f6a"/>
<rect x="106" y="348" width="15" height="18" fill="#c6a15b"/>
<rect x="125" y="348" width="12" height="18" fill="#5f798e"/>
<rect x="141" y="348" width="14" height="18" fill="#7d6a9a"/>
<rect x="159" y="348" width="12" height="18" fill="#a95343"/>
<rect x="175" y="348" width="13" height="18" fill="#6f5138"/>
</g>
<g id="frame">
<rect x="335" y="70" width="150" height="112" rx="4" fill="#73563f"/>
<rect x="345" y="80" width="130" height="92" fill="#ded1af"/>
<path d="M372 112 L402 147 L431 121 L440 130 L440 162 L360 162 L360 132 Z" fill="#899a7c"/>
<circle cx="424" cy="102" r="10" fill="#e5ca84"/>
<rect x="338" y="158" width="144" height="10" fill="#5a4130"/>
</g>
<g id="sofa">
<rect x="24" y="240" width="320" height="126" rx="20" fill="url(#sofaGrad)"/>
<rect x="36" y="258" width="296" height="76" rx="16" fill="#6a4a34"/>
<path d="M36 310 Q120 285 184 310 Q248 335 332 310 L332 334 L36 334 Z" fill="#4e3829"/>
<path d="M38 246 L42 356" stroke="#7c5b43" stroke-width="12" stroke-linecap="round"/>
<path d="M330 246 L326 356" stroke="#7c5b43" stroke-width="12" stroke-linecap="round"/>
<rect x="52" y="274" width="46" height="44" rx="10" fill="#8a604b"/>
<rect x="268" y="278" width="50" height="42" rx="10" fill="#8a604b"/>
<rect x="24" y="366" width="320" height="12" rx="6" fill="#3c2b20"/>
</g>
<g id="plant">
<path d="M822 370 C800 300 778 278 766 266 C820 302 850 328 864 370 Z" fill="#567a48"/>
<path d="M854 370 C876 298 906 276 916 266 C884 306 866 336 858 370 Z" fill="#46683b"/>
<path d="M838 370 C828 330 810 312 800 300 C836 322 858 346 864 370 Z" fill="#66895a"/>
<rect x="818" y="370" width="48" height="40" rx="6" fill="#a2533f"/>
<rect x="822" y="398" width="40" height="12" rx="4" fill="#7c3e31"/>
</g>
<g id="lamp">
<rect x="340" y="310" width="56" height="12" rx="3" fill="#6d4a31"/>
<rect x="348" y="322" width="40" height="8" rx="3" fill="#4f3828"/>
<rect x="365" y="190" width="7" height="124" fill="#5c4635"/>
<path d="M342 190 L368 166 L394 190 Z" fill="#d3b77a"/>
<ellipse cx="368" cy="166" rx="30" ry="9" fill="#efe2c0"/>
<ellipse cx="368" cy="188" rx="28" ry="5" fill="#c0a672"/>
<ellipse cx="368" cy="332" rx="26" ry="6" fill="#ffdc9a" opacity="0.5"/>
</g>
</g>
<ellipse id="catShadow" cx="480" cy="486" rx="88" ry="20" fill="#241912" opacity="0.45" filter="url(#shadowBlur)"/>
</g>
</svg>
<canvas id="whirl" width="1" height="1"></canvas>
<svg id="catSvg" viewBox="0 0 960 600" preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter id="furFilter" x="-20%" y="-20%" width="140%" height="140%">
<feTurbulence type="fractalNoise" baseFrequency="0.055" numOctaves="3" result="noise"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="5" xChannelSelector="R" yChannelSelector="G"/>
</filter>
<linearGradient id="headGrad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#d3b78e"/>
<stop offset="0.45" stop-color="#f3e6cb"/>
<stop offset="1" stop-color="#fbf5e8"/>
</linearGradient>
<linearGradient id="bodyGrad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#c8a878"/>
<stop offset="0.45" stop-color="#e8d5b4"/>
<stop offset="1" stop-color="#f4ecd9"/>
</linearGradient>
<g id="catShape">
<g filter="url(#furFilter)">
<path d="M-52,-252 C-56,-274 -68,-288 -76,-280 C-84,-272 -70,-244 -48,-238 Z" fill="url(#headGrad)"/>
<path d="M52,-252 C56,-274 68,-288 76,-280 C84,-272 70,-244 48,-238 Z" fill="url(#headGrad)"/>
<path d="M-62,-265 C-67,-278 -74,-282 -78,-278 C-81,-274 -71,-259 -59,-252 Z" fill="#eab8a5"/>
<path d="M62,-265 C67,-278 74,-282 78,-278 C81,-274 71,-259 59,-252 Z" fill="#eab8a5"/>
<ellipse cx="0" cy="-220" rx="62" ry="48" fill="url(#headGrad)"/>
<path d="M-58,-168 C-78,-140 -76,-70 -62,-30 C-48,-8 -25,2 0,2 C25,2 48,-8 62,-30 C76,-70 78,-140 58,-168 C40,-190 -40,-190 -58,-168 Z" fill="url(#bodyGrad)"/>
<path d="M-38,-50 C-40,-35 -40,-15 -38,-5 C-36,2 -30,5 -25,4 C-22,2 -21,-6 -22,-18 C-23,-32 -25,-44 -30,-52 Z" fill="#fbf3e4"/>
<path d="M38,-50 C40,-35 40,-15 38,-5 C36,2 30,5 25,4 C22,2 21,-6 22,-18 C23,-32 25,-44 30,-52 Z" fill="#fbf3e4"/>
<path d="M-52,-95 C-68,-72 -72,-42 -62,-16 L-48,-18 C-50,-40 -48,-70 -42,-88 Z" fill="url(#bodyGrad)"/>
<path d="M52,-95 C68,-72 72,-42 62,-16 L48,-18 C50,-40 48,-70 42,-88 Z" fill="url(#bodyGrad)"/>
<path d="M-44,-30 C-62,-24 -78,-32 -84,-50 C-88,-62 -82,-74 -68,-78 C-60,-80 -52,-74 -54,-66 C-58,-60 -66,-60 -70,-64 C-74,-68 -78,-64 -76,-58 C-72,-46 -62,-40 -50,-42 C-44,-42 -40,-38 -36,-32 Z" fill="url(#bodyGrad)"/>
</g>
<path d="M-34,-145 C-48,-100 -42,-55 -32,-28 C-22,-8 -10,2 0,2 C10,2 22,-8 32,-28 C42,-55 48,-100 34,-145 C22,-160 -22,-160 -34,-145 Z" fill="#fbf3e4"/>
<ellipse cx="0" cy="-172" rx="23" ry="16" fill="#fbf3e4"/>
<ellipse cx="-25" cy="-188" rx="15" ry="18" fill="#fdfaf2"/>
<ellipse cx="25" cy="-188" rx="15" ry="18" fill="#fdfaf2"/>
<ellipse cx="-25" cy="-188" rx="11" ry="13" fill="#b8c673"/>
<ellipse cx="25" cy="-188" rx="11" ry="13" fill="#b8c673"/>
<ellipse cx="-25" cy="-188" rx="9" ry="11" fill="#17130e"/>
<ellipse cx="25" cy="-188" rx="9" ry="11" fill="#17130e"/>
<circle cx="-21" cy="-193" r="3" fill="#fff"/>
<circle cx="29" cy="-193" r="3" fill="#fff"/>
<path d="M-6,-167 L6,-167 L0,-159 Z" fill="#eaa792"/>
<path d="M0,-159 C-7,-149 -18,-152 -23,-148 M0,-159 C7,-149 18,-152 23,-148" stroke="#5f4033" stroke-width="2" fill="none" stroke-linecap="round"/>
<path d="M-25,-170 L-70,-179 M-27,-164 L-70,-166 M-26,-157 L-68,-152" stroke="#ddd3c2" stroke-width="1.5" fill="none" opacity="0.7"/>
<path d="M25,-170 L70,-179 M27,-164 L70,-166 M26,-157 L68,-152" stroke="#ddd3c2" stroke-width="1.5" fill="none" opacity="0.7"/>
<ellipse cx="0" cy="-224" rx="32" ry="18" fill="#e2c79b" opacity="0.45"/>
</g>
</defs>
<g id="clones"></g>
<g id="catPos" transform="translate(480,480)">
<g id="cat3d">
<use href="#catShape" xlink:href="#catShape"/>
<ellipse cx="0" cy="-110" rx="105" ry="150" fill="transparent" style="pointer-events:all"/>
</g>
</g>
<circle id="clickpad" cx="480" cy="360" r="110" fill="transparent" style="pointer-events:all"/>
</svg>
<div id="vignette"></div>
<div id="grain"></div>
<button id="muteBtn" aria-label="Toggle sound">🔊</button>
<script>
(function(){
var NS = "http://www.w3.org/2000/svg";
var roomStatic = document.getElementById("roomStatic");
var roomProps = document.getElementById("roomProps");
var catShadow = document.getElementById("catShadow");
var catPos = document.getElementById("catPos");
var cat3d = document.getElementById("cat3d");
var clonesLayer = document.getElementById("clones");
var clickpad = document.getElementById("clickpad");
var whirl = document.getElementById("whirl");
var ctx = whirl.getContext("2d");
var muteBtn = document.getElementById("muteBtn");
var stage = 0;
var bonus = 0;
var active = false;
var startTime = 0;
var angular = 0;
var altitude = 0;
var speed = 0;
var lastTime = performance.now();
var clones = [];
var audio = {
ac: null,
master: null,
noiseBuf: null,
muted: false,
playing: false,
timer: null,
nextTime: 0,
step: 0,
spinSpeed: 0.6,
phrase: [
{v:"o", d:0.9}, {v:"i", d:0.8}, {v:"i", d:0.8}, {v:"a", d:1.0},
{v:"o", d:0.9}, {v:"i", d:0.8}, {v:"i", d:0.8}, {v:"a", d:1.0},
{v:"o", d:0.8}, {v:"i", d:0.7}, {v:"i", d:0.7}, {v:"a", d:1.0},
{v:"i", d:0.8}, {v:"o", d:1.3}, {v:"o", d:1.4}, {v:"o", d:1.4},
{v:"o", d:1.4}, {v:"i", d:0.7}, {v:"a", d:0.8}, {v:"i", d:0.7}
],
init: function() {
if (this.ac) return;
var AC = window.AudioContext || window.webkitAudioContext;
if (!AC) return;
this.ac = new AC();
this.master = this.ac.createGain();
this.master.gain.value = 0.5;
var comp = this.ac.createDynamicsCompressor();
comp.threshold.value = -24;
comp.knee.value = 20;
comp.ratio.value = 8;
comp.attack.value = 0.004;
comp.release.value = 0.25;
this.master.connect(comp);
comp.connect(this.ac.destination);
var len = Math.floor(this.ac.sampleRate * 0.5);
this.noiseBuf = this.ac.createBuffer(1, len, this.ac.sampleRate);
var data = this.noiseBuf.getChannelData(0);
for (var i = 0; i < len; i++) data[i] = Math.random() * 2 - 1;
},
start: function() {
if (!this.ac || this.muted) return;
this.ac.resume();
if (this.playing) return;
this.playing = true;
this.nextTime = this.ac.currentTime + 0.06;
this.step = 0;
var self = this;
this.timer = setInterval(function(){ self.schedule(); }, 80);
},
schedule: function() {
while (this.nextTime < this.ac.currentTime + 0.15) {
this.playSyllable(this.step, this.nextTime);
this.nextTime += this.stepDuration(this.step);
this.step = (this.step + 1) % this.phrase.length;
}
},
stepDuration: function(i) {
var sp = Math.max(0.5, this.spinSpeed || 0.5);
return Math.max(0.05, 0.17 / sp * this.phrase[i].d);
},
vowel: function(v) {
if (v === "i") return [320, 2300, 3200, 0.5, 1.0, 0.35];
if (v === "a") return [800, 1200, 2900, 1.0, 0.5, 0.3];
if (v === "u") return [360, 820, 2600, 1.0, 0.6, 0.2];
return [520, 900, 3000, 1.0, 0.6, 0.2];
},
playSyllable: function(i, time) {
var vow = this.phrase[i].v;
var v = this.vowel(vow);
var sp = Math.max(0.5, this.spinSpeed || 0.5);
var dur = this.stepDuration(i);
var base = Math.min(480, 115 + (sp - 0.5) * 26);
var melody = [0,3,0,-2,0,3,0,-2,-1,2,1,1,2,4,3,2,1,0,-1,2,1];
var f0 = base * Math.pow(2, melody[i % melody.length] / 12);
var osc = this.ac.createOscillator();
osc.type = "sawtooth";
osc.frequency.setValueAtTime(f0, time);
osc.frequency.linearRampToValueAtTime(f0 * (1 + 0.06 * Math.sin(i)), time + dur);
var lp = this.ac.createBiquadFilter();
lp.type = "lowpass";
lp.frequency.value = Math.min(4000, 2000 + sp * 80);
osc.connect(lp);
var env = this.ac.createGain();
var vol = Math.min(0.4, 0.18 + sp * 0.007);
env.gain.setValueAtTime(0.0001, time);
env.gain.exponentialRampToValueAtTime(vol, time + 0.012);
env.gain.exponentialRampToValueAtTime(0.0001, time + dur);
for (var j = 0; j < 3; j++) {
var f = this.ac.createBiquadFilter();
f.type = "bandpass";
f.Q.value = j === 0 ? 10 : 8;
f.frequency.value = v[j];
var g = this.ac.createGain();
g.gain.value = v[3 + j];
lp.connect(f);
f.connect(g);
g.connect(env);
}
env.connect(this.master);
var lfo = this.ac.createOscillator();
lfo.frequency.value = 5.5;
var lfoG = this.ac.createGain();
lfoG.gain.value = 5;
lfo.connect(lfoG);
lfoG.connect(osc.frequency);
var noise = this.ac.createBufferSource();
noise.buffer = this.noiseBuf;
noise.loop = true;
var hp = this.ac.createBiquadFilter();
hp.type = "highpass";
hp.frequency.value = 2200;
var ng = this.ac.createGain();
ng.gain.value = 0.025;
noise.connect(hp);
hp.connect(ng);
ng.connect(env);
osc.start(time);
osc.stop(time + dur + 0.05);
lfo.start(time);
lfo.stop(time + dur + 0.05);
noise.start(time);
noise.stop(time + dur + 0.05);
}
};
function getTargetSpeed() {
var t = (performance.now() - startTime) / 1000;
var p;
if (stage === 1) p = {base:1.1, target:8, tt:6};
else if (stage === 2) p = {base:8, target:16, tt:5};
else if (stage === 3) p = {base:16, target:30, tt:4};
else if (stage === 4) p = {base:30 + bonus * 6, target:54 + bonus * 8, tt:3};
else return 0;
var k = Math.min(1, t / p.tt);
var ease = k * k * (3 - 2 * k);
return p.base + (p.target - p.base) * ease;
}
function getHeightTarget() {
var t = (performance.now() - startTime) / 1000;
var maxH = stage === 1 ? 70 : stage === 2 ? 140 : stage === 3 ? 220 : stage === 4 ? 280 : 0;
var k = Math.min(1, t / 2.5);
return maxH * k * k * (3 - 2 * k);
}
function ensureClones(n) {
while (clones.length < n) {
var g = document.createElementNS(NS, "use");
g.setAttribute("href", "#catShape");
g.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "#catShape");
g.setAttribute("opacity", "0.32");
clonesLayer.appendChild(g);
clones.push({el: g, phase: (clones.length / n) * Math.PI * 2});
}
}
function resizeWhirl() {
var dpr = Math.min(window.devicePixelRatio || 1, 2);
whirl.width = Math.floor(window.innerWidth * dpr);
whirl.height = Math.floor(window.innerHeight * dpr);
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
var w = window.innerWidth, h = window.innerHeight;
var colors = ["#c8a878","#a3573c","#546b4b","#7a6a58","#d9c39a","#2d4050"];
var particles = [];
for (var i = 0; i < 160; i++) {
particles.push({
ang: Math.random() * Math.PI * 2,
r: 30 + Math.random() * Math.max(w, h) * 0.6,
sp: 0.5 + Math.random() * 2,
len: 8 + Math.random() * 60,
w: 1 + Math.random() * 2.5,
color: colors[i % colors.length],
a: 0.06 + Math.random() * 0.15
});
}
whirl._particles = particles;
whirl._w = w;
whirl._h = h;
}
function drawWhirl(now) {
var w = whirl._w || window.innerWidth;
var h = whirl._h || window.innerHeight;
var particles = whirl._particles || [];
ctx.clearRect(0, 0, w, h);
ctx.save();
ctx.translate(w / 2, h / 2);
ctx.globalCompositeOperation = "lighter";
var t = now / 1000;
for (var ring = 0; ring < 5; ring++) {
ctx.beginPath();
for (var i = 0; i <= 48; i++) {
var a = i / 48 * Math.PI * 2;
var rr = 20 + ring * 22 + 8 * Math.sin(t * (6 + ring) + i * 0.5);
var x = Math.cos(a) * rr;
var y = Math.sin(a) * rr * 0.7;
if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
}
ctx.strokeStyle = "hsla(" + (180 + ring * 40 + t * 40) + ",80%,70%,0.15)";
ctx.lineWidth = 2;
ctx.stroke();
}
for (var p = 0; p < particles.length; p++) {
var pt = particles[p];
var a2 = pt.ang + t * (0.8 + speed * 0.015) * pt.sp;
var rr2 = pt.r + 20 * Math.sin(t * 1.5 + pt.ang);
var x2 = Math.cos(a2) * rr2;
var y2 = Math.sin(a2) * rr2 * 0.65;
ctx.strokeStyle = pt.color;
ctx.globalAlpha = pt.a * (stage >= 4 ? 1 : 0.6);
ctx.lineWidth = pt.w;
ctx.beginPath();
ctx.moveTo(x2, y2);
ctx.lineTo(x2 - Math.cos(a2) * pt.len, y2 - Math.sin(a2) * pt.len * 0.6);
ctx.stroke();
}
ctx.restore();
}
function applyScene(now, dt) {
if (active) {
catPos.setAttribute("transform", "translate(480," + (480 - altitude) + ")");
cat3d.style.transform = "perspective(1200px) rotateY(" + angular + "deg)";
var blur = Math.min(14, Math.max(0, (speed - 0.8) * 1.2));
cat3d.style.filter = "blur(" + blur + "px)";
clickpad.setAttribute("cy", 360 - altitude * 0.75);
} else {
catPos.setAttribute("transform", "translate(480,480)");
cat3d.style.transform = "";
cat3d.style.filter = "";
clickpad.setAttribute("cy", 360);
}
var shadowScale = Math.max(0.25, 1 - altitude / 600);
catShadow.setAttribute("rx", 88 * shadowScale);
catShadow.setAttribute("ry", 20 * shadowScale);
catShadow.setAttribute("opacity", Math.max(0.06, 0.45 - altitude / 900));
var roomK = active && stage >= 1 ? Math.min(1, stage === 1 ? speed / 14 : speed / 18) : 0;
var roomAngle = (stage >= 4 ? (now / 1000) * 40 : 0) + Math.sin(now / 700 + speed * 0.5) * 2.2 * roomK;
var roomScale = 1 + roomK * (stage >= 2 ? 0.08 : 0.04);
var roomX = Math.sin(now / 130) * Math.min(6, speed * 0.2) * roomK;
var roomY = Math.sin(now / 160) * 2 * roomK;
roomStatic.style.transform = "translate(" + roomX + "px," + roomY + "px) rotate(" + roomAngle + "deg) scale(" + roomScale + ")";
roomStatic.style.filter = "brightness(" + (1 - stage * 0.05) + ") saturate(" + (1 + stage * 0.25) + ") hue-rotate(" + (stage >= 3 ? (now * 0.025) % 360 : 0) + "deg)";
if (stage >= 4) {
roomStatic.style.opacity = Math.max(0.2, 1 - Math.min(0.8, (speed - 24) * 0.03));
} else {
roomStatic.style.opacity = "1";
}
if (stage >= 2) {
var kick = stage >= 3 ? 1.6 : 1;
roomProps.style.transform = "translate(" + Math.sin(now / 900) * 30 * roomK * kick + "px," + (-Math.abs(Math.sin(now / 800)) * 40 * roomK * kick) + "px) rotate(" + Math.sin(now / 500) * 12 * roomK * kick + "deg)";
} else {
roomProps.style.transform = "";
}
if (stage >= 3 && clones.length < 5) ensureClones(5);
if (stage >= 4 && clones.length < 10) ensureClones(10);
for (var i = 0; i < clones.length; i++) {
var c = clones[i];
var radius = stage >= 4 ? 150 + speed * 3 : 110 + speed * 2;
var orbital = (now / 1000) * (stage >= 4 ? speed * 0.35 : speed * 0.22) + c.phase;
var x = 480 + Math.cos(orbital) * radius;
var y = 480 - altitude * 0.4 + Math.sin(orbital) * radius * 0.35;
var sc = 0.22 + 0.12 * Math.sin(orbital * 2 + i);
c.el.setAttribute("transform", "translate(" + x + "," + y + ") rotate(" + (Math.sin(orbital) * 20) + ") scale(" + sc + ")");
c.el.setAttribute("opacity", stage >= 4 ? "0.5" : "0.32");
}
if (stage >= 3) drawWhirl(now);
}
function frame(now) {
var dt = Math.min(0.05, (now - lastTime) / 1000);
lastTime = now;
if (active) {
var target = getTargetSpeed();
speed += (target - speed) * Math.min(1, dt * 1.8);
angular += speed * dt * 360;
var hT = getHeightTarget();
altitude += (hT - altitude) * Math.min(1, dt * 2);
audio.spinSpeed = speed;
}
applyScene(now, dt);
requestAnimationFrame(frame);
}
function onCat(e) {
e.stopPropagation();
if (stage === 0) audio.init();
if (!audio.muted) audio.start();
if (stage === 4) {
bonus++;
} else {
stage++;
}
document.body.className = "stage" + stage;
startTime = performance.now();
active = true;
}
function toggleMute() {
audio.muted = !audio.muted;
if (audio.ac) {
if (audio.muted) {
if (audio.playing) audio.master.gain.setTargetAtTime(0, audio.ac.currentTime, 0.01);
} else {
audio.ac.resume();
if (audio.playing) {
audio.master.gain.setTargetAtTime(0.5, audio.ac.currentTime, 0.01);
} else if (active) {
audio.start();
}
}
}
muteBtn.textContent = audio.muted ? "🔇" : "🔊";
}
clickpad.addEventListener("click", onCat);
muteBtn.addEventListener("click", function(e){
e.stopPropagation();
toggleMute();
});
window.addEventListener("resize", resizeWhirl);
resizeWhirl();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>