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>
:root {
--catY: 0px;
}
* { box-sizing: border-box; }
html, body {
margin: 0; height: 100%; overflow: hidden;
background: radial-gradient(circle at 50% 40%, #1a1f2e 0%, #05060a 70%);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
color: #e8ecf4;
}
.scene {
position: fixed; inset: 0;
perspective: 920px;
perspective-origin: 50% 42%;
overflow: hidden;
}
.world {
position: absolute; inset: 0;
transform-style: preserve-3d;
transform: translate3d(0,0,0) translateZ(-200px) rotateX(8deg) rotateY(0rad);
will-change: transform, filter;
}
.room {
position: absolute; left: 50%; top: 50%;
transform-style: preserve-3d;
transform: translate(-50%,-50%);
transition: opacity .6s ease;
}
.wall {
position: absolute; left: 50%; top: 50%;
width: 1040px; height: 640px;
transform-origin: center center;
border: 1px solid rgba(0,0,0,.18);
box-shadow: inset 0 0 90px rgba(0,0,0,.16);
}
.wall.back {
transform: translate(-50%,-50%) translateZ(-520px);
background: linear-gradient(180deg, #dfe7f3 0%, #aebfd8 100%);
}
.wall.left {
transform: translate(-50%,-50%) rotateY(90deg) translateZ(-520px);
background: linear-gradient(180deg, #c8d4e6 0%, #95a8c4 100%);
}
.wall.right {
transform: translate(-50%,-50%) rotateY(-90deg) translateZ(-520px);
background: linear-gradient(180deg, #c8d4e6 0%, #95a8c4 100%);
}
.ceiling {
position: absolute; left: 50%; top: 50%;
width: 1040px; height: 1040px;
transform: translate(-50%,-50%) rotateX(-90deg) translateZ(-320px);
background: linear-gradient(180deg, #f4f7fb 0%, #d8e0ec 100%);
box-shadow: inset 0 0 120px rgba(0,0,0,.08);
}
.floor {
position: absolute; left: 50%; top: 50%;
width: 1040px; height: 1040px;
transform: translate(-50%,-50%) rotateX(90deg) translateZ(-320px);
background:
repeating-linear-gradient(90deg, rgba(105,70,42,.95) 0 46px, rgba(88,58,34,.95) 46px 92px),
linear-gradient(180deg, #8a5a35, #6b4328);
box-shadow: inset 0 0 160px rgba(0,0,0,.42);
}
.window {
position: absolute; left: 120px; top: 110px;
width: 260px; height: 180px;
background: linear-gradient(180deg, #9fd0ff 0%, #eaf6ff 100%);
border: 8px solid #f2f2f2;
box-shadow: inset 0 0 30px rgba(255,255,255,.5), 0 0 40px rgba(120,180,255,.25);
}
.window::after {
content: ""; position: absolute; inset: 0;
background: linear-gradient(120deg, rgba(255,255,255,.35), transparent 45%);
}
.baseboard {
position: absolute; left: 0; right: 0; bottom: 0; height: 18px;
background: linear-gradient(180deg, #f0f0f0, #cfcfcf);
}
.furniture {
position: absolute; left: 50%; top: 50%;
transform-style: preserve-3d;
transition: opacity .6s ease;
}
.rug {
position: absolute;
width: 520px; height: 360px;
transform: translate3d(-260px, 305px, -80px) rotateX(90deg);
background: radial-gradient(ellipse at center, #b85b5b 0%, #7a3434 70%);
border-radius: 50%;
box-shadow: 0 0 40px rgba(0,0,0,.35);
opacity: .85;
}
.sofa {
position: absolute;
width: 360px; height: 110px;
transform: translate3d(-420px, 235px, -260px);
background: linear-gradient(180deg, #6b7f95, #4f6175);
border-radius: 18px 18px 8px 8px;
box-shadow: 0 18px 30px rgba(0,0,0,.35);
}
.sofa::before {
content: ""; position: absolute; left: 18px; right: 18px; top: -42px;
height: 52px; border-radius: 16px 16px 6px 6px;
background: linear-gradient(180deg, #7f93aa, #5f738a);
}
.table {
position: absolute;
width: 220px; height: 120px;
transform: translate3d(120px, 275px, 120px) rotateX(90deg);
background: linear-gradient(180deg, #c9a27a, #9a7450);
border-radius: 12px;
box-shadow: 0 10px 24px rgba(0,0,0,.3);
}
.lamp {
position: absolute;
width: 16px; height: 150px;
transform: translate3d(300px, 180px, -300px);
background: linear-gradient(180deg, #d8d8d8, #8a8a8a);
border-radius: 8px;
}
.lamp::before {
content: ""; position: absolute; left: -26px; top: -34px;
width: 68px; height: 40px;
background: linear-gradient(180deg, #f2e6c8, #d8c89a);
clip-path: polygon(15% 0, 85% 0, 100% 100%, 0 100%);
filter: drop-shadow(0 0 24px rgba(255,220,150,.55));
}
.lamp-light {
position: absolute; left: 50%; top: 50%;
width: 900px; height: 900px;
transform: translate(-50%,-50%) translate3d(300px, 260px, -300px) rotateX(90deg);
background: radial-gradient(circle, rgba(255,232,175,.34) 0%, rgba(255,232,175,.12) 35%, transparent 70%);
mix-blend-mode: screen;
pointer-events: none;
animation: flicker 3.6s infinite ease-in-out;
}
@keyframes flicker {
0%, 100% { opacity: .86; }
47% { opacity: .95; }
52% { opacity: .78; }
68% { opacity: .92; }
}
.plant {
position: absolute;
width: 44px; height: 90px;
transform: translate3d(-360px, 230px, 260px);
background: linear-gradient(180deg, #5f8f5f, #3f6a3f);
border-radius: 22px 22px 8px 8px;
box-shadow: 0 12px 24px rgba(0,0,0,.28);
}
.plant::before {
content: ""; position: absolute; left: -18px; top: -34px;
width: 80px; height: 52px;
background: radial-gradient(circle at 40% 40%, #7fbf7f, #4f8f4f);
border-radius: 50%;
}
.cat {
position: absolute; left: 50%; top: 50%;
width: 0; height: 0;
transform-style: preserve-3d;
cursor: pointer;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
.cat * { transform-style: preserve-3d; }
.body {
position: absolute; left: -96px; top: -58px;
width: 192px; height: 132px;
border-radius: 50% 46% 52% 48%;
background: radial-gradient(circle at 34% 28%, #ffffff 0%, #f2e6d0 46%, #d8c2a0 100%);
box-shadow: inset -22px -16px 42px rgba(0,0,0,.18), 0 24px 40px rgba(0,0,0,.34);
}
.body::before {
content: ""; position: absolute; inset: 0; border-radius: inherit;
background: radial-gradient(circle at 30% 24%, rgba(255,255,255,.62), transparent 46%);
}
.body::after {
content: ""; position: absolute; inset: 0; border-radius: inherit;
background: repeating-radial-gradient(circle at 22% 30%, rgba(255,255,255,.13) 0 1px, transparent 1px 7px);
mix-blend-mode: soft-light;
}
.head {
position: absolute; left: -70px; top: -198px;
width: 140px; height: 136px;
border-radius: 50%;
background: radial-gradient(circle at 36% 30%, #ffffff 0%, #f3e7d2 50%, #d9c3a2 100%);
box-shadow: inset -14px -12px 28px rgba(0,0,0,.16), 0 14px 28px rgba(0,0,0,.28);
transform: translateZ(26px);
}
.head::before {
content: ""; position: absolute; inset: 0; border-radius: inherit;
background: radial-gradient(circle at 32% 26%, rgba(255,255,255,.58), transparent 44%);
}
.ear {
position: absolute; top: -34px;
width: 44px; height: 50px;
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
background: linear-gradient(180deg, #f3e7d2, #d9c3a2);
filter: drop-shadow(0 6px 8px rgba(0,0,0,.18));
}
.ear.left { left: 12px; transform: rotateZ(-28deg); }
.ear.right { left: 84px; transform: rotateZ(28deg); }
.ear .inner {
position: absolute; left: 24%; top: 34%;
width: 52%; height: 46%;
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
background: linear-gradient(180deg, #f2a7a7, #e08a8a);
}
.eye {
position: absolute; top: 46px;
width: 32px; height: 38px;
border-radius: 50%;
background: radial-gradient(circle at 50% 44%, #151515 0 50%, #000 51% 100%);
box-shadow: inset 0 0 8px rgba(0,0,0,.55);
}
.eye.left { left: 28px; }
.eye.right { left: 80px; }
.eye .pupil {
position: absolute; left: 50%; top: 52%;
width: 13px; height: 18px;
margin: -9px 0 0 -6.5px;
border-radius: 50%;
background: #000;
}
.eye .shine {
position: absolute; left: 62%; top: 24%;
width: 8px; height: 8px;
border-radius: 50%;
background: rgba(255,255,255,.92);
}
.nose {
position: absolute; left: 61px; top: 84px;
width: 18px; height: 12px;
border-radius: 50%;
background: radial-gradient(circle at 50% 40%, #f0a0a6, #d97f86);
}
.mouth {
position: absolute; left: 54px; top: 98px;
width: 32px; height: 10px;
border-bottom: 2px solid #8a5a5a;
border-radius: 0 0 50% 50%;
}
.leg {
position: absolute; bottom: -78px;
width: 34px; height: 74px;
border-radius: 18px;
background: linear-gradient(180deg, #f3e7d2, #d9c3a2);
box-shadow: inset -6px -8px 14px rgba(0,0,0,.12);
}
.leg.fl { left: -74px; transform: rotateZ(10deg); }
.leg.fr { left: 130px; transform: rotateZ(-10deg); }
.leg.bl { left: -40px; transform: rotateZ(4deg); }
.leg.br { left: 96px; transform: rotateZ(-4deg); }
.paw {
position: absolute; left: -4px; bottom: -6px;
width: 42px; height: 18px;
border-radius: 12px;
background: linear-gradient(180deg, #f7efe2, #e2d0b4);
}
.tail {
position: absolute; left: 150px; top: -18px;
width: 18px; height: 118px;
border-radius: 9px;
transform-origin: 50% 100%;
transform: rotateZ(34deg) translateZ(-12px);
background: linear-gradient(180deg, #f3e7d2, #d9c3a2);
box-shadow: inset -4px -6px 10px rgba(0,0,0,.12);
}
.ghosts { position: absolute; inset: 0; pointer-events: none; }
.ghost {
position: absolute; left: 50%; top: 50%;
opacity: 0;
filter: blur(3px) brightness(1.05);
pointer-events: none;
}
.fx {
position: fixed; inset: 0;
pointer-events: none;
z-index: 5;
}
.ui {
position: fixed; right: 18px; bottom: 18px;
z-index: 10;
display: flex; gap: 10px; align-items: center;
}
button {
appearance: none; border: 1px solid rgba(255,255,255,.22);
background: rgba(10,12,18,.55);
color: #e8ecf4;
padding: 10px 14px; border-radius: 999px;
font: inherit; font-size: 13px;
backdrop-filter: blur(8px);
cursor: pointer;
}
button:hover { background: rgba(25,28,40,.7); }
.hint {
position: fixed; left: 50%; bottom: 26px;
transform: translateX(-50%);
z-index: 10;
font-size: 13px; letter-spacing: .08em; text-transform: uppercase;
color: rgba(232,236,244,.72);
text-shadow: 0 2px 12px rgba(0,0,0,.6);
transition: opacity .8s ease;
pointer-events: none;
}
.scene[data-stage="1"] .cat { filter: drop-shadow(0 0 18px rgba(255,235,190,.25)); }
.scene[data-stage="2"] .room { filter: saturate(1.15) contrast(1.05); }
.scene[data-stage="3"] .world { filter: saturate(1.45) contrast(1.08); }
.scene[data-stage="4"] .cat { filter: brightness(1.18) saturate(1.3); }
</style>
</head>
<body>
<div class="scene" id="scene" data-stage="0">
<div class="world" id="world">
<div class="room" id="room">
<div class="wall back">
<div class="window"></div>
<div class="baseboard"></div>
</div>
<div class="wall left"><div class="baseboard"></div></div>
<div class="wall right"><div class="baseboard"></div></div>
<div class="ceiling"></div>
<div class="floor"></div>
<div class="lamp-light"></div>
</div>
<div class="furniture" id="furniture">
<div class="rug"></div>
<div class="sofa"></div>
<div class="table"></div>
<div class="lamp"></div>
<div class="plant"></div>
</div>
<div class="cat" id="cat">
<div class="tail"><div class="paw" style="left:2px;bottom:-4px;width:14px;height:10px;"></div></div>
<div class="body"></div>
<div class="head">
<div class="ear left"><div class="inner"></div></div>
<div class="ear right"><div class="inner"></div></div>
<div class="eye left"><div class="pupil"></div><div class="shine"></div></div>
<div class="eye right"><div class="pupil"></div><div class="shine"></div></div>
<div class="nose"></div>
<div class="mouth"></div>
</div>
<div class="leg fl"><div class="paw"></div></div>
<div class="leg fr"><div class="paw"></div></div>
<div class="leg bl"><div class="paw"></div></div>
<div class="leg br"><div class="paw"></div></div>
</div>
<div class="ghosts" id="ghosts"></div>
</div>
<canvas class="fx" id="fx"></canvas>
</div>
<div class="ui">
<button id="mute">Sound: on</button>
</div>
<div class="hint" id="hint">click the cat</div>
<script>
(() => {
const scene = document.getElementById('scene');
const world = document.getElementById('world');
const room = document.getElementById('room');
const furniture = document.getElementById('furniture');
const cat = document.getElementById('cat');
const ghostsEl = document.getElementById('ghosts');
const fx = document.getElementById('fx');
const fctx = fx.getContext('2d');
const muteBtn = document.getElementById('mute');
const hint = document.getElementById('hint');
let stage = 0;
let clicks = 0;
let spin = 0;
let roomSpin = 0;
let spinSpeed = 0;
let roomSpeed = 0;
let height = 0;
let scale = 1;
let targetSpinSpeed = 0;
let targetRoomSpeed = 0;
let targetHeight = 0;
let targetScale = 1;
let shakeAmp = 0;
let targetShake = 0;
let roomOpacity = 1;
let targetRoomOpacity = 1;
let floatY = 0;
let targetFloatY = 0;
let ghosts = [];
let particles = [];
let muted = false;
let intensity = 0;
let audioCtx = null;
let masterGain = null;
let voiceSource = null;
let voiceBuffer = null;
function resize() {
fx.width = window.innerWidth;
fx.height = window.innerHeight;
}
window.addEventListener('resize', resize);
resize();
function createVoiceBuffer(ctx) {
const sr = ctx.sampleRate;
const phonemes = [
['o',0.10],['i',0.08],['a',0.12],
['o',0.10],['i',0.08],['a',0.12],
['o',0.10],['i',0.08],['a',0.08],
['i',0.08],['o',0.12],['o',0.12],
['o',0.12],['a',0.10],['i',0.08]
];
const F = {
a: [820,1220,2600],
e: [500,1700,2500],
i: [310,2200,3000],
o: [500,900,2500],
u: [350,700,2300]
};
const BW = [90,110,130];
let total = 0;
const sched = phonemes.map(([v,d]) => {
const s = total; total += d; return { v, d, s, e: total };
});
const n = Math.floor(total * sr);
const data = new Float32Array(n);
const segIndex = new Int32Array(n);
let si = 0;
for (let i = 0; i < n; i++) {
const t = i / sr;
while (si < sched.length - 1 && t >= sched[si].e) si++;
segIndex[i] = si;
}
let maxAbs = 0;
for (let i = 0; i < n; i++) {
const t = i / sr;
const seg = sched[segIndex[i]];
const next = sched[(segIndex[i] + 1) % sched.length];
const local = Math.min(1, Math.max(0, (t - seg.s) / seg.d));
const formants = [0,1,2].map(k => F[seg.v][k] * (1 - local) + F[next.v][k] * local);
const f0 = 165 * (1 + 0.012 * Math.sin(2 * Math.PI * 5.2 * t));
const env = Math.pow(Math.sin(Math.PI * local), 0.8);
let smp = 0;
for (let k = 1; k <= 28; k++) {
const freq = k * f0;
let a = 0;
for (let j = 0; j < 3; j++) {
const df = freq - formants[j];
a += Math.exp(-0.5 * (df / BW[j]) ** 2);
}
a *= (1 / k) * 0.9;
smp += a * Math.sin(2 * Math.PI * freq * t);
}
smp += (Math.random() * 2 - 1) * 0.015 * env;
smp *= env * 0.7;
data[i] = smp;
if (Math.abs(smp) > maxAbs) maxAbs = Math.abs(smp);
}
if (maxAbs > 0) {
for (let i = 0; i < n; i++) data[i] = (data[i] / maxAbs) * 0.85;
}
const buffer = ctx.createBuffer(1, n, sr);
buffer.copyToChannel(data, 0);
return buffer;
}
function ensureAudio() {
if (audioCtx) {
if (audioCtx.state === 'suspended') audioCtx.resume();
return;
}
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
masterGain = audioCtx.createGain();
masterGain.gain.value = muted ? 0 : 0.6;
masterGain.connect(audioCtx.destination);
voiceBuffer = createVoiceBuffer(audioCtx);
voiceSource = audioCtx.createBufferSource();
voiceSource.buffer = voiceBuffer;
voiceSource.loop = true;
voiceSource.playbackRate.value = 0.5;
voiceSource.connect(masterGain);
voiceSource.start();
}
function ensureGhosts(n) {
while (ghosts.length < n) {
const g = cat.cloneNode(true);
g.removeAttribute('id');
g.classList.add('ghost');
g.style.pointerEvents = 'none';
ghostsEl.appendChild(g);
ghosts.push(g);
}
}
function spawnBurst(n, type) {
const cx = window.innerWidth / 2;
const cy = window.innerHeight / 2 - height;
for (let i = 0; i < n; i++) {
particles.push({
x: cx + (Math.random() - 0.5) * 120,
y: cy + (Math.random() - 0.5) * 120,
vx: (Math.random() - 0.5) * (type === 'cat' ? 5 : 3),
vy: (Math.random() - 0.5) * (type === 'cat' ? 5 : 3) - 1,
life: 1,
decay: 0.0015 + Math.random() * 0.004,
type,
size: (type === 'cat' ? 4 : 2) + Math.random() * (type === 'cat' ? 7 : 4),
hue: Math.random() * 360,
rot: Math.random() * Math.PI * 2
});
}
if (particles.length > 1200) particles.splice(0, particles.length - 1200);
}
function applyStage(s) {
scene.dataset.stage = s;
if (s >= 1) {
targetSpinSpeed = 4;
targetRoomSpeed = -1.2;
targetHeight = 72;
targetScale = 1.02;
targetShake = 0.6;
targetRoomOpacity = 1;
targetFloatY = 34;
}
if (s >= 2) {
targetSpinSpeed = 10;
targetRoomSpeed = -2.8;
targetHeight = 175;
targetScale = 1.08;
targetShake = 2.2;
targetFloatY = 86;
ensureGhosts(4);
spawnBurst(70, 'debris');
}
if (s >= 3) {
targetSpinSpeed = 20;
targetRoomSpeed = -5.6;
targetHeight = 305;
targetScale = 1.22;
targetShake = 4.4;
targetFloatY = 165;
ensureGhosts(8);
spawnBurst(130, 'spark');
}
if (s >= 4) {
const extra = Math.max(0, clicks - 4);
targetSpinSpeed = 32 + extra * 3;
targetRoomSpeed = -8.2 - extra * 1.1;
targetHeight = 425 + extra * 22;
targetScale = 2.25 + extra * 0.18;
targetShake = 8.5 + extra * 1.2;
targetRoomOpacity = 0;
targetFloatY = 270;
ensureGhosts(12);
spawnBurst(220, 'cat');
}
}
cat.addEventListener('click', (e) => {
e.stopPropagation();
ensureAudio();
clicks++;
if (clicks === 1) stage = 1;
else if (clicks === 2) stage = 2;
else if (clicks === 3) stage = 3;
else { stage = 4; intensity = clicks; }
applyStage(stage);
if (clicks > 0) hint.style.opacity = '0';
});
muteBtn.addEventListener('click', (e) => {
e.stopPropagation();
muted = !muted;
muteBtn.textContent = muted ? 'Sound: off' : 'Sound: on';
if (masterGain && audioCtx) {
masterGain.gain.setTargetAtTime(muted ? 0 : 0.6, audioCtx.currentTime, 0.05);
}
});
let last = performance.now();
function frame(now) {
const dt = Math.min(0.05, (now - last) / 1000);
last = now;
const time = now / 1000;
spinSpeed += (targetSpinSpeed - spinSpeed) * (1 - Math.exp(-dt * 2.4));
roomSpeed += (targetRoomSpeed - roomSpeed) * (1 - Math.exp(-dt * 2.0));
height += (targetHeight - height) * (1 - Math.exp(-dt * 2.2));
scale += (targetScale - scale) * (1 - Math.exp(-dt * 1.8));
shakeAmp += (targetShake - shakeAmp) * (1 - Math.exp(-dt * 3.0));
roomOpacity += (targetRoomOpacity - roomOpacity) * (1 - Math.exp(-dt * 1.6));
floatY += (targetFloatY - floatY) * (1 - Math.exp(-dt * 2.0));
spin += spinSpeed * dt;
roomSpin += roomSpeed * dt;
const shakeX = (Math.random() * 2 - 1) * shakeAmp * (1 + spinSpeed * 0.04);
const shakeY = (Math.random() * 2 - 1) * shakeAmp * (1 + spinSpeed * 0.04);
world.style.transform = `translate3d(${shakeX}px,${shakeY}px,0) translateZ(-200px) rotateX(8deg) rotateY(${roomSpin}rad)`;
room.style.opacity = roomOpacity.toFixed(3);
furniture.style.opacity = roomOpacity.toFixed(3);
furniture.style.transform = `translateY(${-floatY}px) rotateY(${roomSpin * 0.28}rad)`;
if (stage >= 3) {
world.style.filter = `hue-rotate(${time * 110}deg) saturate(1.45) contrast(1.08)`;
} else {
world.style.filter = '';
}
const blur = Math.min(10, spinSpeed * 0.28);
const idleScale = stage === 0 ? 1 + Math.sin(time * 1.25) * 0.004 : 1;
cat.style.filter = `blur(${blur}px) brightness(${1 + stage * 0.08})`;
cat.style.transform = `translate(-50%,-50%) translateY(${-height}px) rotateY(${spin}rad) scale(${scale * idleScale})`;
ghosts.forEach((g, i) => {
const lag = (i + 1) * 0.13;
const gSpin = spin - lag * Math.max(0.8, spinSpeed * 0.12);
const gHeight = height - (i + 1) * 13;
const op = stage >= 2 ? Math.max(0, 0.46 - i * 0.032) * Math.min(1, spinSpeed / 4) : 0;
g.style.opacity = op.toFixed(3);
g.style.transform = `translate(-50%,-50%) translateY(${-gHeight}px) rotateY(${gSpin}rad) scale(${scale * 0.985})`;
});
if (audioCtx && voiceSource) {
const rate = Math.min(4, 0.5 + spinSpeed / 8);
voiceSource.playbackRate.setTargetAtTime(rate, audioCtx.currentTime, 0.05);
const gain = muted ? 0 : Math.min(1, 0.42 + stage * 0.12 + spinSpeed * 0.012);
masterGain.gain.setTargetAtTime(gain, audioCtx.currentTime, 0.05);
}
if (stage >= 4 && Math.random() < 0.35) spawnBurst(4, 'cat');
if (stage >= 3 && Math.random() < 0.12) spawnBurst(2, 'spark');
const gravity = stage >= 4 ? -0.035 : 0.025;
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.x += p.vx * (1 + spinSpeed * 0.08);
p.y += p.vy * (1 + spinSpeed * 0.08);
p.vx *= 0.985;
p.vy += gravity;
p.rot += 0.08;
p.life -= p.decay * (1 + spinSpeed * 0.02);
if (p.life <= 0) particles.splice(i, 1);
}
fctx.clearRect(0, 0, fx.width, fx.height);
fctx.save();
fctx.globalCompositeOperation = 'lighter';
for (const p of particles) {
fctx.globalAlpha = Math.max(0, p.life);
if (p.type === 'cat') {
fctx.fillStyle = `hsl(${p.hue}, 70%, 72%)`;
fctx.beginPath();
fctx.ellipse(p.x, p.y, p.size * 1.5, p.size, p.rot, 0, Math.PI * 2);
fctx.fill();
fctx.fillStyle = `hsl(${p.hue}, 60%, 88%)`;
fctx.beginPath();
fctx.ellipse(p.x, p.y - p.size * 0.9, p.size * 0.7, p.size * 0.65, p.rot, 0, Math.PI * 2);
fctx.fill();
} else if (p.type === 'debris') {
fctx.fillStyle = `hsla(${p.hue}, 35%, 62%, .85)`;
fctx.save();
fctx.translate(p.x, p.y);
fctx.rotate(p.rot);
fctx.fillRect(-p.size, -p.size * 0.6, p.size * 2, p.size * 1.2);
fctx.restore();
} else {
fctx.fillStyle = `hsl(${p.hue}, 90%, 70%)`;
fctx.beginPath();
fctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
fctx.fill();
}
}
fctx.restore();
fctx.globalAlpha = 1;
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
})();
</script>
</body>
</html>