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{height:100%;margin:0;overflow:hidden;background:#1a1512;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif}
.scene{position:fixed;inset:0;overflow:hidden;background:#2b2118}
.bg{position:absolute;inset:-6%;will-change:transform;transform-origin:50% 50%}
.wall{position:absolute;left:0;top:0;width:100%;height:64%;background:linear-gradient(180deg,#e8dcc8 0%,#dccdb2 55%,#cbb99a 100%);box-shadow:inset 0 -20px 40px rgba(0,0,0,.15)}
.wall::after{content:"";position:absolute;inset:0;background:repeating-linear-gradient(90deg,rgba(255,255,255,.06) 0 60px,rgba(0,0,0,.03) 60px 61px);opacity:.5}
.baseboard{position:absolute;left:0;top:61%;width:100%;height:3%;background:linear-gradient(#f5efe2,#d8cbb0);box-shadow:0 2px 4px rgba(0,0,0,.2)}
.floor{position:absolute;left:-2%;bottom:-2%;width:104%;height:42%;background:linear-gradient(180deg,rgba(0,0,0,.18),transparent 18%),repeating-linear-gradient(90deg,rgba(90,60,30,.35) 0 2px,transparent 2px 92px),repeating-linear-gradient(0deg,rgba(90,60,30,.18) 0 2px,transparent 2px 46px),linear-gradient(180deg,#b98a5a,#8a6238 60%,#6f4e2a);clip-path:polygon(6% 0,94% 0,100% 100%,0 100%)}
.rug{position:absolute;left:50%;bottom:4%;width:72%;height:20%;transform:translateX(-50%);background:radial-gradient(ellipse at center,#a33b3b 0%,#7d2a2a 45%,#5e1f1f 70%,#3f1515 100%);border-radius:50%;box-shadow:inset 0 0 0 6px rgba(255,220,150,.35),inset 0 0 0 12px rgba(0,0,0,.25),0 4px 12px rgba(0,0,0,.3);opacity:.95}
.rug::after{content:"";position:absolute;inset:12%;border-radius:50%;border:3px dashed rgba(255,220,150,.4)}
.window{position:absolute;left:5%;top:9%;width:23%;height:40%;background:#f3ead6;padding:8px;border-radius:4px;box-shadow:0 6px 18px rgba(0,0,0,.25),inset 0 0 0 2px #b8a88a;z-index:2}
.window .view{width:100%;height:100%;background:linear-gradient(180deg,#bfe3ff 0%,#eaf6ff 45%,#cfe8b8 46%,#8fb573 100%);position:relative;overflow:hidden;border-radius:2px}
.window .view::before{content:"";position:absolute;left:0;top:0;width:100%;height:100%;background:linear-gradient(90deg,transparent 48%,rgba(255,255,255,.9) 48% 52%,transparent 52%),linear-gradient(0deg,transparent 48%,rgba(255,255,255,.9) 48% 52%,transparent 52%)}
.window .view::after{content:"";position:absolute;right:12%;top:14%;width:22%;height:22%;background:radial-gradient(circle,#fff8c0,#ffd94d 60%,transparent 70%);border-radius:50%}
.light-shaft{position:absolute;left:16%;top:18%;width:58%;height:58%;background:linear-gradient(105deg,rgba(255,244,180,.55),rgba(255,244,180,.12) 60%,transparent);clip-path:polygon(0 0,38% 0,100% 100%,18% 100%);filter:blur(6px);mix-blend-mode:screen;opacity:.7;pointer-events:none;z-index:3}
.dust{position:absolute;width:5px;height:5px;background:rgba(255,255,220,.9);border-radius:50%;filter:blur(1px);animation:dustFloat 7s ease-in-out infinite;z-index:3}
@keyframes dustFloat{0%,100%{transform:translate(0,0);opacity:.4}50%{transform:translate(14px,-22px);opacity:1}}
.sofa{position:absolute;left:50%;bottom:16%;width:64%;height:30%;transform:translateX(-50%);z-index:2}
.sofa-back{position:absolute;left:4%;top:0;width:92%;height:62%;background:linear-gradient(180deg,#5a7a6a,#3f5a4d);border-radius:18px 18px 8px 8px;box-shadow:inset 0 4px 0 rgba(255,255,255,.15),0 8px 16px rgba(0,0,0,.25)}
.sofa-cushion{position:absolute;top:52%;width:44%;height:38%;background:linear-gradient(180deg,#6b8f7d,#4a6b5a);border-radius:10px;box-shadow:inset 0 3px 0 rgba(255,255,255,.18)}
.c1{left:6%}.c2{right:6%}
.sofa-arm{position:absolute;top:28%;width:10%;height:62%;background:#355044;border-radius:12px}
.a1{left:0}.a2{right:0}
.sofa-base{position:absolute;left:2%;bottom:0;width:96%;height:22%;background:#2f463c;border-radius:6px}
.lamp{position:absolute;right:7%;bottom:18%;width:70px;height:42%;z-index:2}
.lamp-shade{position:absolute;left:50%;top:0;width:64px;height:44px;transform:translateX(-50%);background:linear-gradient(180deg,#ffe9b0,#f5c86a);clip-path:polygon(18% 0,82% 0,100% 100%,0 100%);border-radius:4px}
.lamp-pole{position:absolute;left:50%;top:44px;width:6px;height:110px;transform:translateX(-50%);background:#3a2f26}
.lamp-base{position:absolute;left:50%;bottom:0;width:36px;height:10px;transform:translateX(-50%);background:#3a2f26;border-radius:50%}
.lamp-glow{position:absolute;left:50%;top:10px;width:180px;height:180px;transform:translate(-50%,-30%);background:radial-gradient(circle,rgba(255,220,130,.55),transparent 65%);border-radius:50%;mix-blend-mode:screen}
.plant{position:absolute;left:3%;bottom:16%;width:90px;height:36%;z-index:2}
.pot{position:absolute;bottom:0;left:50%;transform:translateX(-50%);width:52px;height:48px;background:linear-gradient(#b56545,#8a452e);border-radius:4px 4px 10px 10px}
.leaf{position:absolute;left:50%;bottom:38px;width:18px;height:64px;background:linear-gradient(#4a7c3a,#2f5a26);border-radius:50% 50% 50% 50%/60% 60% 40% 40%;transform-origin:bottom center}
.l1{transform:translateX(-50%) rotate(-28deg)}.l2{transform:translateX(-50%) rotate(-12deg);height:78px}.l3{transform:translateX(-50%) rotate(4deg);height:84px}.l4{transform:translateX(-50%) rotate(20deg);height:70px}.l5{transform:translateX(-50%) rotate(34deg)}
.frame{position:absolute;width:110px;height:140px;background:#f8f3e6;border:8px solid #5a4632;box-shadow:0 6px 14px rgba(0,0,0,.3);border-radius:2px;z-index:2}
.f1{right:16%;top:13%;background:linear-gradient(135deg,#e8b4b8,#a8c0d8 60%,#f5e6c8)}
.f2{right:6%;top:20%;width:84px;height:104px;background:linear-gradient(135deg,#c8d8a8,#e8d8a8)}
.table{position:absolute;right:16%;bottom:20%;width:110px;height:70px;z-index:2}
.table-top{position:absolute;top:0;left:0;width:100%;height:12px;background:#6b4a2f;border-radius:4px}
.table-leg{position:absolute;top:12px;width:8px;height:58px;background:#4a3320}
.tl1{left:10px}.tl2{right:10px}
.book{position:absolute;top:-14px;left:14px;width:36px;height:14px;background:#a33b3b;border-radius:2px;transform:rotate(-4deg)}
.book2{top:-26px;left:18px;background:#3b5a8a;width:30px}
.mug{position:absolute;top:-20px;right:16px;width:18px;height:16px;background:#f0f0f0;border-radius:2px 2px 6px 6px;border:2px solid #ccc}
.vortex{position:absolute;left:50%;top:48%;width:125vmin;height:125vmin;transform:translate(-50%,-50%);background:repeating-conic-gradient(from 0deg,rgba(255,80,80,.28) 0 20deg,rgba(255,200,50,.28) 20deg 40deg,rgba(80,255,160,.28) 40deg 60deg,rgba(80,160,255,.28) 60deg 80deg,rgba(200,80,255,.28) 80deg 100deg);border-radius:50%;opacity:0;pointer-events:none;mix-blend-mode:screen;filter:blur(2px);z-index:2;transition:opacity .6s}
body.stage4 .vortex{opacity:.9;animation:vortexSpin 5s linear infinite}
@keyframes vortexSpin{to{transform:translate(-50%,-50%) rotate(360deg) scale(1.12)}}
#bigOiia{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:19vw;font-weight:900;color:transparent;-webkit-text-stroke:3px rgba(255,255,255,.9);opacity:0;pointer-events:none;transform:rotate(-8deg);z-index:6;letter-spacing:.02em}
body.stage4 #bigOiia{opacity:.16;animation:bigPulse .9s ease-in-out infinite alternate}
@keyframes bigPulse{from{transform:rotate(-8deg) scale(.96)}to{transform:rotate(-4deg) scale(1.06)}}
#shadow{position:absolute;left:50%;top:79%;width:280px;height:52px;transform:translate(-50%,-50%);background:radial-gradient(ellipse,rgba(0,0,0,.45),transparent 70%);border-radius:50%;z-index:3;pointer-events:none}
.cloneCat{position:absolute;left:50%;top:58%;width:150px;aspect-ratio:400/430;margin-left:-75px;margin-top:-80px;display:none;z-index:4;pointer-events:none}
.cloneCat .cf,.cloneCat .cb{position:absolute;inset:0}
.cloneCat .cb{display:none}
.cloneCat svg{width:100%;height:100%;display:block;overflow:visible}
#catWrap{position:absolute;left:50%;top:58%;width:clamp(220px,34vw,360px);aspect-ratio:400/430;transform:translate(-50%,-50%);cursor:pointer;z-index:10}
#catWrap:active{cursor:grabbing}
.ghost{position:absolute;inset:0;opacity:0;pointer-events:none}
.gf,.gb{position:absolute;inset:0}
.gb{display:none}
.catFace,.catBack{position:absolute;inset:0;will-change:transform,filter}
.catBack{display:none}
.catFace svg,.catBack svg,.gf svg,.gb svg{width:100%;height:100%;display:block;overflow:visible;animation:breathe 3.6s ease-in-out infinite;transform-origin:50% 85%}
@keyframes breathe{0%,100%{transform:scale(1)}50%{transform:scale(1.012,1.018)}}
.lid{transform:scaleY(0);transform-box:fill-box;transform-origin:center;animation:blink 5.2s infinite}
@keyframes blink{0%,92%,100%{transform:scaleY(0)}95%{transform:scaleY(1)}}
.earL{transform-box:fill-box;transform-origin:bottom center;animation:twitch 7s infinite}
@keyframes twitch{0%,88%,100%{transform:rotate(0)}91%{transform:rotate(-6deg)}94%{transform:rotate(3deg)}}
.blurDisc{position:absolute;inset:8% 2%;border-radius:50%;background:radial-gradient(ellipse,rgba(247,232,195,.95),rgba(255,255,255,.45) 60%,transparent 72%);opacity:0;filter:blur(6px);pointer-events:none}
.orbiter{position:absolute;left:50%;top:58%;width:52px;height:52px;margin:-26px;display:none;z-index:11;pointer-events:none}
.yarn{width:100%;height:100%;border-radius:50%;background:radial-gradient(circle at 32% 30%,#ffb0b0,#d93a3a 65%,#8a1f1f);box-shadow:inset -6px -6px 0 rgba(0,0,0,.18),inset 4px 4px 0 rgba(255,255,255,.25);position:relative;overflow:hidden}
.yarn::after{content:"";position:absolute;inset:0;border-radius:50%;background:repeating-linear-gradient(45deg,transparent 0 6px,rgba(0,0,0,.18) 6px 7px,transparent 7px 12px)}
.y2{background:radial-gradient(circle at 32% 30%,#a0e8ff,#2a8ad9 65%,#145a8a)}
.y3{background:radial-gradient(circle at 32% 30%,#c0ffb0,#3ab54a 65%,#1f6a2a)}
.star{width:100%;height:100%;background:#ffd94d;clip-path:polygon(50% 0%,61% 35%,98% 35%,68% 57%,79% 91%,50% 70%,21% 91%,32% 57%,2% 35%,39% 35%);filter:drop-shadow(0 2px 3px rgba(0,0,0,.3))}
.star.pink{background:#ff7ac8}
.fish{width:100%;height:70%;margin-top:15%;background:linear-gradient(#8ad4ff,#3a8ad9);border-radius:60% 40% 40% 60%;position:relative}
.fish::after{content:"";position:absolute;left:22%;top:32%;width:8px;height:8px;background:#111;border-radius:50%}
.speedlines{position:absolute;left:50%;top:50%;width:85vmin;height:85vmin;transform:translate(-50%,-50%);opacity:0;background:repeating-conic-gradient(from 0deg,transparent 0 10deg,rgba(255,255,255,.4) 10deg 12deg,transparent 12deg 24deg);border-radius:50%;-webkit-mask:radial-gradient(circle,transparent 30%,black 45%,transparent 70%);mask:radial-gradient(circle,transparent 30%,black 45%,transparent 70%);pointer-events:none;z-index:12}
#oiiaPop{position:absolute;left:50%;top:36%;transform:translate(-50%,-50%);font-weight:900;font-size:44px;color:#fff;text-shadow:3px 3px 0 #ff2a6d,-2px -2px 0 #00cfff,0 0 18px rgba(255,255,255,.6);opacity:0;pointer-events:none;letter-spacing:2px;z-index:13}
.vignette{position:absolute;inset:0;pointer-events:none;background:radial-gradient(ellipse at center,transparent 55%,rgba(0,0,0,.35) 100%);z-index:7}
#stageLabel{position:absolute;top:16px;left:16px;z-index:30;background:rgba(0,0,0,.65);color:#fff;padding:8px 14px;border-radius:999px;font-weight:800;letter-spacing:1px;opacity:0;transition:opacity .3s;font-size:14px}
#muteBtn,#resetBtn{position:absolute;top:16px;z-index:30;background:rgba(255,255,255,.92);border:0;padding:9px 14px;border-radius:999px;font-weight:800;cursor:pointer;box-shadow:0 4px 12px rgba(0,0,0,.25);font-size:13px}
#muteBtn{right:16px}
#resetBtn{right:132px;background:rgba(0,0,0,.55);color:#fff}
#hint{position:absolute;bottom:24px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,.68);color:#fff;padding:11px 20px;border-radius:999px;animation:hintPulse 1.6s ease-in-out infinite;z-index:20;font-weight:700;white-space:nowrap}
@keyframes hintPulse{0%,100%{transform:translateX(-50%) scale(1)}50%{transform:translateX(-50%) scale(1.06)}}
body.stage2 .sofa{animation:wobble .9s ease-in-out infinite}
@keyframes wobble{0%,100%{transform:translateX(-50%) rotate(-1deg)}50%{transform:translateX(-50%) rotate(1deg) translateY(-4px)}}
body.stage3 .sofa,body.stage4 .sofa{animation:floaty 3.2s ease-in-out infinite}
@keyframes floaty{0%,100%{transform:translateX(-50%) translateY(0) rotate(-1deg)}50%{transform:translateX(-50%) translateY(-22px) rotate(1.5deg)}}
body.stage3 .plant,body.stage4 .plant{animation:floaty2 3.8s ease-in-out infinite}
body.stage3 .lamp,body.stage4 .lamp{animation:floaty3 3s ease-in-out infinite}
body.stage3 .frame,body.stage4 .frame{animation:floaty2 4.5s ease-in-out infinite}
body.stage3 .table,body.stage4 .table{animation:floaty3 3.4s ease-in-out infinite}
body.stage4 .plant{animation-duration:1.7s}
body.stage4 .lamp{animation-duration:1.4s}
body.stage4 .frame{animation-duration:2s}
body.stage4 .table{animation-duration:1.6s}
body.stage4 .sofa{animation-duration:1.5s}
@keyframes floaty2{0%,100%{transform:translateY(0) rotate(0)}50%{transform:translateY(-30px) rotate(6deg)}}
@keyframes floaty3{0%,100%{transform:translateY(0) rotate(0)}50%{transform:translateY(-18px) rotate(-5deg)}}
</style>
</head>
<body class="stage0">
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<defs>
<linearGradient id="fur" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#fff8e7"/><stop offset="0.55" stop-color="#f7e8c3"/><stop offset="1" stop-color="#e6cf9e"/></linearGradient>
<linearGradient id="whiteFur" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#ffffff"/><stop offset="1" stop-color="#f2e8d5"/></linearGradient>
<radialGradient id="eyeGrad" cx="0.5" cy="0.45" r="0.6"><stop offset="0" stop-color="#ffe9a0"/><stop offset="0.6" stop-color="#d9a441"/><stop offset="1" stop-color="#8a5a1a"/></radialGradient>
<filter id="furNoise"><feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2" stitchTiles="stitch"/><feColorMatrix type="saturate" values="0"/></filter>
</defs>
<symbol id="catFront" viewBox="0 0 400 430">
<path d="M268 352 Q338 352 334 288 Q330 238 298 232" fill="none" stroke="#e8d5a8" stroke-width="30" stroke-linecap="round"/>
<path d="M268 352 Q338 352 334 288 Q330 238 298 232" fill="none" stroke="#d9b87a" stroke-width="30" stroke-linecap="round" stroke-dasharray="6 18" opacity="0.35"/>
<path d="M200 172 C138 172 102 228 102 296 C102 356 144 388 200 388 C256 388 298 356 298 296 C298 228 262 172 200 172 Z" fill="url(#fur)"/>
<ellipse cx="118" cy="332" rx="36" ry="48" fill="#eeddb4"/><ellipse cx="282" cy="332" rx="36" ry="48" fill="#eeddb4"/>
<path d="M200 208 C164 208 148 250 148 296 C148 342 170 372 200 372 C230 372 252 342 252 296 C252 250 236 208 200 208 Z" fill="url(#whiteFur)"/>
<rect x="162" y="282" width="30" height="88" rx="15" fill="#fffaf0"/><rect x="208" y="282" width="30" height="88" rx="15" fill="#fffaf0"/>
<ellipse cx="177" cy="372" rx="20" ry="12" fill="#fffaf0"/><ellipse cx="223" cy="372" rx="20" ry="12" fill="#fffaf0"/>
<ellipse cx="118" cy="372" rx="24" ry="13" fill="#fffaf0"/><ellipse cx="282" cy="372" rx="24" ry="13" fill="#fffaf0"/>
<path d="M118 260 Q138 268 150 260 M118 292 Q138 300 150 292 M282 260 Q262 268 250 260 M282 292 Q262 300 250 292" stroke="#d9b87a" stroke-width="5" fill="none" stroke-linecap="round" opacity="0.55"/>
<g class="earL"><path d="M132 78 L112 8 L182 38 Z" fill="url(#fur)" stroke="#d9b87a" stroke-width="2" stroke-linejoin="round"/><path d="M134 62 L124 28 L162 44 Z" fill="#f2b8c6"/></g>
<g class="earR"><path d="M268 78 L288 8 L218 38 Z" fill="url(#fur)" stroke="#d9b87a" stroke-width="2" stroke-linejoin="round"/><path d="M266 62 L276 28 L238 44 Z" fill="#f2b8c6"/></g>
<circle cx="200" cy="128" r="90" fill="url(#fur)"/>
<path d="M200 48 L200 72 M178 52 L184 74 M222 52 L216 74" stroke="#d9b87a" stroke-width="5" stroke-linecap="round" opacity="0.6"/>
<path d="M112 138 L92 148 L112 158 L96 168 L114 174 Z" fill="#f7e8c3"/><path d="M288 138 L308 148 L288 158 L304 168 L286 174 Z" fill="#f7e8c3"/>
<g><ellipse cx="162" cy="122" rx="25" ry="29" fill="#ffffff" stroke="#5a4632" stroke-width="1.5"/><circle cx="162" cy="124" r="17" fill="url(#eyeGrad)"/><ellipse cx="162" cy="124" rx="7.5" ry="15" fill="#111"/><circle cx="155" cy="113" r="5.5" fill="#fff"/><circle cx="168" cy="133" r="2.5" fill="#fff" opacity="0.9"/></g>
<g><ellipse cx="238" cy="122" rx="25" ry="29" fill="#ffffff" stroke="#5a4632" stroke-width="1.5"/><circle cx="238" cy="124" r="17" fill="url(#eyeGrad)"/><ellipse cx="238" cy="124" rx="7.5" ry="15" fill="#111"/><circle cx="231" cy="113" r="5.5" fill="#fff"/><circle cx="244" cy="133" r="2.5" fill="#fff" opacity="0.9"/></g>
<ellipse class="lid" cx="162" cy="122" rx="26" ry="30" fill="#f7e8c3"/><ellipse class="lid" cx="238" cy="122" rx="26" ry="30" fill="#f7e8c3"/>
<path d="M191 152 L209 152 L200 163 Z" fill="#e88a9a" stroke="#c66a7a" stroke-width="1"/>
<path d="M200 163 Q200 171 190 173 M200 163 Q200 171 210 173" stroke="#8a6a4a" stroke-width="2" fill="none" stroke-linecap="round"/>
<g stroke="#ffffff" stroke-width="1.6" opacity="0.85" stroke-linecap="round"><path d="M148 156 L96 144"/><path d="M148 166 L98 168"/><path d="M148 176 L106 188"/><path d="M252 156 L304 144"/><path d="M252 166 L302 168"/><path d="M252 176 L294 188"/></g>
<rect x="90" y="30" width="220" height="360" fill="#000" filter="url(#furNoise)" opacity="0.07"/>
<ellipse cx="200" cy="382" rx="90" ry="14" fill="#000" opacity="0.08"/>
</symbol>
<symbol id="catBack" viewBox="0 0 400 430">
<path d="M268 352 Q338 352 334 288 Q330 238 298 232" fill="none" stroke="#e8d5a8" stroke-width="30" stroke-linecap="round"/>
<path d="M200 172 C138 172 102 228 102 296 C102 356 144 388 200 388 C256 388 298 356 298 296 C298 228 262 172 200 172 Z" fill="url(#fur)"/>
<ellipse cx="118" cy="332" rx="36" ry="48" fill="#eeddb4"/><ellipse cx="282" cy="332" rx="36" ry="48" fill="#eeddb4"/>
<path d="M130 250 Q200 270 270 250 M125 290 Q200 312 275 290 M140 330 Q200 348 260 330" stroke="#d9b87a" stroke-width="6" fill="none" stroke-linecap="round" opacity="0.55"/>
<path d="M132 78 L112 8 L182 38 Z" fill="url(#fur)" stroke="#d9b87a" stroke-width="2" stroke-linejoin="round"/><path d="M268 78 L288 8 L218 38 Z" fill="url(#fur)" stroke="#d9b87a" stroke-width="2" stroke-linejoin="round"/>
<path d="M134 62 L124 28 L162 44 Z" fill="#d9a8b0" opacity="0.7"/><path d="M266 62 L276 28 L238 44 Z" fill="#d9a8b0" opacity="0.7"/>
<circle cx="200" cy="128" r="90" fill="url(#fur)"/>
<path d="M140 90 Q200 110 260 90 M135 125 Q200 145 265 125 M150 160 Q200 175 250 160" stroke="#d9b87a" stroke-width="6" fill="none" stroke-linecap="round" opacity="0.5"/>
<path d="M112 138 L92 148 L112 158 L96 168 L114 174 Z" fill="#f7e8c3"/><path d="M288 138 L308 148 L288 158 L304 168 L286 174 Z" fill="#f7e8c3"/>
<rect x="90" y="30" width="220" height="360" fill="#000" filter="url(#furNoise)" opacity="0.07"/>
</symbol>
</svg>
<div class="scene" id="scene">
<div class="bg" id="bg">
<div class="wall"></div>
<div class="window"><div class="view"></div></div>
<div class="light-shaft"></div>
<div class="dust" style="left:30%;top:32%"></div><div class="dust" style="left:42%;top:44%;animation-delay:1.2s"></div><div class="dust" style="left:52%;top:36%;animation-delay:2.1s"></div><div class="dust" style="left:36%;top:52%;animation-delay:.6s"></div><div class="dust" style="left:58%;top:50%;animation-delay:3s"></div>
<div class="frame f1"></div><div class="frame f2"></div>
<div class="sofa"><div class="sofa-back"></div><div class="sofa-arm a1"></div><div class="sofa-arm a2"></div><div class="sofa-cushion c1"></div><div class="sofa-cushion c2"></div><div class="sofa-base"></div></div>
<div class="lamp"><div class="lamp-glow"></div><div class="lamp-shade"></div><div class="lamp-pole"></div><div class="lamp-base"></div></div>
<div class="plant"><div class="leaf l1"></div><div class="leaf l2"></div><div class="leaf l3"></div><div class="leaf l4"></div><div class="leaf l5"></div><div class="pot"></div></div>
<div class="table"><div class="book"></div><div class="book book2"></div><div class="mug"></div><div class="table-top"></div><div class="table-leg tl1"></div><div class="table-leg tl2"></div></div>
<div class="baseboard"></div><div class="floor"></div><div class="rug"></div>
</div>
<div class="vortex" id="vortex"></div>
<div id="bigOiia">OIIA!</div>
<div id="shadow"></div>
<div class="cloneCat" id="clone1"><div class="cf"><svg><use href="#catFront"/></svg></div><div class="cb"><svg><use href="#catBack"/></svg></div></div>
<div class="cloneCat" id="clone2"><div class="cf"><svg><use href="#catFront"/></svg></div><div class="cb"><svg><use href="#catBack"/></svg></div></div>
<div id="catWrap" title="click the cat">
<div class="ghost" id="g1"><div class="gf"><svg><use href="#catFront"/></svg></div><div class="gb"><svg><use href="#catBack"/></svg></div></div>
<div class="ghost" id="g2"><div class="gf"><svg><use href="#catFront"/></svg></div><div class="gb"><svg><use href="#catBack"/></svg></div></div>
<div class="ghost" id="g3"><div class="gf"><svg><use href="#catFront"/></svg></div><div class="gb"><svg><use href="#catBack"/></svg></div></div>
<div class="blurDisc" id="blurDisc"></div>
<div class="catFace" id="frontEl"><svg><use href="#catFront"/></svg></div>
<div class="catBack" id="backEl"><svg><use href="#catBack"/></svg></div>
</div>
<div class="orbiter"><div class="yarn y1"></div></div>
<div class="orbiter"><div class="yarn y2"></div></div>
<div class="orbiter"><div class="yarn y3"></div></div>
<div class="orbiter"><div class="star"></div></div>
<div class="orbiter"><div class="star pink"></div></div>
<div class="orbiter"><div class="fish"></div></div>
<div class="speedlines" id="speedlines"></div>
<div id="oiiaPop">oiia</div>
<div class="vignette"></div>
<div id="stageLabel"></div>
<button id="resetBtn" type="button">RESET</button>
<button id="muteBtn" type="button">SOUND: ON</button>
<div id="hint">click the cat — oiia oiia</div>
</div>
<script>
const catWrap=document.getElementById('catWrap'),frontEl=document.getElementById('frontEl'),backEl=document.getElementById('backEl'),blurDisc=document.getElementById('blurDisc'),shadow=document.getElementById('shadow'),bg=document.getElementById('bg'),speedlines=document.getElementById('speedlines'),oiiaPop=document.getElementById('oiiaPop'),stageLabel=document.getElementById('stageLabel'),hint=document.getElementById('hint'),muteBtn=document.getElementById('muteBtn'),resetBtn=document.getElementById('resetBtn');
const ghosts=[document.getElementById('g1'),document.getElementById('g2'),document.getElementById('g3')];
const clones=[document.getElementById('clone1'),document.getElementById('clone2')];
const orbiters=document.querySelectorAll('.orbiter');
let stage=0,spinAngle=0,spinSpeed=0,liftY=0,bgAngle=0,orbitAngle=0,time=0,last=performance.now(),pulse=0,isMuted=false;
const targetSpeeds=[0,7,12,18,28],targetLifts=[0,60,110,160,200];
function setStage(n){stage=n;document.body.className='stage'+n;stageLabel.textContent=n?('OIIA LEVEL '+n+(n===4?' — MAXIMUM OIIA':'')):'';stageLabel.style.opacity=n?1:0;hint.style.display=n?'none':'block';clones.forEach(c=>c.style.display=n>=3?'block':'none');orbiters.forEach(o=>o.style.display=n>=3?'block':'none');}
catWrap.addEventListener('click',()=>{if(!audioCtx)initAudio();if(audioCtx&&audioCtx.state==='suspended')audioCtx.resume();if(stage<4)setStage(stage+1);else pulse=1;});
resetBtn.addEventListener('click',()=>setStage(0));
muteBtn.addEventListener('click',()=>{isMuted=!isMuted;muteBtn.textContent=isMuted?'SOUND: OFF':'SOUND: ON';if(masterGain)masterGain.gain.setTargetAtTime(isMuted?0:0.85,audioCtx.currentTime,0.03);});
function popOiia(v,sf){if(stage===0)return;oiiaPop.textContent='oiia';oiiaPop.style.left=(50+(Math.random()*10-5))+'%';oiiaPop.style.top=(34+(Math.random()*8-4))+'%';oiiaPop.style.fontSize=(28+sf*38)+'px';oiiaPop.animate([{transform:'translate(-50%,-50%) scale(0.55) rotate(-5deg)',opacity:0},{transform:'translate(-50%,-50%) scale(1.25) rotate(3deg)',opacity:1,offset:0.35},{transform:'translate(-50%,-50%) scale(1) rotate(0deg)',opacity:0}],{duration:200+(1-sf)*220,easing:'ease-out'});}
function loop(now){const dt=Math.min(0.05,(now-last)/1000);last=now;time+=dt;
const targetSpeed=targetSpeeds[stage];spinSpeed+=(targetSpeed-spinSpeed)*(1-Math.exp(-dt*(stage===0?2:1.1)));if(stage===0&&spinSpeed<0.01)spinSpeed=0;spinAngle+=spinSpeed*dt;
pulse*=Math.exp(-dt*2);const targetLift=targetLifts[stage]+(pulse?Math.sin(time*20)*10*pulse:0);liftY+=(targetLift-liftY)*(1-Math.exp(-dt*3));
const bob=stage>0?Math.sin(time*3)*6:0;const speedFactor=Math.min(1,spinSpeed/22);
const cosA=Math.cos(spinAngle),absCos=Math.abs(cosA),sx=Math.max(0.07,absCos),isFront=cosA>=0;
frontEl.style.display=isFront?'block':'none';backEl.style.display=isFront?'none':'block';const active=isFront?frontEl:backEl;
const sy=1+speedFactor*0.05+(stage>=2?0.03:0);
catWrap.style.transform='translate(-50%,-50%) translateY('+(-liftY+bob)+'px)';
active.style.transform='scaleX('+sx+') scaleY('+sy+')';
active.style.filter=speedFactor>0.25?('blur('+(speedFactor*2.2).toFixed(1)+'px) saturate('+(1+speedFactor*0.4)+')'):'none';
blurDisc.style.opacity=(speedFactor*0.75).toFixed(2);
ghosts.forEach((g,i)=>{const ga=spinAngle-(i+1)*0.38,gc=Math.cos(ga),gsx=Math.max(0.07,Math.abs(gc)),gFront=gc>=0;const gf=g.querySelector('.gf'),gb=g.querySelector('.gb');gf.style.display=gFront?'block':'none';gb.style.display=gFront?'none':'block';const gel=gFront?gf:gb;gel.style.transform='scaleX('+gsx+')';g.style.opacity=stage>0?(0.28/(i+1)*Math.min(1,speedFactor*1.5)).toFixed(2):0;g.style.filter='blur('+(2+i*2)+'px)';});
const shScale=Math.max(0.35,1-liftY/260);shadow.style.transform='translate(-50%,-50%) scale('+shScale+')';shadow.style.opacity=(0.38*shScale).toFixed(2);
let bgTransform='';if(stage===1){bgTransform='translate('+(Math.sin(time*28)*1.2)+'px,'+(Math.cos(time*24)*1)+'px)';}else if(stage===2){bgTransform='rotate('+(Math.sin(time*1.8)*3)+'deg) translateY('+(Math.sin(time*2.2)*4)+'px)';}else if(stage===3){bgTransform='rotate('+(Math.sin(time*0.6)*6)+'deg) scale(1.04) translateY('+(Math.sin(time*1.5)*8)+'px)';}else if(stage===4){bgAngle+=dt*28;bgTransform='rotate('+(bgAngle%360)+'deg) scale('+(1.08+Math.sin(time*4)*0.04)+')';}
bg.style.transform=bgTransform;
speedlines.style.opacity=(speedFactor*0.9).toFixed(2);speedlines.style.transform='translate(-50%,-50%) rotate('+(spinAngle*20)+'deg) scale('+(1+speedFactor*0.2)+')';
if(stage>=3){orbitAngle+=dt*(0.6+stage*0.35);const radius=170+stage*25+Math.sin(time*2)*10;
orbiters.forEach((o,i)=>{const a=orbitAngle+i*(Math.PI*2/orbiters.length);const x=Math.cos(a)*radius*1.3,y=Math.sin(a)*radius*0.75-liftY*0.5;o.style.transform='translate('+x+'px,'+y+'px) rotate('+(a*2)+'rad)';});
clones.forEach((c,i)=>{const a=orbitAngle*1.2+i*Math.PI;const x=Math.cos(a)*(radius*0.85),y=Math.sin(a)*(radius*0.5)-liftY*0.6;c.style.transform='translate('+x+'px,'+y+'px) scale(0.55)';const cCos=Math.cos(spinAngle+i*2),cf=c.querySelector('.cf'),cb=c.querySelector('.cb');cf.style.display=cCos>=0?'block':'none';cb.style.display=cCos>=0?'none':'block';cf.style.transform='scaleX('+Math.max(0.1,Math.abs(cCos))+')';cb.style.transform='scaleX('+Math.max(0.1,Math.abs(cCos))+')';});}
requestAnimationFrame(loop);}
requestAnimationFrame(loop);
/* Audio: formant voice locked to spinSpeed */
let audioCtx,masterGain,voiceGain,osc,oscSub,formantFilters=[],noiseGain,syllableIndex=0,nextSyllableTime=0;
const pattern=['o','i','i','a','o','i','i','a','o','i','i','a','i','o','o','o','i','a','i'];
const formantMap={o:[380,760,2600],i:[270,2300,3100],a:[730,1100,2500]};
function makeDistortion(k){const n=256,c=new Float32Array(n);for(let i=0;i<n;i++){const x=i*2/n-1;c[i]=(3+k)*x*20*Math.PI/180/(Math.PI+k*Math.abs(x));}return c;}
function initAudio(){audioCtx=new (window.AudioContext||window.webkitAudioContext)();
const comp=audioCtx.createDynamicsCompressor();comp.threshold.value=-18;comp.ratio.value=6;
masterGain=audioCtx.createGain();masterGain.gain.value=isMuted?0:0.85;masterGain.connect(comp);comp.connect(audioCtx.destination);
voiceGain=audioCtx.createGain();voiceGain.gain.value=0;const shaper=audioCtx.createWaveShaper();shaper.curve=makeDistortion(12);voiceGain.connect(shaper);shaper.connect(masterGain);
osc=audioCtx.createOscillator();osc.type='sawtooth';osc.frequency.value=180;
oscSub=audioCtx.createOscillator();oscSub.type='sine';oscSub.frequency.value=90;
const oscGain=audioCtx.createGain();oscGain.gain.value=0.6;const subGain=audioCtx.createGain();subGain.gain.value=0.35;
formantFilters=[400,900,2600].map((f,i)=>{const bp=audioCtx.createBiquadFilter();bp.type='bandpass';bp.frequency.value=f;bp.Q.value=i===2?6:9;const g=audioCtx.createGain();g.gain.value=i===0?1:i===1?0.7:0.35;bp.connect(g);g.connect(voiceGain);return bp;});
osc.connect(oscGain);oscGain.connect(formantFilters[0]);oscGain.connect(formantFilters[1]);oscGain.connect(formantFilters[2]);oscSub.connect(subGain);subGain.connect(formantFilters[0]);
const lfo=audioCtx.createOscillator();lfo.frequency.value=5.5;const lfoGain=audioCtx.createGain();lfoGain.gain.value=4;lfo.connect(lfoGain);lfoGain.connect(osc.frequency);
const len=audioCtx.sampleRate,buf=audioCtx.createBuffer(1,len,audioCtx.sampleRate),d=buf.getChannelData(0);for(let i=0;i<len;i++)d[i]=Math.random()*2-1;
const noise=audioCtx.createBufferSource();noise.buffer=buf;noise.loop=true;const nbp=audioCtx.createBiquadFilter();nbp.type='bandpass';nbp.frequency.value=2800;nbp.Q.value=0.8;noiseGain=audioCtx.createGain();noiseGain.gain.value=0.02;noise.connect(nbp);nbp.connect(noiseGain);noiseGain.connect(voiceGain);
osc.start();oscSub.start();lfo.start();noise.start();nextSyllableTime=audioCtx.currentTime+0.06;setInterval(scheduler,25);}
function scheduleSyllable(t,vowel,dur,pitch,sf){const f=formantMap[vowel]||formantMap.o;formantFilters.forEach((bp,i)=>bp.frequency.setTargetAtTime(f[i]*(1+sf*0.08),t,0.012));
osc.frequency.cancelScheduledValues(t);osc.frequency.setValueAtTime(pitch,t);osc.frequency.exponentialRampToValueAtTime(pitch*1.28,t+dur*0.75);
oscSub.frequency.setValueAtTime(pitch/2,t);oscSub.frequency.exponentialRampToValueAtTime(pitch/2*1.2,t+dur*0.7);
voiceGain.gain.cancelScheduledValues(t);voiceGain.gain.setValueAtTime(0.0001,t);voiceGain.gain.exponentialRampToValueAtTime(0.85,t+0.018);voiceGain.gain.setValueAtTime(0.85,t+dur*0.55);voiceGain.gain.exponentialRampToValueAtTime(0.0001,t+dur);
noiseGain.gain.setValueAtTime(0.015+sf*0.03,t);noiseGain.gain.exponentialRampToValueAtTime(0.0001,t+dur);}
function scheduler(){if(!audioCtx)return;while(nextSyllableTime<audioCtx.currentTime+0.14){
if(stage===0||spinSpeed<0.8){nextSyllableTime=audioCtx.currentTime+0.05;voiceGain.gain.setTargetAtTime(0,audioCtx.currentTime,0.03);break;}
const sf=Math.min(1,spinSpeed/22);const idx=syllableIndex%pattern.length;const vowel=pattern[idx];const isLong=idx>=13&&idx<=15;
const dur=(0.30-sf*0.17)*(isLong?2.4:1);const pitch=145+sf*340+(stage-1)*35+Math.random()*18;
scheduleSyllable(nextSyllableTime,vowel,dur,pitch,sf);
const delay=Math.max(0,(nextSyllableTime-audioCtx.currentTime)*1000);const v=vowel,s=sf;setTimeout(()=>popOiia(v,s),delay);
syllableIndex++;nextSyllableTime+=dur*0.92;}}
</script>
</body>
</html>