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, viewport-fit=cover">
<title>Oiia Cat — The Room Gets Involved</title>
<style>
:root {
--ink: #1d1814;
--cream: #f8f1e4;
--muted: rgba(255,255,255,.66);
--motion-blur: 0px;
--energy: 0;
}
* { box-sizing: border-box; }
html, body { width: 100%; height: 100%; margin: 0; }
body {
overflow: hidden;
background: #17120f;
color: var(--cream);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
-webkit-font-smoothing: antialiased;
}
button { font: inherit; }
button:focus-visible { outline: 2px solid #fff8dc; outline-offset: 7px; }
.svg-defs {
position: absolute;
width: 0;
height: 0;
overflow: hidden;
pointer-events: none;
}
#experience {
position: fixed;
inset: 0;
overflow: hidden;
isolation: isolate;
perspective: 1250px;
perspective-origin: 50% 43%;
background: #18130f;
}
/* The room ------------------------------------------------------- */
#room {
position: absolute;
inset: -3%;
z-index: 2;
overflow: hidden;
transform-style: preserve-3d;
transform-origin: 50% 68%;
will-change: transform;
}
.back-wall {
position: absolute;
inset: 0 0 27%;
z-index: 0;
overflow: hidden;
background:
radial-gradient(circle at 17% 17%, rgba(255,249,219,.76), transparent 32%),
radial-gradient(circle at 83% 28%, rgba(183,142,91,.12), transparent 35%),
linear-gradient(90deg, #a89b87 0%, #c8bca7 14%, #d8cdb9 52%, #b9ad98 100%);
box-shadow:
inset 0 -80px 100px rgba(62,40,23,.16),
inset 0 0 150px rgba(48,31,19,.2);
transition: filter 1.4s ease, transform 1.4s cubic-bezier(.2,.8,.2,1), background 1.4s ease;
}
.back-wall::before {
content: "";
position: absolute;
inset: 0;
opacity: .28;
background:
repeating-linear-gradient(91deg, transparent 0 3px, rgba(71,48,28,.035) 3px 4px),
radial-gradient(ellipse at 50% 50%, transparent 0 55%, rgba(54,34,20,.12) 100%);
mix-blend-mode: multiply;
}
.back-wall::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 10%;
background: linear-gradient(#d6c9b5, #a99982);
border-top: 1px solid rgba(70,48,30,.22);
box-shadow: 0 3px 9px rgba(43,27,17,.18);
}
.floor {
position: absolute;
z-index: 1;
left: -20%;
right: -20%;
top: 70%;
bottom: -36%;
transform: perspective(900px) rotateX(57deg);
transform-origin: 50% 0;
background:
linear-gradient(90deg, transparent 0 12%, rgba(64,38,20,.16) 12.1% 12.3%, transparent 12.4% 25%, rgba(64,38,20,.14) 25.1% 25.3%, transparent 25.4% 38%, rgba(64,38,20,.13) 38.1% 38.3%, transparent 38.4% 51%, rgba(64,38,20,.12) 51.1% 51.3%, transparent 51.4% 64%, rgba(64,38,20,.13) 64.1% 64.3%, transparent 64.4% 77%, rgba(64,38,20,.14) 77.1% 77.3%, transparent 77.4% 90%, rgba(64,38,20,.16) 90.1% 90.3%, transparent 90.4%),
repeating-linear-gradient(0deg, transparent 0 58px, rgba(63,37,19,.14) 59px 61px),
linear-gradient(180deg, #b99a72, #8e6743 72%, #725035);
box-shadow: inset 0 40px 70px rgba(255,232,190,.18);
transition: transform 1.5s cubic-bezier(.18,.82,.2,1), filter 1.5s ease;
}
.floor::after {
content: "";
position: absolute;
inset: 0;
background:
radial-gradient(ellipse at 50% 8%, rgba(255,237,201,.25), transparent 42%),
linear-gradient(90deg, rgba(0,0,0,.13), transparent 24%, transparent 76%, rgba(0,0,0,.16));
}
.rug {
position: absolute;
z-index: 2;
left: 50%;
top: 77%;
width: min(65vw, 850px);
height: min(34vw, 430px);
transform: translate(-50%, -50%) perspective(850px) rotateX(65deg);
transform-origin: center;
border-radius: 50%;
background:
radial-gradient(ellipse at center, #6f7c78 0 38%, #9b8f78 39% 46%, #596b6b 47% 63%, #c2ad87 64% 70%, #526467 71% 100%);
box-shadow: 0 24px 50px rgba(25,17,12,.38), inset 0 0 38px rgba(29,28,24,.3);
opacity: .94;
transition: transform 1.5s ease, opacity 1.2s ease;
}
.rug::after {
content: "";
position: absolute;
inset: 7%;
border: 2px dashed rgba(229,210,173,.2);
border-radius: inherit;
}
.sunbeam {
position: absolute;
z-index: 3;
left: 4%;
top: 8%;
width: 45%;
height: 85%;
clip-path: polygon(7% 0, 27% 0, 79% 100%, 24% 100%);
background: linear-gradient(180deg, rgba(255,249,214,.31), rgba(255,239,196,.08));
filter: blur(13px);
mix-blend-mode: screen;
opacity: .78;
transform-origin: 15% 10%;
pointer-events: none;
}
.window {
position: absolute;
z-index: 4;
left: 8%;
top: 9%;
width: 22%;
height: 39%;
overflow: hidden;
border: 13px solid #756d60;
border-top-width: 15px;
border-bottom-width: 17px;
border-radius: 3px;
background: #c8d8d5;
box-shadow:
0 18px 32px rgba(39,26,18,.25),
inset 0 0 0 2px rgba(255,255,255,.15),
0 0 70px rgba(255,247,205,.25);
transition: all 1.5s cubic-bezier(.2,.8,.2,1);
}
.window .outside {
position: absolute;
inset: 0;
background:
radial-gradient(circle at 72% 18%, rgba(255,255,238,.9) 0 4%, transparent 17%),
radial-gradient(ellipse at 22% 73%, #829b77 0 20%, transparent 44%),
radial-gradient(ellipse at 78% 79%, #6f886d 0 25%, transparent 50%),
linear-gradient(#b7d1d7 0 56%, #91aa79 57% 100%);
filter: blur(1.4px);
transform: scale(1.04);
}
.window::before {
content: "";
position: absolute;
z-index: 2;
inset: 0;
background:
linear-gradient(90deg, transparent 0 48%, #756d60 48% 52%, transparent 52%),
linear-gradient(0deg, transparent 0 47%, #756d60 47% 52%, transparent 52%);
box-shadow: inset 0 0 25px rgba(37,50,48,.28);
}
.window::after {
content: "";
position: absolute;
z-index: 3;
left: -10%;
top: -8%;
width: 33%;
height: 120%;
background: linear-gradient(90deg, #847967, #b2a38b 45%, #756858);
opacity: .9;
box-shadow: 8px 0 18px rgba(31,23,18,.15);
}
.art {
position: absolute;
z-index: 4;
left: 40%;
top: 20%;
width: 15%;
height: 21%;
padding: 8px;
background: linear-gradient(135deg, #5a4c3e, #352b25);
box-shadow: 0 12px 22px rgba(38,25,18,.25);
transition: opacity 1s ease, transform 1.4s ease;
}
.art::before {
content: "";
display: block;
width: 100%;
height: 100%;
background:
radial-gradient(circle at 68% 25%, #d9ad73 0 10%, transparent 11%),
linear-gradient(160deg, transparent 0 46%, #66746b 47% 65%, #38443f 66% 100%),
linear-gradient(#aeb7ae, #d9c7a8);
}
.shelf {
position: absolute;
z-index: 4;
left: 39%;
top: 46%;
width: 28%;
height: 7px;
border-radius: 2px;
background: linear-gradient(#72523a, #3e2a1e);
box-shadow: 0 7px 10px rgba(35,22,15,.26);
transition: opacity 1s ease, transform 1.4s ease;
}
.book {
position: absolute;
bottom: 6px;
width: 4.5%;
border-radius: 1px 1px 0 0;
box-shadow: inset 2px 0 rgba(255,255,255,.1), 2px 2px 3px rgba(25,18,13,.2);
}
.book:nth-child(1) { left: 4%; height: 47px; background: #6f3930; transform: rotate(-2deg); }
.book:nth-child(2) { left: 9%; height: 57px; width: 3.3%; background: #4a5d5b; }
.book:nth-child(3) { left: 13%; height: 43px; width: 5.2%; background: #b08a55; }
.book:nth-child(4) { left: 19%; height: 52px; width: 3%; background: #494257; transform: rotate(3deg); }
.book:nth-child(5) { right: 11%; height: 48px; width: 5%; background: #82624a; }
.book:nth-child(6) { right: 5%; height: 58px; width: 4%; background: #6c7c70; transform: rotate(2deg); }
.shelf-frame {
position: absolute;
right: 19%;
bottom: 7px;
width: 54px;
height: 38px;
border: 4px solid #4a3525;
background: linear-gradient(145deg, #c1b59b, #718078);
box-shadow: 0 3px 4px rgba(20,15,12,.3);
}
.plant {
position: absolute;
z-index: 5;
left: 32%;
top: 44%;
width: 90px;
height: 145px;
transition: opacity 1s ease, transform 1.4s ease;
}
.plant-pot {
position: absolute;
bottom: 0;
left: 19px;
width: 52px;
height: 47px;
border-radius: 5px 5px 18px 18px;
background: linear-gradient(90deg, #8b4d32, #b26b45 48%, #75402c);
box-shadow: 0 13px 16px rgba(29,18,12,.3);
}
.plant i {
position: absolute;
left: 38px;
bottom: 34px;
width: 26px;
height: 80px;
border-radius: 100% 0 100% 0;
background: linear-gradient(90deg, #314f3d, #67836b 52%, #2c4939);
transform-origin: 50% 100%;
box-shadow: inset 4px 0 rgba(255,255,255,.08);
}
.plant i:nth-child(1) { transform: rotate(-44deg) scale(.82); }
.plant i:nth-child(2) { transform: rotate(-24deg) scale(1.05); }
.plant i:nth-child(3) { transform: rotate(3deg) scale(1.12); }
.plant i:nth-child(4) { transform: rotate(29deg) scale(.95); }
.plant i:nth-child(5) { transform: rotate(48deg) scale(.75); }
.plant i:nth-child(6) { left: 25px; transform: rotate(-62deg) scale(.6); }
.sofa {
position: absolute;
z-index: 6;
right: -2%;
top: 42%;
width: 38%;
height: 29%;
transform: perspective(700px) rotateY(-5deg);
transform-origin: 50% 100%;
filter: drop-shadow(0 25px 18px rgba(29,20,14,.3));
transition: transform 1.4s ease, opacity 1.2s ease;
}
.sofa-back {
position: absolute;
left: 9%;
top: 0;
width: 82%;
height: 64%;
border-radius: 25px 25px 12px 12px;
background:
radial-gradient(ellipse at 35% 26%, rgba(255,255,255,.08), transparent 38%),
linear-gradient(105deg, #59655b, #77806c 45%, #505d54);
box-shadow: inset 0 -18px 30px rgba(20,28,23,.22);
}
.sofa-seat {
position: absolute;
left: 5%;
bottom: 8%;
width: 91%;
height: 42%;
border-radius: 15px;
background: linear-gradient(#6d786b, #505c52);
box-shadow: inset 0 9px 15px rgba(255,255,255,.08), 0 8px 10px rgba(26,24,19,.22);
}
.sofa-arm {
position: absolute;
bottom: 4%;
width: 15%;
height: 71%;
border-radius: 18px 18px 10px 10px;
background: linear-gradient(100deg, #778171, #536056);
box-shadow: inset 6px 0 9px rgba(255,255,255,.06);
}
.sofa-arm.left { left: 0; }
.sofa-arm.right { right: 0; }
.sofa-cushion {
position: absolute;
top: 15%;
width: 37%;
height: 44%;
border-radius: 12px;
background: linear-gradient(145deg, #7a8374, #5b675d);
box-shadow: inset 0 0 0 1px rgba(255,255,255,.05), 0 7px 9px rgba(26,25,21,.16);
}
.sofa-cushion.one { left: 13%; transform: rotate(-2deg); }
.sofa-cushion.two { right: 13%; transform: rotate(2deg); }
.sofa-leg {
position: absolute;
bottom: -4%;
width: 5%;
height: 13%;
background: linear-gradient(90deg, #3a281d, #74523a);
}
.sofa-leg.left { left: 11%; }
.sofa-leg.right { right: 11%; }
.side-table {
position: absolute;
z-index: 6;
left: 7%;
top: 58%;
width: 14%;
height: 7%;
border-radius: 50%;
background: linear-gradient(90deg, #68452e, #9b704a 50%, #563924);
box-shadow: 0 16px 20px rgba(26,18,13,.28);
transition: transform 1.4s ease, opacity 1.2s ease;
}
.side-table::before,
.side-table::after {
content: "";
position: absolute;
top: 50%;
width: 8px;
height: 115px;
background: linear-gradient(90deg, #3c2b20, #765038);
}
.side-table::before { left: 18%; transform: perspective(120px) rotateX(-6deg); }
.side-table::after { right: 18%; transform: perspective(120px) rotateX(-6deg); }
.lamp {
position: absolute;
z-index: 7;
left: 11%;
top: 38%;
width: 7%;
height: 24%;
transition: transform 1.4s ease, opacity 1.2s ease;
}
.lamp-stem {
position: absolute;
left: 47%;
bottom: 0;
width: 7px;
height: 80%;
border-radius: 5px;
background: linear-gradient(90deg, #302b24, #9b825d 55%, #332a21);
}
.lamp-base {
position: absolute;
bottom: 0;
left: 22%;
width: 58%;
height: 12px;
border-radius: 50%;
background: #4b3a2b;
box-shadow: 0 5px 7px rgba(26,18,13,.35);
}
.lamp-shade {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 36%;
clip-path: polygon(22% 0, 78% 0, 100% 100%, 0 100%);
background: linear-gradient(90deg, #b79864, #e1c895 50%, #9c794c);
box-shadow: 0 15px 28px rgba(255,202,107,.34);
}
.lamp-glow {
position: absolute;
z-index: -1;
left: -120%;
top: -20%;
width: 340%;
height: 180%;
border-radius: 50%;
background: radial-gradient(circle, rgba(255,205,111,.3), rgba(255,195,93,.08) 42%, transparent 70%);
filter: blur(9px);
}
.speaker {
position: absolute;
z-index: 7;
top: 19%;
width: 23vw;
height: 23vw;
max-width: 340px;
max-height: 340px;
border-radius: 50%;
opacity: 0;
transform: scale(.72);
background:
radial-gradient(circle at 50% 50%, #252321 0 13%, #0e0e0e 14% 29%, #2b2926 30% 34%, #111 35% 66%, #26231f 67% 100%);
border: 8px solid #171513;
box-shadow: 0 18px 30px rgba(0,0,0,.38), inset 0 0 0 3px #3b3630;
transition: opacity 1s ease, transform 1.4s cubic-bezier(.2,.8,.2,1);
}
.speaker.left { left: 5%; }
.speaker.right { right: 5%; }
.speaker::after {
content: "";
position: absolute;
inset: 32%;
border-radius: 50%;
background: radial-gradient(circle at 38% 32%, #77736b, #242321 38%, #070707 72%);
box-shadow: 0 0 0 8px #121110, 0 0 0 10px #39352f;
}
.wall-equalizer {
position: absolute;
z-index: 6;
left: 39%;
top: 7%;
width: 22%;
height: 15%;
display: flex;
align-items: flex-end;
justify-content: center;
gap: 3px;
opacity: 0;
transition: opacity 1s ease;
}
.wall-equalizer i {
width: 5px;
height: 34%;
border-radius: 3px 3px 0 0;
background: linear-gradient(#fff0b1, #cf6d42);
box-shadow: 0 0 10px rgba(255,176,86,.5);
transform-origin: 50% 100%;
animation: equalize .52s ease-in-out infinite alternate;
animation-delay: calc(var(--i) * -.073s);
}
.vinyl-wrap {
position: absolute;
z-index: 8;
left: 50%;
top: 78%;
width: min(78vw, 980px);
aspect-ratio: 1;
transform: translate(-50%, -50%) perspective(900px) rotateX(64deg);
transform-origin: center;
opacity: 0;
transition: opacity 1.2s ease;
pointer-events: none;
}
.vinyl-disc {
position: absolute;
inset: 0;
border-radius: 50%;
background:
radial-gradient(circle, transparent 0 7%, #e39a48 7.2% 16%, #181818 16.5% 18%, transparent 18.5%),
repeating-radial-gradient(circle, #171717 0 4px, #292929 5px 6px, #101010 7px 9px);
box-shadow: 0 20px 35px rgba(0,0,0,.55), inset 0 0 45px rgba(255,255,255,.04);
animation: recordSpin 1.8s linear infinite;
}
.vinyl-label {
position: absolute;
left: 50%;
top: 50%;
width: 24%;
height: 24%;
transform: translate(-50%, -50%);
border-radius: 50%;
display: grid;
place-items: center;
background: radial-gradient(circle, #171717 0 9%, #dd8e45 10% 100%);
color: #241b16;
font-size: clamp(14px, 3vw, 38px);
font-weight: 900;
letter-spacing: -.08em;
}
/* Cat ------------------------------------------------------------ */
#catAnchor {
position: absolute;
z-index: 20;
left: 50%;
bottom: 3.7%;
width: clamp(270px, 29vw, 430px);
aspect-ratio: 520 / 650;
transform: translate3d(-50%, 0, 190px);
transform-style: preserve-3d;
will-change: transform;
}
#catButton {
position: absolute;
inset: 0;
z-index: 3;
width: 100%;
height: 100%;
padding: 0;
border: 0;
background: transparent;
cursor: pointer;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
#catModel {
position: absolute;
inset: 0;
transform-style: preserve-3d;
transform-origin: 50% 78%;
will-change: transform;
}
.cat-plane {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
overflow: visible;
filter: blur(var(--motion-blur)) drop-shadow(0 14px 11px rgba(30,22,16,.23));
backface-visibility: hidden;
}
.cat-plane.front { transform: translateZ(.8px); }
.cat-plane.side { transform: rotateY(90deg) translateZ(.8px); backface-visibility: visible; }
.cat-plane.back { transform: rotateY(180deg) translateZ(.8px); }
.breath-art {
transform-box: fill-box;
transform-origin: 50% 92%;
}
[data-stage="0"] .breath-art { animation: breathe 5.2s ease-in-out infinite; }
#catShadow {
position: absolute;
z-index: 1;
left: 50%;
bottom: 2.5%;
width: min(31vw, 370px);
height: 42px;
border-radius: 50%;
transform: translateX(-50%);
background: radial-gradient(ellipse, rgba(26,17,11,.48), rgba(26,17,11,.22) 44%, transparent 72%);
filter: blur(8px);
pointer-events: none;
}
#spinAura {
position: absolute;
z-index: 0;
left: 50%;
top: 43%;
width: 108%;
aspect-ratio: 1;
transform: translate(-50%, -50%) scaleX(.42);
border-radius: 50%;
opacity: 0;
background: conic-gradient(from 0deg, transparent, rgba(255,248,219,.22), transparent 28%, rgba(206,154,90,.16), transparent 61%);
filter: blur(12px);
mix-blend-mode: screen;
pointer-events: none;
}
/* Supporting oddities -------------------------------------------- */
#space {
position: absolute;
z-index: 0;
inset: 0;
opacity: 0;
background:
radial-gradient(circle at 52% 42%, rgba(81,61,134,.48), transparent 30%),
radial-gradient(circle at 22% 20%, rgba(37,88,116,.35), transparent 27%),
#080a16;
transition: opacity 1.4s ease;
}
#space i {
position: absolute;
width: 2px;
height: 2px;
border-radius: 50%;
background: #fff;
box-shadow: 0 0 7px #fff;
animation: twinkle 2.7s ease-in-out infinite alternate;
}
.gravity-glyphs {
position: absolute;
z-index: 9;
inset: 0;
opacity: 0;
color: rgba(255,242,203,.7);
font-size: clamp(22px, 3vw, 44px);
font-weight: 300;
letter-spacing: 1.1em;
text-shadow: 0 0 14px rgba(255,236,180,.4);
transition: opacity 1s ease;
pointer-events: none;
}
.gravity-glyphs span:nth-child(1) { position: absolute; left: 21%; top: 32%; transform: rotate(-18deg); }
.gravity-glyphs span:nth-child(2) { position: absolute; right: 19%; top: 27%; transform: rotate(21deg); }
.gravity-glyphs span:nth-child(3) { position: absolute; right: 31%; bottom: 25%; transform: rotate(8deg); }
.loop-path {
position: absolute;
z-index: 4;
left: 50%;
top: 45%;
width: min(86vw, 1180px);
height: min(38vw, 520px);
transform: translate(-50%, -50%);
opacity: 0;
overflow: visible;
filter: drop-shadow(0 0 8px rgba(255,213,126,.5));
transition: opacity 1.2s ease;
pointer-events: none;
}
.loop-path path {
fill: none;
stroke: rgba(255,231,165,.7);
stroke-width: 3;
stroke-linecap: round;
stroke-dasharray: 18 13;
animation: dashLoop .8s linear infinite;
}
.loop-path path.back-line {
stroke: rgba(255,128,67,.4);
stroke-width: 11;
stroke-dasharray: none;
filter: blur(7px);
}
.satellite {
position: absolute;
z-index: 24;
left: 50%;
top: 50%;
opacity: 0;
pointer-events: none;
will-change: transform;
}
.mug {
width: 54px;
height: 48px;
border-radius: 7px 7px 14px 14px;
background: linear-gradient(90deg, #efe4cf, #fff8e9 52%, #c7b89f);
box-shadow: 0 9px 12px rgba(35,24,16,.28), inset 0 -8px rgba(92,63,40,.08);
}
.mug::before {
content: "";
position: absolute;
left: 44px;
top: 9px;
width: 22px;
height: 25px;
border: 7px solid #e9deca;
border-left: 0;
border-radius: 0 50% 50% 0;
}
.mug::after {
content: "";
position: absolute;
left: 5px;
top: 4px;
width: 44px;
height: 8px;
border-radius: 50%;
background: #6b4430;
box-shadow: inset 0 2px 3px rgba(0,0,0,.35);
}
.steam {
position: absolute;
left: 16px;
top: -24px;
width: 20px;
height: 28px;
border-left: 3px solid rgba(255,255,255,.48);
border-radius: 50%;
filter: blur(1px);
animation: steam 1.2s ease-in-out infinite alternate;
}
.duck {
width: 76px;
height: 53px;
border-radius: 55% 48% 45% 48%;
background: linear-gradient(145deg, #ffe56b, #dbaa18);
box-shadow: 0 10px 16px rgba(40,28,10,.3), inset -8px -6px rgba(139,87,8,.12);
}
.duck::before {
content: "";
position: absolute;
right: -11px;
top: -20px;
width: 39px;
height: 39px;
border-radius: 50%;
background: linear-gradient(145deg, #fff07a, #dbaa18);
}
.duck::after {
content: "";
position: absolute;
right: -18px;
top: -8px;
width: 20px;
height: 10px;
border-radius: 50%;
background: #e97824;
box-shadow: -16px -18px 0 -3px #241d14;
}
#motes {
position: absolute;
z-index: 8;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.mote {
position: absolute;
width: 3px;
height: 3px;
border-radius: 50%;
opacity: 0;
background: rgba(255,235,188,.7);
box-shadow: 0 0 6px rgba(255,229,168,.5);
animation: rise 7s linear infinite;
animation-play-state: paused;
}
.click-ripple {
position: absolute;
z-index: 45;
width: 26px;
height: 26px;
border: 1px solid rgba(255,246,213,.8);
border-radius: 50%;
transform: translate(-50%, -50%);
animation: ripple .75s ease-out forwards;
pointer-events: none;
}
/* Interface ------------------------------------------------------ */
.hud {
position: absolute;
z-index: 60;
inset: 0 0 auto;
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: 22px 25px;
pointer-events: none;
}
.identity {
display: grid;
gap: 5px;
text-shadow: 0 2px 12px rgba(0,0,0,.4);
}
.identity small {
color: rgba(255,255,255,.56);
font-size: 9px;
font-weight: 700;
letter-spacing: .23em;
text-transform: uppercase;
}
.identity strong {
font-size: 12px;
font-weight: 750;
letter-spacing: .13em;
text-transform: uppercase;
}
#soundButton {
pointer-events: auto;
display: flex;
align-items: center;
gap: 8px;
min-height: 35px;
padding: 8px 12px;
color: rgba(255,255,255,.85);
background: rgba(20,15,12,.3);
border: 1px solid rgba(255,255,255,.2);
border-radius: 999px;
backdrop-filter: blur(10px);
cursor: pointer;
font-size: 9px;
font-weight: 750;
letter-spacing: .14em;
text-transform: uppercase;
transition: background .2s, border-color .2s, opacity .2s;
}
#soundButton:disabled { cursor: default; opacity: .45; }
#soundButton:not(:disabled):hover { background: rgba(255,255,255,.13); border-color: rgba(255,255,255,.45); }
.sound-icon {
width: 8px;
height: 8px;
border-radius: 50%;
background: #d4ef9b;
box-shadow: 0 0 8px rgba(212,239,155,.8);
}
#soundButton.muted .sound-icon { background: #d1a18b; box-shadow: none; }
.bottom-hud {
position: absolute;
z-index: 60;
left: 50%;
bottom: 20px;
transform: translateX(-50%);
display: grid;
justify-items: center;
gap: 11px;
width: min(90vw, 480px);
pointer-events: none;
}
#prompt {
color: rgba(255,255,255,.74);
font-size: 9px;
font-weight: 700;
letter-spacing: .24em;
text-align: center;
text-transform: uppercase;
text-shadow: 0 2px 10px rgba(0,0,0,.6);
}
#stageDots { display: flex; gap: 7px; }
#stageDots i {
width: 5px;
height: 5px;
border: 1px solid rgba(255,255,255,.55);
border-radius: 50%;
transition: background .4s, box-shadow .4s, transform .4s;
}
#stageDots i.on {
background: #fff4d1;
box-shadow: 0 0 8px rgba(255,235,173,.8);
transform: scale(1.25);
}
.vignette {
position: absolute;
z-index: 50;
inset: 0;
pointer-events: none;
background: radial-gradient(ellipse at 50% 43%, transparent 44%, rgba(24,14,9,.17) 76%, rgba(15,9,6,.5) 115%);
}
.grain-overlay {
position: absolute;
z-index: 51;
inset: 0;
width: 100%;
height: 100%;
opacity: .22;
mix-blend-mode: soft-light;
pointer-events: none;
}
/* Stage transformations ------------------------------------------- */
[data-stage="2"] .sofa { animation: sofaOrbit 5.2s ease-in-out infinite; }
[data-stage="2"] .side-table,
[data-stage="2"] .lamp { animation: tableFloat 4.3s ease-in-out infinite; }
[data-stage="2"] .plant { animation: plantFloat 4.8s ease-in-out infinite; }
[data-stage="2"] .art { animation: pictureWobble 3.9s ease-in-out infinite; }
[data-stage="3"] .floor { transform: perspective(900px) rotateX(62deg) translateY(18vh); filter: saturate(.8); }
[data-stage="3"] .rug { transform: translate(-50%, 5vh) perspective(850px) rotateX(67deg) scale(.94); opacity: .65; }
[data-stage="3"] .back-wall { transform: perspective(1300px) rotateY(-4deg) translateX(-2%); filter: brightness(.86); }
[data-stage="3"] .window {
left: 35%;
top: 7%;
width: 31%;
height: 53%;
border-radius: 50%;
border-width: 8px;
box-shadow: 0 0 75px rgba(101,120,255,.35), inset 0 0 45px rgba(0,0,0,.45);
}
[data-stage="3"] .window .outside {
background-color: #070a19;
background-image:
radial-gradient(circle at 24% 31%, #fff 0 1px, transparent 2px),
radial-gradient(circle at 72% 20%, #fff 0 1px, transparent 2px),
radial-gradient(circle at 62% 72%, #fff 0 1px, transparent 2px),
radial-gradient(circle at 18% 77%, #fff 0 1px, transparent 2px),
radial-gradient(ellipse at 52% 51%, rgba(103,61,153,.55), transparent 47%);
background-size: 71px 67px, 93px 87px, 61px 73px, 113px 91px, 100% 100%;
}
[data-stage="3"] .window::before { display: none; }
[data-stage="3"] .window::after { display: none; }
[data-stage="3"] .sofa { animation: sofaOrbit 3.6s ease-in-out infinite reverse; }
[data-stage="3"] .side-table,
[data-stage="3"] .lamp { animation: tableFloat 3.1s ease-in-out infinite reverse; }
[data-stage="3"] .plant { animation: plantFloat 3.4s ease-in-out infinite; }
[data-stage="3"] #space { opacity: .7; }
[data-stage="3"] .gravity-glyphs { opacity: .75; }
[data-stage="3"] .mote { opacity: .6; animation-play-state: running; }
[data-stage="4"] .back-wall {
background:
radial-gradient(circle at 50% 33%, rgba(224,102,54,.19), transparent 27%),
linear-gradient(135deg, #29231f, #171514 62%, #30231c);
filter: saturate(.82) brightness(.78);
}
[data-stage="4"] .floor {
transform: perspective(900px) rotateX(65deg) translateY(5vh);
filter: brightness(.58) saturate(.65);
}
[data-stage="4"] .rug { opacity: 0; }
[data-stage="4"] .art,
[data-stage="4"] .shelf { opacity: .16; }
[data-stage="4"] .plant { opacity: .23; transform: translate(31vw, -5vh) rotate(16deg) scale(.8); }
[data-stage="4"] .sofa {
animation: none;
opacity: .38;
transform: translate(16vw, 7vh) perspective(700px) rotateY(-14deg) rotateZ(-8deg) scale(.64);
}
[data-stage="4"] .side-table {
animation: none;
opacity: .4;
transform: translate(-24vw, -7vh) scale(.7) rotateZ(8deg);
}
[data-stage="4"] .lamp {
animation: none;
opacity: .38;
transform: translate(-29vw, -11vh) scale(.75) rotate(-12deg);
}
[data-stage="4"] .window {
left: 38.5%;
top: 7%;
width: 24%;
height: 24%;
border-radius: 50%;
border-width: 5px;
box-shadow: 0 0 65px rgba(255,137,74,.38), inset -18px -13px 30px rgba(63,24,47,.55);
}
[data-stage="4"] .window .outside {
background:
radial-gradient(circle at 32% 28%, #ffe1a4 0 7%, transparent 8%),
radial-gradient(circle at 42% 43%, #e77b47 0 28%, #9c3d3c 55%, #382039 100%);
}
[data-stage="4"] .window::before,
[data-stage="4"] .window::after { display: none; }
[data-stage="4"] .speaker { opacity: 1; transform: scale(1); }
[data-stage="4"] .wall-equalizer { opacity: .8; }
[data-stage="4"] .vinyl-wrap { opacity: 1; }
[data-stage="4"] #space { opacity: .3; }
[data-stage="4"] .loop-path { opacity: 1; }
[data-stage="4"] .mote { opacity: .5; }
@keyframes breathe {
0%, 100% { transform: scale(1, 1); }
50% { transform: scale(1.006, 1.012); }
}
@keyframes sofaOrbit {
0%, 100% { transform: translate3d(0,0,-15px) perspective(700px) rotateY(-5deg) rotateZ(-1deg); }
50% { transform: translate3d(-14px,-32px,45px) perspective(700px) rotateY(7deg) rotateZ(2deg); }
}
@keyframes tableFloat {
0%, 100% { transform: translate3d(0,0,0) rotateZ(-1deg); }
50% { transform: translate3d(9px,-36px,55px) rotateZ(4deg); }
}
@keyframes plantFloat {
0%, 100% { transform: translate3d(0,0,0) rotateZ(0); }
50% { transform: translate3d(-9px,-25px,40px) rotateZ(5deg); }
}
@keyframes pictureWobble {
0%, 100% { transform: rotate(-1deg); }
50% { transform: rotate(2.5deg) translateY(-3px); }
}
@keyframes recordSpin { to { transform: rotate(360deg); } }
@keyframes equalize { from { transform: scaleY(.35); } to { transform: scaleY(1); } }
@keyframes dashLoop { to { stroke-dashoffset: -62; } }
@keyframes twinkle { from { opacity: .25; transform: scale(.7); } to { opacity: .9; transform: scale(1.3); } }
@keyframes steam { from { transform: translateX(-3px) scale(.8); opacity: .2; } to { transform: translateX(5px) scale(1.1); opacity: .65; } }
@keyframes rise {
from { transform: translate3d(0,0,0); }
to { transform: translate3d(35px,-105vh,0); }
}
@keyframes ripple {
from { opacity: .8; transform: translate(-50%,-50%) scale(.4); }
to { opacity: 0; transform: translate(-50%,-50%) scale(13); }
}
@media (max-width: 700px) {
.hud { padding: 16px; }
.identity small { display: none; }
#catAnchor { width: min(76vw, 390px); bottom: 5%; }
#catShadow { width: 54vw; bottom: 3.8%; }
.window { left: 4%; width: 28%; height: 35%; }
.art { left: 43%; width: 20%; }
.shelf { left: 41%; width: 30%; }
.plant { left: 28%; transform: scale(.78); transform-origin: bottom; }
.sofa { right: -11%; width: 57%; }
.side-table { left: 5%; width: 21%; }
.lamp { left: 10%; width: 10%; }
.wall-equalizer { left: 35%; width: 30%; }
.speaker { width: 34vw; height: 34vw; }
.speaker.left { left: -6%; }
.speaker.right { right: -6%; }
}
@media (prefers-reduced-motion: reduce) {
[data-stage="0"] .breath-art { animation: none; }
}
</style>
</head>
<body>
<!-- Reusable, inline vector model. The room and cat share no external assets. -->
<svg class="svg-defs" aria-hidden="true">
<defs>
<linearGradient id="furBody" x1="15%" y1="5%" x2="85%" y2="95%">
<stop offset="0" stop-color="#fffdf5"/>
<stop offset=".42" stop-color="#eee4d3"/>
<stop offset=".78" stop-color="#d8c8b0"/>
<stop offset="1" stop-color="#bca88c"/>
</linearGradient>
<radialGradient id="furFace" cx="37%" cy="24%" r="78%">
<stop offset="0" stop-color="#fffef9"/>
<stop offset=".55" stop-color="#f4ecdd"/>
<stop offset=".85" stop-color="#d8c9b3"/>
<stop offset="1" stop-color="#b9a68d"/>
</radialGradient>
<linearGradient id="furChest" x1="0" y1="0" x2="100%" y2="100%">
<stop offset="0" stop-color="#fffef9"/>
<stop offset=".58" stop-color="#eee4d3"/>
<stop offset="1" stop-color="#cdbba1"/>
</linearGradient>
<linearGradient id="earPink" x1="0" y1="0" x2="0" y2="100%">
<stop offset="0" stop-color="#d99e96"/>
<stop offset=".55" stop-color="#c9827d"/>
<stop offset="1" stop-color="#a75f60"/>
</linearGradient>
<radialGradient id="iris" cx="35%" cy="30%" r="70%">
<stop offset="0" stop-color="#a9c6bd"/>
<stop offset=".42" stop-color="#769c96"/>
<stop offset="1" stop-color="#425f60"/>
</radialGradient>
<linearGradient id="nose" x1="0" y1="0" x2="0" y2="100%">
<stop offset="0" stop-color="#d99c96"/>
<stop offset="1" stop-color="#9f5d5c"/>
</linearGradient>
<filter id="furTexture" x="-8%" y="-8%" width="116%" height="116%">
<feTurbulence type="fractalNoise" baseFrequency=".035 .48" numOctaves="2" seed="12" result="noise"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale=".8" xChannelSelector="R" yChannelSelector="G"/>
</filter>
<filter id="grainFilter" x="0" y="0" width="100%" height="100%">
<feTurbulence type="fractalNoise" baseFrequency=".72" numOctaves="3" seed="8" stitchTiles="stitch"/>
<feColorMatrix type="saturate" values="0"/>
<feComponentTransfer>
<feFuncA type="table" tableValues="0 .22"/>
</feComponentTransfer>
</filter>
</defs>
<symbol id="catFront" viewBox="0 0 520 650">
<g>
<g filter="url(#furTexture)">
<path d="M330 548 C409 561 472 522 460 469 C452 431 413 431 396 458 C386 474 398 492 415 490"
fill="none" stroke="url(#furBody)" stroke-width="45" stroke-linecap="round"/>
<path d="M339 539 C409 546 453 515 447 478" fill="none" stroke="#fff9ed" stroke-width="9" stroke-linecap="round" opacity=".42"/>
<path d="M185 570 C174 530 174 462 183 398 C191 344 216 307 258 306 C303 305 333 350 342 407 C351 465 352 526 329 572 Z"
fill="url(#furBody)"/>
<path d="M185 467 C151 467 133 505 143 549 C149 574 178 583 209 567 C222 537 214 483 185 467 Z"
fill="url(#furBody)"/>
<path d="M337 466 C371 468 390 505 378 550 C371 576 342 582 314 565 C301 532 307 485 337 466 Z"
fill="url(#furBody)"/>
<path d="M214 305 C196 337 194 408 197 480 L198 557 C211 570 244 570 257 557 L254 353 C253 326 239 309 214 305 Z"
fill="url(#furChest)"/>
<path d="M273 307 C258 339 259 408 261 480 L261 557 C276 570 307 568 320 554 L316 394 C313 341 297 315 273 307 Z"
fill="url(#furChest)"/>
<ellipse cx="198" cy="559" rx="48" ry="25" fill="url(#furFace)"/>
<ellipse cx="317" cy="558" rx="47" ry="25" fill="url(#furFace)"/>
<path d="M172 551 C172 537 185 529 207 531 C225 532 237 540 237 553 C237 570 218 577 196 575 C178 573 172 565 172 551 Z"
fill="url(#furFace)"/>
<path d="M281 551 C282 537 295 530 316 531 C338 532 349 541 348 554 C347 569 329 576 307 574 C289 573 281 565 281 551 Z"
fill="url(#furFace)"/>
<path d="M159 149 C133 123 137 79 149 47 C179 58 205 80 216 112 Z" fill="url(#furFace)"/>
<path d="M361 148 C386 122 383 78 369 47 C341 58 315 82 305 111 Z" fill="url(#furFace)"/>
<path d="M166 128 C153 111 155 87 161 70 C178 80 192 94 198 112 Z" fill="url(#earPink)"/>
<path d="M355 127 C367 109 364 85 358 69 C342 80 328 94 322 112 Z" fill="url(#earPink)"/>
<path d="M165 120 C191 91 229 82 263 88 C303 85 342 100 364 132 C385 164 382 217 363 255 C342 296 304 318 260 318 C214 318 177 296 155 256 C135 220 138 158 165 120 Z"
fill="url(#furFace)"/>
<path d="M179 143 C193 112 229 100 258 103 C232 121 219 144 211 175 C195 194 169 188 165 167 C164 157 169 149 179 143 Z"
fill="#fffdf7" opacity=".55"/>
<path d="M278 101 C310 97 343 112 356 139 C364 156 365 178 355 193 C334 185 318 176 310 156 C303 137 293 117 278 101 Z"
fill="#d2bea3" opacity=".22"/>
</g>
<path d="M179 177 C192 157 228 156 241 176 C249 190 242 211 225 219 C202 221 178 205 179 177 Z"
fill="#f8f3e8" stroke="#8e806e" stroke-width="2.2"/>
<path d="M280 176 C293 155 329 157 342 177 C346 192 337 212 317 219 C293 220 277 202 280 176 Z"
fill="#f8f3e8" stroke="#8e806e" stroke-width="2.2"/>
<ellipse cx="210" cy="185" rx="20" ry="22" fill="url(#iris)"/>
<ellipse cx="311" cy="184" rx="20" ry="22" fill="url(#iris)"/>
<ellipse cx="211" cy="184" rx="10" ry="15" fill="#11100e"/>
<ellipse cx="310" cy="183" rx="10" ry="15" fill="#11100e"/>
<circle cx="204" cy="177" r="5.4" fill="#fff" opacity=".95"/>
<circle cx="304" cy="176" r="5.4" fill="#fff" opacity=".95"/>
<circle cx="217" cy="193" r="2.2" fill="#cde8ec" opacity=".72"/>
<circle cx="316" cy="192" r="2.2" fill="#cde8ec" opacity=".72"/>
<ellipse cx="225" cy="238" rx="38" ry="28" fill="#fffaf0" opacity=".8"/>
<ellipse cx="294" cy="238" rx="38" ry="28" fill="#fffaf0" opacity=".8"/>
<path d="M247 226 Q260 217 274 226 Q271 239 260 242 Q249 239 247 226 Z" fill="url(#nose)"/>
<path d="M260 241 C260 252 249 255 243 248 M260 241 C260 252 271 255 278 248"
fill=" none" stroke="#6f5d4e" stroke-width="2.2" stroke-linecap="round"/>
<g fill="#8e7766" opacity=".55">
<circle cx="220" cy="237" r="1.5"/><circle cx="230" cy="233" r="1.4"/><circle cx="225" cy="245" r="1.3"/>
<circle cx="299" cy="237" r="1.5"/><circle cx="289" cy="233" r="1.4"/><circle cx="294" cy="245" r="1.3"/>
</g>
<g fill="none" stroke="#fdf9ed" stroke-linecap="round" opacity=".78">
<path d="M199 239 C158 229 123 224 83 229" stroke-width="2"/>
<path d="M198 247 C151 246 112 251 74 261" stroke-width="1.7"/>
<path d="M202 255 C159 263 127 273 98 286" stroke-width="1.5"/>
<path d="M320 238 C360 228 395 222 438 228" stroke-width="2"/>
<path d="M321 247 C367 244 405 250 447 260" stroke-width="1.7"/>
<path d="M317 255 C359 261 391 270 421 284" stroke-width="1.5"/>
</g>
<g fill="none" stroke-linecap="round">
<path d="M182 139 l8 -9 M195 126 l8 -10 M211 117 l7 -9 M229 111 l5 -9 M248 108 l3 -8 M267 108 l-2 -8 M286 110 l-4 -8 M304 115 l-6 -8 M321 123 l-7 -7"
stroke="#fff" stroke-width="2" opacity=".48"/>
<path d="M193 239 C218 262 219 316 212 364 M204 237 C225 274 225 318 221 369 M235 254 C242 292 239 338 237 389 M281 252 C275 296 279 342 281 390 M302 248 C291 278 294 326 296 370 M318 238 C304 266 307 302 304 347"
stroke="#fffdf6" stroke-width="3" opacity=".37"/>
<path d="M165 489 C178 506 192 513 208 514 M312 488 C326 503 340 508 353 504 M223 538 C237 542 247 542 258 538"
stroke="#a7957d" stroke-width="1.5" opacity=".28"/>
<path d="M184 552 v10 M198 550 v12 M213 551 v10 M291 551 v10 M306 550 v12 M320 551 v10"
stroke="#8f7b66" stroke-width="1.4" opacity=".4"/>
</g>
</g>
</symbol>
<symbol id="catSide" viewBox="0 0 520 650">
<g>
<g filter="url(#furTexture)">
<path d="M315 546 C393 558 459 524 450 474 C442 435 398 440 386 467 C378 486 394 500 411 490"
fill="none" stroke="url(#furBody)" stroke-width="46" stroke-linecap="round"/>
<path d="M208 565 C183 522 183 444 198 383 C208 339 234 307 273 313 C319 319 350 366 355 420 C360 477 352 529 330 570 Z"
fill="url(#furBody)"/>
<path d="M294 453 C339 450 365 489 357 535 C351 573 316 586 281 566 C267 529 268 481 294 453 Z"
fill="url(#furBody)"/>
<path d="M210 326 C190 357 190 428 192 492 L194 555 C207 568 237 568 249 555 L247 370 C244 340 232 322 210 326 Z"
fill="url(#furChest)"/>
<path d="M242 325 C232 367 234 437 236 494 L237 555 C250 567 278 566 290 553 L285 394 C280 350 266 327 242 325 Z"
fill="url(#furChest)"/>
<ellipse cx="211" cy="556" rx="44" ry="24" fill="url(#furFace)"/>
<ellipse cx="274" cy="555" rx="44" ry="24" fill="url(#furFace)"/>
<path d="M139 147 C116 120 121 81 134 53 C161 66 180 87 184 113 Z" fill="url(#furFace)"/>
<path d="M195 116 C220 93 256 95 278 119 C301 144 300 185 279 215 L229 246 C193 257 156 238 137 210 C112 173 122 139 139 147 Z"
fill="url(#furFace)"/>
<path d="M141 133 C130 115 133 94 138 78 C151 87 160 97 165 111 Z" fill="url(#earPink)"/>
<path d="M190 135 C173 143 154 158 145 181 C137 204 145 226 164 236 C183 245 204 241 221 231 C212 207 208 178 215 149 Z"
fill="#fffdf6" opacity=".62"/>
</g>
<path d="M151 181 C164 164 188 166 196 183 C200 197 190 211 174 214 C157 211 146 197 151 181 Z"
fill="#f8f3e8" stroke="#8e806e" stroke-width="2"/>
<ellipse cx="174" cy="188" rx="13" ry="16" fill="url(#iris)"/>
<ellipse cx="174" cy="188" rx="7" ry="11" fill="#11100e"/>
<circle cx="170" cy="182" r="3.8" fill="#fff"/>
<path d="M137 215 Q121 207 108 216 Q123 226 138 223" fill="url(#nose)"/>
<path d="M138 223 C130 231 120 231 113 226" fill="none" stroke="#6f5d4e" stroke-width="2" stroke-linecap="round"/>
<g fill="none" stroke="#fdf9ed" stroke-linecap="round" opacity=".75">
<path d="M130 217 C98 206 73 204 48 210 M130 224 C96 221 68 224 42 233 M132 231 C100 235 78 241 57 251" stroke-width="1.8"/>
<path d="M190 132 l7 -9 M207 123 l7 -8 M224 120 l6 -8 M161 142 l-6 -8" stroke-width="2" opacity=".5"/>
<path d="M205 256 C213 296 211 344 208 388 M220 258 C229 305 225 350 223 397 M249 333 C241 370 244 412 245 443"
stroke-width="3" opacity=".38"/>
</g>
</g>
</symbol>
<symbol id="catBack" viewBox="0 0 520 650">
<g>
<g filter="url(#furTexture)">
<path d="M325 544 C416 550 465 503 444 464 C429 437 391 446 387 469 C384 488 400 498 416 487"
fill="none" stroke="url(#furBody)" stroke-width="45" stroke-linecap="round"/>
<path d="M174 570 C159 524 163 444 180 384 C193 335 220 305 260 304 C302 303 330 337 343 388 C360 449 361 525 339 571 Z"
fill="url(#furBody)"/>
<path d="M184 462 C148 466 131 506 142 550 C150 577 179 582 211 566 C222 533 211 480 184 462 Z"
fill="url(#furBody)"/>
<path d="M337 460 C373 465 390 504 378 551 C370 577 340 583 312 566 C301 531 307 479 337 460 Z"
fill="url(#furBody)"/>
<path d="M206 306 C197 346 197 431 201 556 C220 570 243 570 258 557 L258 342 C257 318 240 303 206 306 Z"
fill="url(#furChest)"/>
<path d="M269 305 C282 343 282 430 278 557 C296 570 318 568 330 554 L325 390 C319 341 297 305 269 305 Z"
fill="url(#furChest)"/>
<ellipse cx="204" cy="558" rx="47" ry="24" fill="url(#furFace)"/>
<ellipse cx="318" cy="557" rx="46" ry="24" fill="url(#furFace)"/>
<path d="M161 147 C136 119 140 80 151 49 C180 61 203 82 215 112 Z" fill="url(#furFace)"/>
<path d="M360 146 C385 119 380 79 368 48 C340 60 316 82 305 111 Z" fill="url(#furFace)"/>
<path d="M165 118 C192 88 231 82 261 87 C295 83 333 96 356 123 C375 145 378 192 360 224 C338 257 300 275 260 276 C215 276 177 253 157 218 C140 187 142 143 165 118 Z"
fill="url(#furFace)"/>
<path d="M260 101 C240 126 237 160 260 184 C283 160 282 126 260 101 Z" fill="#fffdf6" opacity=".25"/>
</g>
<g fill="none" stroke="#fffdf6" stroke-linecap="round" opacity=".44">
<path d="M181 132 C216 114 231 112 258 132 C286 111 305 112 337 134" stroke-width="3"/>
<path d="M195 203 C219 225 239 236 260 238 C282 235 303 222 325 201" stroke-width="2.5"/>
<path d="M211 263 C220 295 218 340 216 389 M236 273 C239 313 237 356 236 402 M284 270 C281 318 285 356 284 402 M308 258 C298 297 302 337 302 387" stroke-width="3"/>
<path d="M179 520 C191 535 207 541 221 540 M301 540 C317 539 332 531 343 516" stroke-width="2"/>
</g>
</g>
</symbol>
</svg>
<main id="experience" data-stage="0" aria-label="An interactive Oiia cat in a living room">
<div id="space" aria-hidden="true"></div>
<div id="room" aria-hidden="true">
<div class="back-wall"></div>
<div class="floor"></div>
<div class="rug"></div>
<div class="sunbeam" id="sunbeam"></div>
<div class="window"><div class="outside"></div></div>
<div class="art"></div>
<div class="shelf">
<i class="book"></i><i class="book"></i><i class="book"></i><i class="book"></i><i class="book"></i><i class="book"></i>
<i class="shelf-frame"></i>
</div>
<div class="plant">
<i></i><i></i><i></i><i></i><i></i><i></i>
<div class="plant-pot"></div>
</div>
<div class="sofa">
<div class="sofa-back"></div>
<div class="sofa-seat"></div>
<div class="sofa-cushion one"></div>
<div class="sofa-cushion two"></div>
<div class="sofa-arm left"></div>
<div class="sofa-arm right"></div>
<div class="sofa-leg left"></div>
<div class="sofa-leg right"></div>
</div>
<div class="side-table"></div>
<div class="lamp">
<div class="lamp-glow"></div>
<div class="lamp-stem"></div>
<div class="lamp-base"></div>
<div class="lamp-shade"></div>
</div>
<div class="wall-equalizer" aria-hidden="true">
<i style="--i:0"></i><i style="--i:1"></i><i style="--i:2"></i><i style="--i:3"></i>
<i style="--i:4"></i><i style="--i:5"></i><i style="--i:6"></i><i style="--i:7"></i>
<i style="--i:8"></i><i style="--i:9"></i><i style="--i:10"></i><i style="--i:11"></i>
<i style="--i:12"></i><i style="--i:13"></i><i style="--i:14"></i><i style="--i:15"></i>
</div>
<div class="speaker left"></div>
<div class="speaker right"></div>
<div class="vinyl-wrap">
<div class="vinyl-disc"><div class="vinyl-label">OI</div></div>
</div>
</div>
<div class="gravity-glyphs" aria-hidden="true"><span>↑</span><span>↑</span><span>↑</span></div>
<svg class="loop-path" viewBox="0 0 1000 460" aria-hidden="true">
<path class="back-line" d="M115 230 C115 70 375 70 500 230 C625 390 885 390 885 230 C885 70 625 70 500 230 C375 390 115 390 115 230"/>
<path d="M115 230 C115 70 375 70 500 230 C625 390 885 390 885 230 C885 70 625 70 500 230 C375 390 115 390 115 230"/>
</svg>
<div id="motes" aria-hidden="true"></div>
<div id="catAnchor">
<div id="spinAura"></div>
<button id="catButton" type="button" aria-label="Click the cat to make it spin">
<div id="catModel">
<svg class="cat-plane front" viewBox="0 0 520 650" aria-hidden="true"><g class="breath-art"><use href="#catFront" width="520" height="650"/></g></svg>
<svg class="cat-plane side" viewBox="0 0 520 650" aria-hidden="true"><g class="breath-art"><use href="#catSide" width="520" height="650"/></g></svg>
<svg class="cat-plane back" viewBox="0 0 520 650" aria-hidden="true"><g class="breath-art"><use href="#catBack" width="520" height="650"/></g></svg>
</div>
</button>
</div>
<div id="catShadow" aria-hidden="true"></div>
<div class="satellite mug" id="mug" aria-hidden="true">
<div class="steam"></div>
</div>
<div class="satellite duck" id="duck" aria-hidden="true"></div>
<div class="hud">
<div class="identity">
<small>Domestic motion study / 01</small>
<strong id="stageLabel">Quiet room</strong>
</div>
<button id="soundButton" type="button" disabled aria-pressed="false">
<span class="sound-icon"></span>
<span id="soundText">Sound armed</span>
</button>
</div>
<div class="bottom-hud">
<div id="prompt" aria-live="polite">Click the cat</div>
<div id="stageDots" aria-hidden="true"><i></i><i></i><i></i><i></i></div>
</div>
<div class="vignette" aria-hidden="true"></div>
<svg class="grain-overlay" aria-hidden="true"><rect width="100%" height="100%" filter="url(#grainFilter)"/></svg>
</main>
<script>
(() => {
"use strict";
const experience = document.getElementById("experience");
const room = document.getElementById("room");
const catAnchor = document.getElementById("catAnchor");
const catModel = document.getElementById("catModel");
const catButton = document.getElementById("catButton");
const catShadow = document.getElementById("catShadow");
const spinAura = document.getElementById("spinAura");
const stageLabel = document.getElementById("stageLabel");
const prompt = document.getElementById("prompt");
const stageDots = [...document.querySelectorAll("#stageDots i")];
const soundButton = document.getElementById("soundButton");
const soundText = document.getElementById("soundText");
const sunbeam = document.getElementById("sunbeam");
const mug = document.getElementById("mug");
const duck = document.getElementById("duck");
const space = document.getElementById("space");
const motes = document.getElementById("motes");
const stageData = [
{ name: "Quiet room", speed: 0, lift: 0 },
{ name: "Liftoff", speed: 360, lift: .055 },
{ name: "Room orbit", speed: 680, lift: .125 },
{ name: "Gravity off", speed: 1120, lift: .215 },
{ name: "Living-room turntable", speed: 1800, lift: .29 }
];
const state = {
level: 0,
stage: 0,
speed: 0,
targetSpeed: 0,
lift: 0,
targetLift: 0,
angle: 0,
muted: false,
lastTime: performance.now(),
pointerX: 0,
pointerY: 0,
smoothX: 0,
smoothY: 0,
turnAge: 10
};
function makeStars() {
for (let i = 0; i < 56; i++) {
const star = document.createElement("i");
star.style.left = `${(i * 37.17) % 100}%`;
star.style.top = `${(i * 61.73) % 100}%`;
star.style.animationDelay = `${-(i % 9) * .31}s`;
star.style.transform = `scale(${.6 + (i % 4) * .18})`;
space.appendChild(star);
}
}
function makeMotes() {
for (let i = 0; i < 28; i++) {
const mote = document.createElement("span");
mote.className = "mote";
mote.style.left = `${8 + (i * 29.7) % 84}%`;
mote.style.bottom = `${-8 + (i % 7) * 4}%`;
mote.style.animationDelay = `${-(i % 11) * .67}s`;
mote.style.animationDuration = `${6 + (i % 5) * 1.1}s`;
mote.style.width = mote.style.height = `${2 + (i % 3)}px`;
motes.appendChild(mote);
}
}
makeStars();
makeMotes();
window.addEventListener("pointermove", event => {
state.pointerX = (event.clientX / innerWidth - .5) * 2;
state.pointerY = (event.clientY / innerHeight - .5) * 2;
}, { passive: true });
window.addEventListener("pointerleave", () => {
state.pointerX = 0;
state.pointerY = 0;
});
function makeRipple(x, y) {
const ripple = document.createElement("span");
ripple.className = "click-ripple";
ripple.style.left = `${x}px`;
ripple.style.top = `${y}px`;
experience.appendChild(ripple);
setTimeout(() => ripple.remove(), 800);
}
function updateStageUI() {
experience.dataset.stage = state.stage;
experience.dataset.encore = Math.max(0, state.level - 4);
const baseNames = ["Quiet room", "Liftoff", "Room orbit", "Gravity off", "Living-room turntable"];
const encore = Math.max(0, state.level - 4);
stageLabel.textContent = encore ? `Living-room turntable / encore ${encore + 1}` : baseNames[state.stage];
const messages = [
"Click the cat",
"Liftoff // it has begun",
"The furniture is orbiting",
"Gravity has left the room",
"The living room is a turntable"
];
prompt.textContent = encore
? `Again. Louder. Loop ${encore + 1}.`
: messages[state.stage];
stageDots.forEach((dot, index) => dot.classList.toggle("on", index < state.stage));
}
function triggerCat(event) {
event.preventDefault();
state.level += 1;
state.stage = Math.min(4, state.level);
state.turnAge = 0;
const config = stageData[state.stage];
const encore = Math.max(0, state.level - 4);
state.targetSpeed = config.speed + encore * 180;
state.targetSpeed = Math.min(2680, state.targetSpeed);
state.targetLift = Math.min(innerHeight * .36, innerHeight * (config.lift + encore * .012));
updateStageUI();
startAudio();
const rect = catButton.getBoundingClientRect();
const x = event.clientX || rect.left + rect.width / 2;
const y = event.clientY || rect.top + rect.height / 2;
makeRipple(x, y);
}
catButton.addEventListener("click", triggerCat);
/* Web Audio: a formant-shaped glottal pulse, not a simple oscillator. */
let audio = null;
function makeImpulse(ctx, seconds, decay) {
const length = Math.floor(ctx.sampleRate * seconds);
const impulse = ctx.createBuffer(2, length, ctx.sampleRate);
for (let channel = 0; channel < 2; channel++) {
const data = impulse.getChannelData(channel);
for (let i = 0; i < length; i++) {
const envelope = Math.pow(1 - i / length, decay);
data[i] = (Math.random() * 2 - 1) * envelope;
}
}
return impulse;
}
function makeNoise(ctx) {
const length = Math.floor(ctx.sampleRate * .5);
const buffer = ctx.createBuffer(1, length, ctx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < length; i++) data[i] = Math.random() * 2 - 1;
return buffer;
}
function setupAudio() {
const AudioContext = window.AudioContext || window.webkitAudioContext;
if (!AudioContext) return;
const ctx = new AudioContext();
const master = ctx.createGain();
const compressor = ctx.createDynamicsCompressor();
const voiceBus = ctx.createGain();
const shaper = ctx.createWaveShaper();
const convolver = ctx.createConvolver();
const wet = ctx.createGain();
const dry = ctx.createGain();
master.gain.value = .5;
voiceBus.gain.value = .8;
wet.gain.value = .18;
dry.gain.value = .95;
compressor.threshold.value = -20;
compressor.knee.value = 18;
compressor.ratio.value = 5;
compressor.attack.value = .003;
compressor.release.value = .18;
const curve = new Float32Array(2048);
for (let i = 0; i < curve.length; i++) {
const x = i * 2 / (curve.length - 1) - 1;
curve[i] = Math.tanh(x * 1.65);
}
shaper.curve = curve;
shaper.oversample = "2x";
voiceBus.connect(shaper);
shaper.connect(dry);
shaper.connect(convolver);
convolver.connect(wet);
dry.connect(master);
wet.connect(master);
master.connect(compressor);
compressor.connect(ctx.destination);
const real = new Float32Array(28);
const imag = new Float32Array(28);
for (let n = 1; n < imag.length; n++) {
imag[n] = (1 / Math.pow(n, 1.18)) * (n % 2 ? 1 : .72);
}
const pulseWave = ctx.createPeriodicWave(real, imag, { disableNormalization: false });
const sub = ctx.createOscillator();
const subGain = ctx.createGain();
sub.type = "sine";
sub.frequency.value = 38;
subGain.gain.value = .012;
sub.connect(subGain);
subGain.connect(master);
sub.start();
audio = {
ctx,
master,
voiceBus,
pulseWave,
noiseBuffer: makeNoise(ctx),
convolver,
sub,
subGain,
voices: [],
nextPhrase: 0,
interval: null,
lastLevel: -1
};
convolver.buffer = makeImpulse(ctx, 1.15, 2.6);
}
function audioBasePitch() {
const ratio = Math.min(1, state.speed / 2600);
return 94 + ratio * 151 + state.stage * 2;
}
function speakOiia(time, base, duration) {
if (!audio) return;
const { ctx, voiceBus, pulseWave, noiseBuffer } = audio;
const sourceGain = ctx.createGain();
const oscillator = ctx.createOscillator();
const shimmer = ctx.createOscillator();
const shimmerGain = ctx.createGain();
const mix = ctx.createGain();
oscillator.setPeriodicWave(pulseWave);
shimmer.setPeriodicWave(pulseWave);
shimmer.detune.setValueAtTime(8, time);
shimmerGain.gain.setValueAtTime(.16, time);
const oEnd = time + duration * .20;
const iEnd = time + duration * .64;
const end = time + duration;
oscillator.frequency.setValueAtTime(base * .88, time);
oscillator.frequency.exponentialRampToValueAtTime(base, oEnd);
oscillator.frequency.linearRampToValueAtTime(base * 1.075, iEnd);
oscillator.frequency.exponentialRampToValueAtTime(base * .97, end);
shimmer.frequency.setValueAtTime(base * .88, time);
shimmer.frequency.exponentialRampToValueAtTime(base, oEnd);
shimmer.frequency.linearRampToValueAtTime(base * 1.075, iEnd);
shimmer.frequency.exponentialRampToValueAtTime(base * .97, end);
sourceGain.gain.setValueAtTime(.0001, time);
sourceGain.gain.exponentialRampToValueAtTime(.34, time + .025);
sourceGain.gain.setValueAtTime(.34, Math.max(time + .03, end - .055));
sourceGain.gain.exponentialRampToValueAtTime(.0001, end);
oscillator.connect(sourceGain);
shimmer.connect(shimmerGain);
shimmerGain.connect(sourceGain);
const f1 = ctx.createBiquadFilter();
const f2 = ctx.createBiquadFilter();
const f3 = ctx.createBiquadFilter();
f1.type = f2.type = f3.type = "bandpass";
f1.Q.value = 7;
f2.Q.value = 10;
f3.Q.value = 8;
f1.frequency.setValueAtTime(520, time);
f1.frequency.exponentialRampToValueAtTime(340, oEnd);
f1.frequency.exponentialRampToValueAtTime(260, iEnd);
f1.frequency.exponentialRampToValueAtTime(760, end);
f2.frequency.setValueAtTime(900, time);
f2.frequency.exponentialRampToValueAtTime(760, oEnd);
f2.frequency.exponentialRampToValueAtTime(2200, iEnd);
f2.frequency.exponentialRampToValueAtTime(1250, end);
f3.frequency.setValueAtTime(2550, time);
f3.frequency.linearRampToValueAtTime(2700, oEnd);
f3.frequency.linearRampToValueAtTime(3050, iEnd);
f3.frequency.linearRampToValueAtTime(2550, end);
f1.gain.setValueAtTime(.78, time);
f1.gain.linearRampToValueAtTime(.34, oEnd);
f1.gain.linearRampToValueAtTime(.18, iEnd);
f1.gain.linearRampToValueAtTime(.84, end);
f2.gain.setValueAtTime(.24, time);
f2.gain.linearRampToValueAtTime(.42, oEnd);
f2.gain.linearRampToValueAtTime(1.05, iEnd);
f2.gain.linearRampToValueAtTime(.76, end);
f3.gain.setValueAtTime(.08, time);
f3.gain.linearRampToValueAtTime(.13, oEnd);
f3.gain.linearRampToValueAtTime(.42, iEnd);
f3.gain.linearRampToValueAtTime(.2, end);
sourceGain.connect(f1);
sourceGain.connect(f2);
sourceGain.connect(f3);
f1.connect(mix);
f2.connect(mix);
f3.connect(mix);
mix.connect(voiceBus);
const breath = ctx.createBufferSource();
const highpass = ctx.createBiquadFilter();
const breathGain = ctx.createGain();
breath.buffer = noiseBuffer;
highpass.type = "highpass";
highpass.frequency.value = 2800;
breathGain.gain.setValueAtTime(.0001, time);
breathGain.gain.exponentialRampToValueAtTime(.028, time + .035);
breathGain.gain.exponentialRampToValueAtTime(.009, oEnd);
breathGain.gain.exponentialRampToValueAtTime(.0001, end);
breath.connect(highpass);
highpass.connect(breathGain);
breathGain.connect(mix);
oscillator.start(time);
shimmer.start(time);
breath.start(time);
oscillator.stop(end + .02);
shimmer.stop(end + .02);
breath.stop(end + .02);
const voice = { oscillator, shimmer, base, duration, nodes: [sourceGain, shimmerGain, mix, f1, f2, f3, highpass, breathGain] };
audio.voices.push(voice);
oscillator.onended = () => {
voice.nodes.forEach(node => { try { node.disconnect(); } catch (_) {} });
const index = audio.voices.indexOf(voice);
if (index !== -1) audio.voices.splice(index, 1);
};
}
function scheduleVoice() {
if (!audio) return;
const now = audio.ctx.currentTime;
if (audio.nextPhrase < now) audio.nextPhrase = now + .025;
while (audio.nextPhrase < now + .12) {
const ratio = Math.min(1, state.speed / 2600);
const duration = .64 - ratio * .31;
const base = audioBasePitch();
speakOiia(audio.nextPhrase, base, duration);
audio.nextPhrase += duration * .96;
}
}
function startAudio() {
if (!audio) setupAudio();
if (!audio) return;
audio.ctx.resume().catch(() => {});
if (audio.nextPhrase < audio.ctx.currentTime) audio.nextPhrase = audio.ctx.currentTime + .035;
if (!audio.interval) audio.interval = setInterval(scheduleVoice, 35);
soundButton.disabled = false;
soundButton.classList.remove("muted");
soundButton.setAttribute("aria-pressed", "false");
soundText.textContent = "Mute";
}
function updateAudio() {
if (!audio) return;
const now = audio.ctx.currentTime;
const ratio = Math.min(1, state.speed / 2600);
const targetBase = audioBasePitch();
audio.voices.forEach(voice => {
const cents = 1200 * Math.log2(Math.max(.5, targetBase / voice.base));
voice.oscillator.detune.setTargetAtTime(cents, now, .025);
voice.shimmer.detune.setTargetAtTime(cents + 8, now, .025);
});
const level = state.muted
? 0
: Math.min(.88, .36 + state.stage * .08 + Math.max(0, state.level - 4) * .035);
if (Math.abs(level - audio.lastLevel) > .008) {
audio.master.gain.setTargetAtTime(level, now, .035);
audio.lastLevel = level;
}
audio.sub.frequency.setTargetAtTime(35 + ratio * 38, now, .05);
audio.subGain.gain.setTargetAtTime(.01 + ratio * .035, now, .05);
document.documentElement.style.setProperty("--energy", ratio.toFixed(3));
}
soundButton.addEventListener("click", () => {
state.muted = !state.muted;
soundButton.classList.toggle("muted", state.muted);
soundButton.setAttribute("aria-pressed", String(state.muted));
soundText.textContent = state.muted ? "Unmute" : "Mute";
if (audio) audio.master.gain.setTargetAtTime(state.muted ? 0 : (.36 + state.stage * .08), audio.ctx.currentTime, .03);
});
document.addEventListener("keydown", event => {
if (event.key.toLowerCase() === "m" && !soundButton.disabled) soundButton.click();
});
/* Main loop: speed, blur, lift, room deformation, and the voice all use
the same physical spin value. */
function frame(now) {
const dt = Math.min(.05, (now - state.lastTime) / 1000);
state.lastTime = now;
state.turnAge += dt;
const time = now / 1000;
const pointerEase = 1 - Math.exp(-dt * 5);
state.smoothX += (state.pointerX - state.smoothX) * pointerEase;
state.smoothY += (state.pointerY - state.smoothY) * pointerEase;
const speedEase = 1 - Math.exp(-dt * 1.55);
state.speed += (state.targetSpeed - state.speed) * speedEase;
state.lift += (state.targetLift - state.lift) * speedEase;
state.angle = (state.angle + state.speed * dt) % 360;
const stage = state.stage;
let x = 0;
let y = 0;
if (stage === 2) {
x = Math.sin(time * 1.25) * Math.min(innerWidth * .085, 115);
y = Math.sin(time * 2.5) * 10;
} else if (stage === 3) {
x = Math.sin(time * .92) * Math.min(innerWidth * .14, 180);
y = Math.sin(time * 1.84) * 19;
} else if (stage === 4) {
x = Math.sin(time * 1.15) * Math.min(innerWidth * .23, 340);
y = Math.sin(time * 2.3) * 24;
}
const nod = state.turnAge < .55
? Math.sin((state.turnAge / .55) * Math.PI) * -4
: Math.sin(time * 4.1) * (stage ? .35 : 0);
const roll = Math.sin(time * (2.4 + stage * .37)) * (stage * .65);
const catZ = 190 + stage * 25;
catAnchor.style.transform =
`translate3d(calc(-50% + ${x.toFixed(2)}px), ${(-state.lift + y).toFixed(2)}px, ${catZ}px)`;
catModel.style.transform = `rotateY(${state.angle.toFixed(2)}deg) rotateZ(${roll.toFixed(2)}deg) rotateX(${nod.toFixed(2)}deg)`;
const blur = Math.max(0, (state.speed - 470) / 1270 * 3.8);
document.documentElement.style.setProperty("--motion-blur", `${blur.toFixed(2)}px`);
const spinRatio = Math.min(1, state.speed / 1800);
spinAura.style.opacity = Math.max(0, (spinRatio - .18) * .85).toFixed(3);
spinAura.style.transform = `translate(-50%, -50%) scaleX(${(.32 + spinRatio * .34).toFixed(3)}) rotate(${time * 90}deg)`;
const shadowFade = Math.max(.08, 1 - state.lift / Math.max(1, innerHeight * .32));
const shadowScale = Math.max(.45, 1 - state.lift / Math.max(1, innerHeight * .42));
catShadow.style.opacity = shadowFade.toFixed(3);
catShadow.style.transform = `translateX(calc(-50% + ${(x * .22).toFixed(2)}px)) scale(${shadowScale.toFixed(3)})`;
catShadow.style.filter = `blur(${(8 + state.lift / 30).toFixed(1)}px)`;
const activePointer = stage ? .35 : 1;
let roomX = state.smoothX * 8 * activePointer;
let roomY = state.smoothY * 5 * activePointer;
let roomYaw = state.smoothX * .65 * activePointer;
let roomPitch = -state.smoothY * .35 * activePointer;
let roomRoll = 0;
let roomScale = 1;
if (stage === 1) {
roomYaw += Math.sin(time * 1.35) * 1.2;
roomRoll = Math.sin(time * 1.1) * .45;
roomY += Math.sin(time * 2.2) * 2;
} else if (stage === 2) {
roomYaw += Math.sin(time * 1.1) * 2.8;
roomRoll = Math.sin(time * .7) * 1.15;
roomY += Math.sin(time * 1.8) * 4;
} else if (stage === 3) {
roomYaw += Math.sin(time * .78) * 6.5;
roomRoll = 3.2 + Math.sin(time * .72) * 1.8;
roomPitch += Math.sin(time * .9) * 1.7;
roomY += Math.sin(time * 1.2) * 5;
} else if (stage === 4) {
const encore = Math.max(0, state.level - 4);
roomYaw += Math.sin(time * 1.02) * (5 + encore * .7);
roomRoll = Math.sin(time * .78) * (2.2 + encore * .35);
roomPitch += Math.sin(time * .63) * 1.8;
roomScale = .94 - Math.min(.035, encore * .008);
roomX += Math.sin(time * .6) * 5;
}
room.style.transform =
`translate3d(${roomX.toFixed(2)}px, ${roomY.toFixed(2)}px, 0) rotateX(${roomPitch.toFixed(2)}deg) rotateY(${roomYaw.toFixed(2)}deg) rotateZ(${roomRoll.toFixed(2)}deg) scale(${roomScale.toFixed(3)})`;
sunbeam.style.transform =
`translateX(${(x * .22 + Math.sin(time * .7) * 5).toFixed(2)}px) rotate(${(stage * 1.5 + Math.sin(time * .5) * 1.2).toFixed(2)}deg)`;
if (stage >= 2) {
const radius = stage === 2 ? Math.min(innerWidth * .18, 135) : Math.min(innerWidth * .24, 225);
const orbitSpeed = stage === 4 ? 2.05 : 1.55;
const mx = Math.cos(time * orbitSpeed) * radius;
const my = Math.sin(time * orbitSpeed) * radius * .34 - state.lift * .45;
mug.style.opacity = "1";
mug.style.transform = `translate3d(calc(-50% + ${mx.toFixed(1)}px), calc(-50% + ${my.toFixed(1)}px), 230px) rotate(${(time * 80).toFixed(1)}deg)`;
} else {
mug.style.opacity = "0";
}
if (stage >= 3) {
const radius = stage === 4 ? Math.min(innerWidth * .25, 250) : Math.min(innerWidth * .2, 185);
const dx = -Math.cos(time * 1.2) * radius;
const dy = Math.sin(time * 1.2) * radius * .32 - state.lift * .28;
duck.style.opacity = "1";
duck.style.transform = `translate3d(calc(-50% + ${dx.toFixed(1)}px), calc(-50% + ${dy.toFixed(1)}px), 240px) rotate(${(time * 115).toFixed(1)}deg)`;
} else {
duck.style.opacity = "0";
}
updateAudio();
requestAnimationFrame(frame);
}
updateStageUI();
requestAnimationFrame(frame);
})();
</script>
</body>
</html>