Interactive 3D aquariumOne-line
GLM 5.3
by Z.aiReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 26.4 s
- Cost
- $0.0071
- Output tokens
- 5,049
- Spent thinking
- 63 tokens
- Speed
- 194 tok/s
- Console errors
- 0
- First token after
- 380 ms
- First visible text after
- 820 ms
- Prompt tokens
- 424
- Code size
- 14.2 KB
- Finish reason
- stop
- Served by
- Wafer
- Model id
z-ai/glm-5.3- Reasoning sent
{"effort":"low"}- 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 · 392 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Aquarium</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; overflow: hidden; background: #04121f; font-family: Georgia, serif; }
#app { position: fixed; inset: 0; }
canvas { display: block; }
.panel {
position: fixed; top: 18px; left: 18px; z-index: 10;
color: #cfeef5; background: rgba(4,26,44,.55);
border: 1px solid rgba(120,200,255,.25);
padding: 14px 18px 12px; border-radius: 10px;
backdrop-filter: blur(6px); max-width: 300px;
pointer-events: none;
}
.panel h1 { font-size: 19px; letter-spacing: .06em; font-weight: normal; }
.panel p { font-size: 13px; margin-top: 5px; opacity: .85; line-height: 1.45; }
.hint {
position: fixed; bottom: 16px; left: 50%; transform: translateX(-50%);
color: #bfe9f5; background: rgba(4,26,44,.6);
border: 1px solid rgba(120,200,255,.25);
padding: 8px 16px; border-radius: 999px; font-size: 13px;
z-index: 10; pointer-events: none;
}
@media (max-width: 560px) { .panel { max-width: 220px; } .panel h1 { font-size: 16px; } }
</style>
</head>
<body>
<div id="app"></div>
<div class="panel">
<h1>Reef Tank №7</h1>
<p>Move your cursor through the water — the fish scatter from your presence. Click to feed them and watch them gather.</p>
</div>
<div class="hint" id="hint">move your mouse · click to feed</div>
<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>
<script type="module">
import * as THREE from 'three';
const app = document.getElementById('app');
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a3550);
scene.fog = new THREE.FogExp2(0x0a3550, 0.035);
const camera = new THREE.PerspectiveCamera(55, innerWidth/innerHeight, 0.1, 100);
camera.position.set(0, 3.5, 14);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
app.appendChild(renderer.domElement);
// ---------- Lights ----------
scene.add(new THREE.HemisphereLight(0x9fd8ff, 0x0b2a3d, 0.9));
const sun = new THREE.DirectionalLight(0xfff2d0, 1.6);
sun.position.set(4, 10, 6);
scene.add(sun);
// ---------- Water surface (top plane, seen from below) ----------
const waterGeo = new THREE.PlaneGeometry(60, 60, 1, 1);
const water = new THREE.Mesh(waterGeo, new THREE.MeshPhongMaterial({
color: 0x66ccff, transparent: true, opacity: 0.35, side: THREE.DoubleSide, shininess: 90
}));
water.rotation.x = -Math.PI/2;
water.position.y = 9;
scene.add(water);
// ---------- Sand floor ----------
const sand = new THREE.Mesh(
new THREE.CircleGeometry(30, 48),
new THREE.MeshStandardMaterial({ color: 0xc9b088, roughness: 1 })
);
sand.rotation.x = -Math.PI/2;
sand.position.y = -3;
scene.add(sand);
// subtle undulation on the sand via vertex noise
{
const g = new THREE.PlaneGeometry(60, 60, 40, 40);
const p = g.attributes.position;
for (let i=0;i<p.count;i++){
const x = p.getX(i), y = p.getY(i);
p.setZ(i, Math.sin(x*0.6)*0.15 + Math.cos(y*0.8)*0.12);
}
g.computeVertexNormals();
const m = new THREE.Mesh(g, new THREE.MeshStandardMaterial({ color: 0xbfa87e, roughness: 1 }));
m.rotation.x = -Math.PI/2; m.position.y = -3.02;
scene.add(m);
}
// ---------- Rocks ----------
const rockMat = new THREE.MeshStandardMaterial({ color: 0x6b7f8a, roughness: 0.95, flatShading: true });
for (let i=0;i<14;i++){
const s = 0.4 + Math.random()*1.2;
const rock = new THREE.Mesh(new THREE.DodecahedronGeometry(s, 0), rockMat);
const a = Math.random()*Math.PI*2, r = 5 + Math.random()*16;
rock.position.set(Math.cos(a)*r, -3 + s*0.4, Math.sin(a)*r - 3);
rock.rotation.set(Math.random()*3, Math.random()*3, Math.random()*3);
scene.add(rock);
}
// ---------- Plants (swaying blades) ----------
const plants = [];
const bladeMat = new THREE.MeshStandardMaterial({ color: 0x2e8b57, roughness: 0.8, side: THREE.DoubleSide });
for (let i=0;i<26;i++){
const h = 1.5 + Math.random()*2.5;
const blade = new THREE.Mesh(new THREE.PlaneGeometry(0.18, h, 1, 4), bladeMat);
const a = Math.random()*Math.PI*2, r = 3 + Math.random()*18;
blade.position.set(Math.cos(a)*r, -3 + h/2, Math.sin(a)*r - 3);
blade.rotation.y = Math.random()*Math.PI;
blade.userData = { phase: Math.random()*6.28, h };
plants.push(blade);
scene.add(blade);
}
// ---------- Fish ----------
function makeFishTexture(hue){
const c = document.createElement('canvas'); c.width = 128; c.height = 64;
const ctx = c.getContext('2d');
const g = ctx.createLinearGradient(0,0,128,64);
g.addColorStop(0, `hsl(${hue},70%,55%)`);
g.addColorStop(0.55, `hsl(${hue},75%,45%)`);
g.addColorStop(1, `hsl(${(hue+30)%360},70%,35%)`);
ctx.fillStyle = g; ctx.fillRect(0,0,128,64);
ctx.fillStyle = `hsl(${hue},80%,25%)`;
for (let i=0;i<7;i++){ ctx.beginPath(); ctx.ellipse(15+i*16, 32+Math.sin(i)*14, 7, 4, 0.5, 0, 6.29); ctx.fill(); }
const t = new THREE.CanvasTexture(c);
t.colorSpace = THREE.SRGBColorSpace;
return t;
}
const fishes = [];
const FISH_COUNT = 26;
const fishGroup = new THREE.Group();
scene.add(fishGroup);
for (let i=0;i<FISH_COUNT;i++){
const hue = Math.floor(Math.random()*360);
const scale = 0.55 + Math.random()*0.9;
// body: elongated ellipsoid via scaled sphere
const body = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 12, 10),
new THREE.MeshStandardMaterial({ map: makeFishTexture(hue), roughness: 0.6 })
);
body.scale.set(1.6, 0.8, 0.5);
// tail (cone pointing back)
const tail = new THREE.Mesh(
new THREE.ConeGeometry(0.32, 0.6, 8),
new THREE.MeshStandardMaterial({ color: new THREE.Color().setHSL(hue/360, 0.7, 0.4), roughness: 0.7, flatShading: true })
);
tail.rotation.z = Math.PI/2;
tail.position.x = -0.85;
// fins
const finGeo = new THREE.ConeGeometry(0.16, 0.4, 4);
const finMat = new THREE.MeshStandardMaterial({ color: new THREE.Color().setHSL(hue/360, 0.6, 0.55), transparent: true, opacity: 0.85, flatShading: true });
const topFin = new THREE.Mesh(finGeo, finMat);
topFin.position.set(0.05, 0.35, 0); topFin.scale.set(0.8, 1, 0.35);
const sideFinL = new THREE.Mesh(finGeo, finMat.clone());
sideFinL.position.set(0.1, -0.05, 0.25); sideFinL.rotation.x = Math.PI/2.4; sideFinL.scale.set(0.7,1,0.3);
const sideFinR = new MeshMirror(sideFinL);
function MeshMirror(m){ const c = m.clone(); c.position.z = -0.25; c.rotation.x = -Math.PI/2.4; return c; }
const f = new THREE.Group();
f.add(body, tail, topFin, sideFinL, sideFinR);
f.scale.setScalar(scale);
const a = Math.random()*Math.PI*2, r = 3 + Math.random()*10;
f.userData = {
pos: new THREE.Vector3(Math.cos(a)*r, -1 + Math.random()*5, Math.sin(a)*r - 2),
vel: new THREE.Vector3((Math.random()-0.5)*2, 0, (Math.random()-0.5)*2),
phase: Math.random()*Math.PI*2,
hue,
wander: Math.random()*Math.PI*2,
scared: 0
};
f.position.copy(f.userData.pos);
fishes.push(f);
fishGroup.add(f);
}
// ---------- Bubbles ----------
const bubbles = [];
const bubbleMat = new THREE.MeshPhongMaterial({ color: 0xbfeaff, transparent: true, opacity: 0.4, shininess: 100 });
for (let i=0;i<40;i++){
const b = new THREE.Mesh(new THREE.SphereGeometry(0.05 + Math.random()*0.09, 8, 8), bubbleMat);
b.userData = {
x: (Math.random()-0.5)*20, z: (Math.random()-0.5)*20 - 2,
y: -3 + Math.random()*12, spd: 0.4 + Math.random()*0.9,
wob: Math.random()*6.28
};
bubbles.push(b); scene.add(b);
}
// ---------- Floating motes ----------
const moteGeo = new THREE.BufferGeometry();
const moteCount = 300;
const mp = new Float32Array(moteCount*3);
for (let i=0;i<moteCount;i++){
mp[i*3] = (Math.random()-0.5)*30;
mp[i*3+1] = -3 + Math.random()*12;
mp[i*3+2] = (Math.random()-0.5)*30 - 2;
}
moteGeo.setAttribute('position', new THREE.BufferAttribute(mp, 3));
const motes = new THREE.Points(moteGeo, new THREE.PointsMaterial({ color: 0xcde8ff, size: 0.06, transparent: true, opacity: 0.6 }));
scene.add(motes);
// ---------- Mouse "finger" in the water ----------
const finger = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 16, 16),
new THREE.MeshPhongMaterial({ color: 0xffe9b0, emissive: 0x553300, transparent: true, opacity: 0.35 })
);
scene.add(finger);
// ---------- Interaction ----------
const raycaster = new THREE.Raycaster();
const mouseNDC = new THREE.Vector2(10, 10); // off-screen initially
const waterPlane = new THREE.Plane(new THREE.Vector3(0,1,0), 0);
let mouseWorld = new THREE.Vector3(0, 0.5, 0);
let feedSpots = []; // food positions fish are attracted to
let hasMouse = false;
function updateMouse(e){
mouseNDC.x = (e.clientX / innerWidth) * 2 - 1;
mouseNDC.y = -(e.clientY / innerHeight) * 2 + 1;
hasMouse = true;
}
addEventListener('pointermove', updateMouse);
addEventListener('pointerdown', (e) => {
updateMouse(e);
// drop food at mouse position — fish rush toward it briefly
raycaster.setFromCamera(mouseNDC, camera);
const pt = new THREE.Vector3();
if (raycaster.ray.intersectPlane(waterPlane, pt)){
feedSpots.push({ pos: new THREE.Vector3(pt.x, 0, pt.z), t: 5 });
// visual food puff
const puff = new THREE.Group();
for (let i=0;i<8;i++){
const p = new THREE.Mesh(new THREE.SphereGeometry(0.05, 6, 6),
new THREE.MeshStandardMaterial({ color: 0xd9a05a }));
p.position.set((Math.random()-0.5)*0.8, Math.random()*0.6, (Math.random()-0.5)*0.8);
puff.add(p);
}
puff.position.copy(pt).setY(0.2);
puff.userData = { t: 5, food: true };
feedSpots[feedSpots.length-1].puff = puff;
scene.add(puff);
}
});
// keyboard not required, but let's rotate view with drag-like keys? Keep simple.
// ---------- Animation ----------
const clock = new THREE.Clock();
let time = 0;
// fish tank bounds
const BOUND = 13;
const Y_MIN = -2.4, Y_MAX = 7.5;
function animate(){
requestAnimationFrame(animate);
const dt = Math.min(clock.getDelta(), 0.05);
time += dt;
// raycast mouse to horizontal plane at y=0.. approximate depth using camera-facing plane at z=0
if (hasMouse){
raycaster.setFromCamera(mouseNDC, camera);
const hit = raycaster.ray.intersectPlane(waterPlane, new THREE.Vector3());
if (hit) mouseWorld.copy(hit);
}
finger.position.lerp(new THREE.Vector3(mouseWorld.x, Math.max(-2, Math.min(7, mouseWorld.y + 1)), mouseWorld.z), 0.2);
// decay feed spots
feedSpots = feedSpots.filter(s => { s.t -= dt; if (s.puff){ s.puff.userData.t = s.t; s.puff.children.forEach((c,i)=>{ c.position.y += dt*0.15; c.scale.setScalar(Math.max(0.01, Math.min(1, s.t))); }); } if (s.t <= 0){ if (s.puff) scene.remove(s.puff); return false; } return true; });
const nearestFeed = feedSpots.length ? feedSpots[feedSpots.length-1].pos : null;
// fish update
for (const f of fishes){
const u = f.userData;
const p = u.pos, v = u.vel;
// steering: wander
u.wander += dt * 0.7;
const wanderDir = new THREE.Vector3(Math.sin(u.wander), Math.sin(u.wander*0.6)*0.3, Math.cos(u.wander*1.3));
v.addScaledVector(wanderDir, dt * 1.6);
// flee from mouseWorld (in x/z, mild in y)
const toMouse = new THREE.Vector3().subVectors(p, new THREE.Vector3(mouseWorld.x, p.y, mouseWorld.z));
const dist = toMouse.length();
const scareR = 4.5;
if (hasMouse && dist < scareR){
const strength = (1 - dist/scareR) * 14;
toMouse.normalize();
v.addScaledVector(toMouse, strength * dt);
u.scared = Math.min(1, u.scared + dt*4);
} else {
u.scared = Math.max(0, u.scared - dt*1.5);
}
// attracted to food
if (nearestFeed){
const toFood = new THREE.Vector3().subVectors(nearestFeed, p);
if (toFood.length() < 14){
toFood.normalize();
v.addScaledVector(toFood, dt * 5);
}
}
// schooling-lite: mild pull toward center
v.addScaledVector(new THREE.Vector3().subVectors(new THREE.Vector3(0, 2, -2), p), dt * 0.25);
// bounds
if (Math.abs(p.x) > BOUND) v.x += (Math.sign(-p.x) * (Math.abs(p.x)-BOUND) * 3) * dt * 3;
if (Math.abs(p.z) > BOUND) v.z += (Math.sign(-p.z) * (Math.abs(p.z)-BOUND) * 3) * dt * 3;
if (p.y < Y_MIN) v.y += (Y_MIN - p.y) * 2 * dt * 3;
if (p.y > Y_MAX) v.y += (Y_MAX - p.y) * 2 * dt * 3;
// drag & speed clamp (scared fish swim faster)
const maxSpd = 2.2 + u.scared*4;
v.multiplyScalar(1 - dt * (1.2 + u.scared*0.5));
if (v.length() > maxSpd) v.setLength(maxSpd);
if (v.length() < 0.6) v.setLength(0.6);
p.addScaledVector(v, dt);
// orient fish along velocity
f.position.copy(p);
const look = new THREE.Vector3().addVectors(p, v);
if (look.distanceToSquared(p) > 0.0001) f.lookAt(look);
// bank/roll into turn + tail wiggle
u.phase += dt * (4 + v.length()*2 + u.scared*6);
f.rotation.z = 0; // keep simple: apply wiggle to scale
const wig = Math.sin(u.phase) * 0.12;
f.children[1].rotation.z = Math.PI/2 + wig; // tail swings in local plane
f.children[1].rotation.y = wig * 1.2;
f.children[3].rotation.z = Math.sin(time*3 + u.phase)*0.2;
f.children[4].rotation.z = -Math.sin(time*3 + u.phase)*0.2;
}
// camera gentle drift
const cx = Math.sin(time*0.1)*1.5;
camera.position.x += (cx - camera.position.x)*0.02;
camera.lookAt(0, 1.5, -2);
// water shimmer
water.position.y = 9 + Math.sin(time*0.7)*0.06;
// plants sway
for (const b of plants){
b.rotation.z = Math.sin(time*1.1 + b.userData.phase) * 0.14;
b.rotation.x = Math.cos(time*0.8 + b.userData.phase) * 0.1;
}
// bubbles rise
for (const b of bubbles){
const u = b.userData;
u.y += u.spd * dt;
u.wob += dt * 3;
b.position.set(u.x + Math.sin(u.wob)*0.3, u.y, u.z + Math.cos(u.wob*0.7)*0.3);
if (u.y > 8.8){ u.y = -3; u.x = (Math.random()-0.5)*20; u.z = (Math.random()-0.5)*20 - 2; }
}
// motes drift
const marr = moteGeo.attributes.position.array;
for (let i=0;i<moteCount;i++){
marr[i*3+1] += dt*0.08;
if (marr[i*3+1] > 9) marr[i*3+1] = -3;
}
moteGeo.attributes.position.needsUpdate = true;
renderer.render(scene, camera);
}
animate();
addEventListener('resize', () => {
camera.aspect = innerWidth/innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
</script>
</body>
</html>Reasoning trace (63 tokens)
Build a 3D aquarium with Three.js, fish fleeing from mouse. Use import map. Fish made from simple geometry (cones/ellipsoids). Raycast mouse to water plane, fish flee from that point. Add caustics-ish lighting, bubbles, plants, sand. Self-contained.