Interactive 3D aquariumOne-line
Muse Ember
by MetaReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 10.5 s
- Cost
- $0.0028
- Output tokens
- 5,554
- Spent thinking
- 89 tokens
- Speed
- 588.1 tok/s
- Console errors
- 0
- First token after
- 1.1 s
- First visible text after
- 1.1 s
- Prompt tokens
- 441
- Code size
- 16.6 KB
- Finish reason
- stop
- Served by
- Meta
- Model id
meta/muse-ember- Reasoning sent
{"effort":"minimal"}- Max output
- 943,718 tokens
- Generated
- Nov 12, 2026
What it was asked
The prompt
“a 3D aquarium with Three.js where the fish swim away when I move the mouse near them (Load Three.js only through this import map: <script type="importmap">{"imports":{"three":"https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js","three/addons/":"https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/"}}</script>)”
System prompt, sent with every prompt
You are generating a self-contained web demo. It will be rendered automatically in a sandboxed iframe and compared side by side with the output of other AI models given the exact same task. Output rules: - Reply with exactly ONE fenced code block containing the complete file: ```html (or ```svg when the task asks for an SVG). No text before or after the code block. - Everything must be inline: CSS in <style>, JavaScript in <script>. No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task. - Do not use localStorage, sessionStorage, cookies, IndexedDB, alert(), confirm(), prompt() or window.open(): they are blocked in the sandbox and will throw. - The page must look right in a 1280×800 viewport and adapt gracefully down to 375px wide. No horizontal scrollbar. - Something meaningful must be visible on first paint: a screenshot is taken automatically about 3 seconds after load, without any interaction. - Everything that makes the demo interesting must be visible within the first seconds: nothing hidden behind a high score, a later level or a long play session. - Games must start in a self-playing demo mode (a simple AI plays) behind a "Click or press a key to start" message, so the game can be judged in action without playing. - The iframe only receives keyboard events after the user clicks it: if the demo uses the keyboard, display a clear "Click or press a key to start" message. - The code must run with zero errors in the console of the latest Chrome.
Code run-1.html · 317 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>3D Aquarium — Fish Flee From Your Cursor</title>
<script type="importmap">{"imports":{"three":"https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js","three/addons/":"https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/"}}</script>
<style>
*{box-sizing:border-box}
html,body{margin:0;height:100%;overflow:hidden;background:#062a3f;font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;color:#eaf7ff}
#app{position:fixed;inset:0}
canvas{display:block;width:100%;height:100%}
.ui{position:fixed;left:18px;top:18px;z-index:10;pointer-events:none;max-width:460px}
.card{pointer-events:auto;background:linear-gradient(160deg,rgba(6,45,72,.82),rgba(4,26,44,.68));border:1px solid rgba(140,220,255,.28);backdrop-filter:blur(12px);border-radius:18px;padding:14px 16px 12px;box-shadow:0 18px 50px rgba(0,0,0,.35)}
h1{margin:0;font-size:22px;letter-spacing:.2px;line-height:1.1}
h1 span{color:#6ee7ff}
p{margin:7px 0 0;font-size:13.5px;line-height:1.45;color:#cdeefb}
.badges{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.badge{font-size:11px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;background:rgba(110,231,255,.13);border:1px solid rgba(110,231,255,.3);padding:6px 9px;border-radius:999px;color:#dff8ff}
.controls{position:fixed;left:18px;bottom:18px;z-index:10;display:flex;gap:10px;flex-wrap:wrap;pointer-events:auto}
button{border:1px solid rgba(140,220,255,.35);background:rgba(8,55,84,.85);color:white;padding:10px 14px;border-radius:999px;font-weight:700;cursor:pointer;box-shadow:0 8px 24px rgba(0,0,0,.25)}
button:hover{background:#0b6b9b}
button.primary{background:linear-gradient(135deg,#06b6d4,#2563eb)}
.hint{position:fixed;right:18px;top:18px;z-index:10;background:rgba(3,25,42,.62);border:1px solid rgba(140,220,255,.25);backdrop-filter:blur(10px);padding:10px 12px;border-radius:14px;font-size:12.5px;line-height:1.4;max-width:260px;pointer-events:none}
.hint b{color:#6ee7ff}
.counter{position:fixed;right:18px;bottom:18px;z-index:10;background:rgba(3,25,42,.68);border:1px solid rgba(140,220,255,.25);backdrop-filter:blur(10px);padding:10px 13px;border-radius:14px;font-weight:800;font-size:13px;pointer-events:none}
.counter i{font-style:normal;color:#6ee7ff}
#fleeFlash{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:11;pointer-events:none;font-size:clamp(28px,5vw,56px);font-weight:900;letter-spacing:.5px;color:white;text-shadow:0 10px 40px rgba(0,190,255,.8);opacity:0;transition:opacity .18s,transform .18s}
#fleeFlash.show{opacity:1;transform:translate(-50%,-58%) scale(1.03)}
#startOverlay{position:fixed;inset:0;z-index:9;display:grid;place-items:center;background:radial-gradient(circle at 50% 40%,rgba(14,116,144,.22),rgba(2,18,32,.55));pointer-events:none}
#startOverlay .inner{text-align:center;pointer-events:auto;background:rgba(3,30,52,.72);border:1px solid rgba(140,220,255,.32);backdrop-filter:blur(12px);padding:16px 20px;border-radius:18px;box-shadow:0 20px 60px rgba(0,0,0,.35)}
#startOverlay h2{margin:0 0 6px;font-size:20px}
#startOverlay p{margin:0;color:#cdeefb;font-size:13px}
@media(max-width:640px){.ui{left:12px;top:12px;right:12px}.hint{display:none}.controls{left:12px;bottom:12px}.counter{right:12px;bottom:12px}h1{font-size:19px}}
</style>
</head>
<body>
<div id="app"></div>
<div class="ui">
<div class="card">
<h1>🫧 3D Aquarium <span>Live Tank</span></h1>
<p>Move your mouse (or finger) close to a fish — it will dart away! Drag to orbit, scroll to zoom. Fish also wander, nibble plants and blow bubbles on their own.</p>
<div class="badges"><span class="badge">Three.js WebGL</span><span class="badge">Flee AI</span><span class="badge">No downloads</span></div>
</div>
</div>
<div class="hint"><b>How to play</b><br>• Hover near fish to scare them<br>• Drag background to orbit<br>• Scroll / pinch to zoom<br>• Press <b>F</b> to feed, <b>Space</b> to scatter</div>
<div class="controls">
<button id="feedBtn" class="primary">🍤 Feed fish</button>
<button id="scatterBtn">💨 Scatter burst</button>
<button id="camBtn">🎥 Auto-orbit: ON</button>
</div>
<div class="counter">Fish scared: <i id="scared">0</i> • Bubbles: <i id="bubbles">0</i></div>
<div id="fleeFlash">💨 WHOOSH!</div>
<div id="startOverlay"><div class="inner"><h2>Click or press a key to start</h2><p>Then move your cursor near the fish — they flee!</p></div></div>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const app=document.getElementById('app');
const scene=new THREE.Scene();
scene.background=new THREE.Color(0x07395b);
scene.fog=new THREE.FogExp2(0x07395b,0.028);
const camera=new THREE.PerspectiveCamera(58,innerWidth/innerHeight,0.1,100);
camera.position.set(0,2.2,10.5);
const renderer=new THREE.WebGLRenderer({antialias:true});
renderer.setSize(innerWidth,innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio,2));
renderer.shadowMap.enabled=true;
renderer.shadowMap.type=THREE.PCFSoftShadowMap;
app.appendChild(renderer.domElement);
const controls=new OrbitControls(camera,renderer.domElement);
controls.enableDamping=true; controls.dampingFactor=.06;
controls.target.set(0,0.2,0); controls.minDistance=5; controls.maxDistance=16;
controls.maxPolarAngle=Math.PI*0.62; controls.autoRotate=true; controls.autoRotateSpeed=.45;
scene.add(new THREE.AmbientLight(0xbfefff,.65));
const sun=new THREE.DirectionalLight(0xffffff,1.5); sun.position.set(5,10,6); sun.castShadow=true; sun.shadow.mapSize.set(2048,2048); scene.add(sun);
const blue=new THREE.PointLight(0x22d3ee,18,18); blue.position.set(-4,4,4); scene.add(blue);
const warm=new THREE.PointLight(0xffcc88,10,15); warm.position.set(5,2,-3); scene.add(warm);
// Tank bounds
const W=12,H=6.4,D=6;
const tank=new THREE.Group(); scene.add(tank);
// Back wall / water tint
const backGeo=new THREE.PlaneGeometry(W,H);
const backMat=new THREE.MeshStandardMaterial({color:0x0b5f86,roughness:.9,metalness:0});
const back=new THREE.Mesh(backGeo,backMat); back.position.set(0,H/2-1.2,-D/2); tank.add(back);
// Side walls subtle
[-W/2,W/2].forEach(x=>{const m=new THREE.Mesh(new THREE.PlaneGeometry(D,H),new THREE.MeshStandardMaterial({color:0x0a4a6b,roughness:1,transparent:true,opacity:.9}));m.position.set(x,H/2-1.2,0);m.rotation.y=x<0?Math.PI/2:-Math.PI/2;tank.add(m);});
// Sandy floor
const sand=new THREE.Mesh(new THREE.BoxGeometry(W,.7,D),new THREE.MeshStandardMaterial({color:0xe8d8a8,roughness:1}));
sand.position.y=-1.55; sand.receiveShadow=true; tank.add(sand);
// Glass edges
const glassMat=new THREE.MeshPhysicalMaterial({color:0xbfefff,transparent:true,opacity:.14,roughness:.08,metalness:0,clearcoat:1});
const glass=new THREE.Mesh(new THREE.BoxGeometry(W,H,D),glassMat); glass.position.y=H/2-1.2; tank.add(glass);
const frameMat=new THREE.MeshStandardMaterial({color:0x0f2f42,roughness:.4,metalness:.6});
[[W+.15,.18,D+.15,0,H/2-1.2+D/2-.09,0],[W+.15,.18,D+.15,0,H/2-1.2-D/2+.09,0]].forEach(([a,b,c,x,y,z])=>{const f=new THREE.Mesh(new THREE.BoxGeometry(a,b,c),frameMat);f.position.set(x,y,z);tank.add(f);});
// Rocks
const rockMat=new THREE.MeshStandardMaterial({color:0x6b7f8d,roughness:.95});
for(let i=0;i<14;i++){
const s=.5+Math.random()*1.1;
const r=new THREE.Mesh(new THREE.DodecahedronGeometry(s,1),rockMat);
r.position.set((Math.random()*2-1)*(W/2-1),-1.15+Math.random()*.25,(Math.random()*2-1)*(D/2-1));
r.rotation.set(Math.random()*3,Math.random()*3,Math.random()*3); r.castShadow=r.receiveShadow=true; tank.add(r);
}
// Plants
const plants=[];
function makePlant(x,z,scale,hue){
const g=new THREE.Group();
const mat=new THREE.MeshStandardMaterial({color:new THREE.Color().setHSL(hue,.65,.32),roughness:.7,side:THREE.DoubleSide});
for(let i=0;i<9;i++){
const leaf=new THREE.Mesh(new THREE.ConeGeometry(.09*(1+Math.random()*.6),1.4+Math.random()*1.6,6),mat);
leaf.position.set((Math.random()-.5)*.7,.7,(Math.random()-.5)*.7);
leaf.rotation.z=(Math.random()-.5)*.55; leaf.rotation.x=(Math.random()-.5)*.4;
leaf.castShadow=true; g.add(leaf);
}
g.position.set(x,-1.2,z); g.scale.setScalar(scale); tank.add(g); plants.push({g,phase:Math.random()*10});
return g;
}
for(let i=0;i<16;i++) makePlant((Math.random()*2-1)*(W/2-1.2),(Math.random()*2-1)*(D/2-1.2),.8+Math.random()*1.1, .32+Math.random()*.12);
makePlant(-4.8,-1.5,1.8,.38); makePlant(4.9,1.2,1.9,.35); makePlant(0,-2.2,1.5,.33);
// Bubbles from aerator
const bubbleGeo=new THREE.SphereGeometry(.09,10,10);
const bubbleMat=new THREE.MeshStandardMaterial({color:0xcff6ff,transparent:true,opacity:.55,roughness:.1,metalness:.1});
const bubbles=[];
for(let i=0;i<90;i++){
const b=new THREE.Mesh(bubbleGeo,bubbleMat);
b.position.set(-5.2+Math.random()*.4,-1+Math.random()*4.5,(Math.random()-.5)*1.2);
b.userData={speed:.6+Math.random()*1.2,wob:Math.random()*10};
const s=.5+Math.random()*1.4; b.scale.setScalar(s); tank.add(b); bubbles.push(b);
}
// Light rays
for(let i=0;i<6;i++){
const ray=new THREE.Mesh(new THREE.PlaneGeometry(.7,H),new THREE.MeshBasicMaterial({color:0x9beeff,transparent:true,opacity:.06,side:THREE.DoubleSide,depthWrite:false}));
ray.position.set(-4+i*1.6,1.5,-1); ray.rotation.z=.25; tank.add(ray);
}
// Fish factory
const fishes=[];
const palette=[0xff7a18,0xff3b7f,0xffd93b,0x28d17c,0x38bdf8,0xa78bfa,0xfb923c,0xf472b6,0x34d399,0x60a5fa,0xe879f9,0xfacc15,0x2dd4bf,0xfdba74];
function makeFish(color,i){
const g=new THREE.Group();
const mat=new THREE.MeshStandardMaterial({color,roughness:.35,metalness:.15});
const body=new THREE.Mesh(new THREE.SphereGeometry(.34,22,16),mat);
body.scale.set(1.45,.72,.5); body.castShadow=true; g.add(body);
const tail=new THREE.Mesh(new THREE.ConeGeometry(.28,.55,4),mat);
tail.position.x=-.62; tail.rotation.z=Math.PI/2; tail.scale.z=.35; g.add(tail);
const fin=new THREE.Mesh(new THREE.ConeGeometry(.16,.32,4),mat);
fin.position.set(.05,.28,0); fin.rotation.x=.2; fin.scale.z=.3; g.add(fin);
const eyeMat=new THREE.MeshBasicMaterial({color:0x111111});
[-.18,.18].forEach(z=>{const e=new THREE.Mesh(new THREE.SphereGeometry(.055,10,10),eyeMat);e.position.set(.38,.08,z);e.scale.z=.5;g.add(e);});
// stripes for some
if(i%3===0){const stripe=new THREE.Mesh(new THREE.TorusGeometry(.26,.035,8,20),new THREE.MeshStandardMaterial({color:0xffffff}));stripe.position.x=.05;stripe.rotation.y=Math.PI/2;stripe.scale.set(1,.7,.5);g.add(stripe);}
g.position.set((Math.random()*2-1)*4,Math.random()*3-0.5,(Math.random()*2-1)*1.8);
scene.add(g);
const fish={g,body,tail,vel:new THREE.Vector3(),target:new THREE.Vector3(),speed:.9+Math.random()*.9,flee:0,phase:Math.random()*10,scared:false};
fish.target.copy(g.position); fishes.push(fish); return fish;
}
for(let i=0;i<18;i++) makeFish(palette[i%palette.length],i);
// Food pellets
const foods=[];
function feed(){
for(let i=0;i<26;i++){
const f=new THREE.Mesh(new THREE.SphereGeometry(.07,8,8),new THREE.MeshStandardMaterial({color:0xffb347}));
f.position.set((Math.random()*2-1)*3,3.2+Math.random(),(Math.random()*2-1)*1.5);
f.userData={vy:-.35-Math.random()*.3,life:8+Math.random()*5}; scene.add(f); foods.push(f);
}
fishes.forEach(f=>{f.target.set((Math.random()*2-1)*4,Math.random()*2+.5,(Math.random()*2-1)*1.5);});
}
// Mouse / pointer -> 3D point on tank plane
const raycaster=new THREE.Raycaster();
const pointerNDC=new THREE.Vector2(10,10);
const pointer3D=new THREE.Vector3(999,999,999);
const pointerActive=false;
let hasPointer=false;
let scaredCount=0;
const flash=document.getElementById('fleeFlash');
let flashT=null;
function updatePointer(e){
const x=e.clientX??e.touches?.[0]?.clientX, y=e.clientY??e.touches?.[0]?.clientY;
if(x==null) return;
pointerNDC.x=(x/innerWidth)*2-1; pointerNDC.y=-(y/innerHeight)*2+1;
hasPointer=true; hideStart();
}
addEventListener('pointermove',updatePointer,{passive:true});
addEventListener('touchmove',updatePointer,{passive:true});
addEventListener('pointerdown',updatePointer,{passive:true});
function hideStart(){document.getElementById('startOverlay').style.display='none';}
addEventListener('keydown',hideStart);
renderer.domElement.addEventListener('pointerdown',hideStart);
document.getElementById('feedBtn').onclick=feed;
document.getElementById('scatterBtn').onclick=scatter;
function scatter(){ fishes.forEach(f=>{f.flee=1.2; f.vel.add(new THREE.Vector3((Math.random()-.5)*4,(Math.random())*2,(Math.random()-.5)*4)); flashIt();}); }
document.getElementById('camBtn').onclick=(e)=>{controls.autoRotate=!controls.autoRotate; e.target.textContent='🎥 Auto-orbit: '+(controls.autoRotate?'ON':'OFF');};
addEventListener('keydown',e=>{ if(e.code==='KeyF') feed(); if(e.code==='Space'){e.preventDefault(); scatter();}});
function flashIt(){ flash.classList.add('show'); clearTimeout(flashT); flashT=setTimeout(()=>flash.classList.remove('show'),450); scaredCount++; document.getElementById('scared').textContent=scaredCount; }
// initial demo feed so something happens immediately
setTimeout(feed,600);
const clock=new THREE.Clock();
const tmpV=new THREE.Vector3(), tmp2=new THREE.Vector3();
function animate(){
requestAnimationFrame(animate);
const dt=Math.min(clock.getDelta(),.05);
const t=clock.elapsedTime;
// update pointer 3D: intersect a virtual plane at z=0 and also closest approach
if(hasPointer){
raycaster.setFromCamera(pointerNDC,camera);
// intersect plane z=0 for a stable pointer point inside tank
const plane=new THREE.Plane(new THREE.Vector3(0,0,1),0);
raycaster.ray.intersectPlane(plane,pointer3D);
pointer3D.x=THREE.MathUtils.clamp(pointer3D.x,-W/2+ .5,W/2-.5);
pointer3D.y=THREE.MathUtils.clamp(pointer3D.y,-1,3.8);
pointer3D.z=THREE.MathUtils.clamp(pointer3D.z,-D/2+.5,D/2-.5);
}
// Plants sway
plants.forEach(p=>{p.g.rotation.z=Math.sin(t*1.1+p.phase)*.06; p.g.rotation.x=Math.cos(t*.8+p.phase)*.04;});
// Bubbles
bubbles.forEach(b=>{
b.position.y+=b.userData.speed*dt;
b.position.x+=Math.sin(t*2+b.userData.wob)*.008;
if(b.position.y>3.9){b.position.y=-1; b.position.x=-5.2+Math.random()*.4;}
});
document.getElementById('bubbles').textContent=bubbles.length;
// Foods sink
for(let i=foods.length-1;i>=0;i--){const f=foods[i]; f.position.y+=f.userData.vy*dt; f.userData.life-=dt; if(f.position.y<-.9||f.userData.life<=0){scene.remove(f); foods.splice(i,1);}}
// Fish AI
fishes.forEach((fish,idx)=>{
const pos=fish.g.position;
// distance to pointer
let fleeing=false;
if(hasPointer){
const d=pos.distanceTo(pointer3D);
if(d<2.4){
fleeing=true;
if(!fish.scared){ flashIt(); }
fish.scared=true; fish.flee=1;
tmpV.copy(pos).sub(pointer3D).normalize();
// add upward/outward bias
tmpV.y+=.55; tmpV.z+=(Math.random()-.5)*.4;
tmpV.normalize().multiplyScalar((2.4-d)*3.2+1.2);
fish.vel.lerp(tmpV,.25);
} else if(d>3.2){ fish.scared=false; }
}
if(!fleeing){
// wander / seek food
if(pos.distanceTo(fish.target)<.6 || Math.random()<.005){
fish.target.set((Math.random()*2-1)*(W/2-1.2),Math.random()*3-.4,(Math.random()*2-1)*(D/2-1.2));
if(foods.length){ const f=foods[Math.floor(Math.random()*foods.length)]; fish.target.copy(f.position); }
}
tmpV.copy(fish.target).sub(pos).normalize().multiplyScalar(fish.speed);
// gentle sine wander
tmpV.x+=Math.sin(t*1.2+fish.phase)*.25; tmpV.y+=Math.cos(t*1.5+fish.phase)*.15;
fish.vel.lerp(tmpV, fleeing? .1 : .06);
} else {
fish.vel.multiplyScalar(1.02); // burst
}
// clamp speed
if(fish.vel.length()>5.5) fish.vel.setLength(5.5);
if(fish.vel.length()<.35 && !fleeing) fish.vel.setLength(.35);
pos.addScaledVector(fish.vel,dt);
// keep inside tank with soft bounce
const mx=W/2-.8, myTop=3.6, myBot=-.7, mz=D/2-.8;
if(pos.x>mx){pos.x=mx; fish.vel.x*=-.8; fish.target.x=-mx;}
if(pos.x<-mx){pos.x=-mx; fish.vel.x*=-.8; fish.target.x=mx;}
if(pos.y>myTop){pos.y=myTop; fish.vel.y*=-.8;}
if(pos.y<myBot){pos.y=myBot; fish.vel.y*=-.6;}
if(pos.z>mz){pos.z=mz; fish.vel.z*=-.8;}
if(pos.z<-mz){pos.z=-mz; fish.vel.z*=-.8;}
// orient fish along velocity
if(fish.vel.lengthSq()>.001){
tmp2.copy(pos).add(fish.vel);
fish.g.lookAt(tmp2);
fish.g.rotateY(Math.PI); // because body built +x forward? adjust
// tail wag
fish.tail.rotation.y=Math.sin(t*14+fish.phase)*.55;
fish.body.position.y=Math.sin(t*8+fish.phase)*.03;
}
});
controls.update();
renderer.render(scene,camera);
}
animate();
addEventListener('resize',()=>{
camera.aspect=innerWidth/innerHeight; camera.updateProjectionMatrix();
renderer.setSize(innerWidth,innerHeight);
});
</script>
</body>
</html>