Models 44

GPT-6 Luna Pro

by OpenAIreasoning defaultClean
Open in new window

Running live in a sandboxed frame. Click inside it before using the keyboard.

How it went

Total time
2 min 25 s
Cost
$0.014
Output tokens
23,378
Spent thinking
9,896 tokens
Speed
945.3 tok/s
Console errors
0
First token after
1 min 60 s
First visible text after
2 min 24 s
Prompt tokens
24,357
Code size
19.4 KB
Finish reason
stop
Served by
OpenAI
Model id
openai/gpt-6-luna-pro
Reasoning sent
model default
Max output
128,000 tokens
Generated
Nov 13, 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 · 470 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Blue Room — Living Aquarium</title>
<style>
  * { box-sizing: border-box; }
  html, body { margin: 0; width: 100%; height: 100%; overflow: hidden; }
  body {
    color: #e8f8fa;
    background: #041a27;
    font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  }
  #aquarium { position: fixed; inset: 0; }
  #aquarium canvas { display: block; width: 100%; height: 100%; }
  .vignette {
    position: fixed; inset: 0; pointer-events: none;
    background:
      linear-gradient(180deg, rgba(2,18,29,.62) 0%, transparent 24%, transparent 68%, rgba(2,16,25,.52) 100%),
      radial-gradient(ellipse at 50% 46%, transparent 42%, rgba(0,13,23,.34) 100%);
  }
  .topbar {
    position: fixed; inset: 0 0 auto; display: flex; align-items: flex-start;
    justify-content: space-between; padding: 27px 36px; pointer-events: none;
  }
  .brand { display: flex; align-items: center; gap: 12px; }
  .brand-mark {
    width: 34px; height: 34px; border: 1px solid rgba(134,227,235,.52);
    border-radius: 50%; display: grid; place-items: center;
    color: #a7f0e5; font-size: 15px; box-shadow: 0 0 22px rgba(73,219,219,.14);
  }
  .brand-name { font-size: 11px; font-weight: 650; letter-spacing: .2em; text-transform: uppercase; }
  .brand-sub { margin-top: 4px; color: rgba(208,237,239,.55); font-size: 9px; letter-spacing: .13em; text-transform: uppercase; }
  .live {
    display: flex; align-items: center; gap: 9px; padding: 9px 13px;
    border: 1px solid rgba(176,229,231,.18); border-radius: 99px;
    background: rgba(7,31,43,.44); color: rgba(222,244,244,.78);
    font-size: 9px; letter-spacing: .15em; text-transform: uppercase;
    backdrop-filter: blur(10px);
  }
  .live-dot { width: 6px; height: 6px; border-radius: 50%; background: #76e4c2; box-shadow: 0 0 9px #76e4c2; }
  .bottom {
    position: fixed; bottom: 30px; left: 50%; transform: translateX(-50%);
    display: flex; align-items: center; gap: 17px; pointer-events: none;
    padding: 13px 19px; border: 1px solid rgba(183,225,231,.17);
    border-radius: 7px; background: rgba(5,24,35,.58); backdrop-filter: blur(14px);
    box-shadow: 0 10px 40px rgba(0,0,0,.16); white-space: nowrap;
  }
  .mouse-icon {
    width: 22px; height: 29px; border: 1px solid rgba(173,236,232,.68); border-radius: 12px;
    position: relative; flex: none;
  }
  .mouse-icon::before { content:""; position:absolute; top:5px; left:9px; width:2px; height:5px; border-radius:2px; background:#9ae6d8; }
  .hint-title { font-size: 10px; letter-spacing: .13em; text-transform: uppercase; color: #e4f6f4; }
  .hint-sub { margin-top: 4px; font-size: 10px; color: rgba(200,227,230,.56); letter-spacing: .025em; }
  .count { border-left: 1px solid rgba(191,229,231,.17); padding-left: 17px; color: rgba(219,244,242,.75); font-size: 10px; letter-spacing: .08em; }
  .count strong { display: block; color: #a6eadc; font-size: 16px; font-weight: 500; margin-bottom: 2px; }
  #cursor {
    position: fixed; width: 34px; height: 34px; margin: -17px 0 0 -17px; border-radius: 50%;
    border: 1px solid rgba(170,245,230,.8); box-shadow: 0 0 18px rgba(96,242,216,.35), inset 0 0 10px rgba(96,242,216,.12);
    pointer-events: none; opacity: 0; transform: scale(.75); transition: opacity .18s, transform .18s; z-index: 5;
  }
  #cursor::after { content:""; position:absolute; inset: 11px; background: rgba(150,255,228,.8); border-radius:50%; box-shadow:0 0 9px #82ffdd; }
  #cursor.visible { opacity: .82; transform: scale(1); }
  @media (max-width: 600px) {
    .topbar { padding: 20px 18px; }
    .brand-mark { width: 30px; height: 30px; }
    .brand-name { font-size: 10px; }
    .brand-sub { font-size: 8px; }
    .live { font-size: 8px; padding: 8px 10px; }
    .bottom { bottom: 18px; gap: 12px; padding: 11px 13px; width: calc(100% - 28px); justify-content: center; }
    .hint-title { font-size: 9px; }
    .hint-sub { font-size: 9px; }
    .count { padding-left: 12px; font-size: 9px; }
    .count strong { font-size: 14px; }
    .mouse-icon { width: 18px; height: 25px; }
    .mouse-icon::before { left: 7px; }
  }
  @media (max-width: 390px) {
    .bottom { gap: 9px; }
    .hint-sub { max-width: 195px; white-space: normal; line-height: 1.3; }
  }
</style>
<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>
</head>
<body>
<div id="aquarium"></div>
<div class="vignette"></div>
<header class="topbar">
  <div class="brand">
    <div class="brand-mark">≈</div>
    <div><div class="brand-name">Blue Room</div><div class="brand-sub">A little world beneath the surface</div></div>
  </div>
  <div class="live"><span class="live-dot"></span> Live habitat</div>
</header>
<div class="bottom">
  <div class="mouse-icon"></div>
  <div><div class="hint-title">Move gently through the water</div><div class="hint-sub">Come close and watch the fish dart away</div></div>
  <div class="count"><strong>09</strong>inhabitants</div>
</div>
<div id="cursor"></div>
<script type="module">
import * as THREE from "three";

const container = document.getElementById("aquarium");
const cursor = document.getElementById("cursor");
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x061d2b);
scene.fog = new THREE.FogExp2(0x061d2b, 0.018);

const camera = new THREE.PerspectiveCamera(42, innerWidth / innerHeight, 0.1, 100);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
renderer.setPixelRatio(Math.min(devicePixelRatio, 1.7));
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.12;
container.appendChild(renderer.domElement);

scene.add(new THREE.HemisphereLight(0xa4e7eb, 0x102d36, 1.35));
const keyLight = new THREE.DirectionalLight(0x9ceeff, 2.1);
keyLight.position.set(-5, 8, 7);
scene.add(keyLight);
const turquoiseLight = new THREE.PointLight(0x3bd2c4, 45, 23, 2);
turquoiseLight.position.set(4, 1.5, 2);
scene.add(turquoiseLight);
const blueLight = new THREE.PointLight(0x4786ff, 35, 25, 2);
blueLight.position.set(-6, 0, -2);
scene.add(blueLight);

const world = new THREE.Group();
scene.add(world);
const tankW = 17, tankH = 9, tankD = 7.4;
const glassMaterial = new THREE.MeshPhysicalMaterial({
  color: 0x5bd9e1, roughness: 0.19, metalness: 0.05, transparent: true,
  opacity: 0.045, side: THREE.DoubleSide, depthWrite: false
});
const glass = new THREE.Mesh(new THREE.BoxGeometry(tankW, tankH, tankD), glassMaterial);
glass.position.y = 0;
world.add(glass);

const frame = new THREE.LineSegments(
  new THREE.EdgesGeometry(new THREE.BoxGeometry(tankW, tankH, tankD)),
  new THREE.LineBasicMaterial({ color: 0x69d8e4, transparent: true, opacity: 0.27 })
);
world.add(frame);

const floorMat = new THREE.MeshStandardMaterial({ color: 0x9a8c69, roughness: 0.95, metalness: 0.0 });
const floor = new THREE.Mesh(new THREE.PlaneGeometry(tankW, tankD), floorMat);
floor.rotation.x = -Math.PI / 2;
floor.position.y = -4.48;
world.add(floor);

const rimMat = new THREE.MeshStandardMaterial({ color: 0x194758, roughness: .35, metalness: .35 });
const baseRim = new THREE.Mesh(new THREE.BoxGeometry(tankW + .25, .18, tankD + .25), rimMat);
baseRim.position.y = -4.54;
world.add(baseRim);
const topRim = new THREE.Mesh(new THREE.BoxGeometry(tankW + .12, .09, tankD + .12), rimMat);
topRim.position.y = 4.52;
world.add(topRim);

// Soft, suspended water glints.
const particleCount = 165;
const particlePositions = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
  particlePositions[i * 3] = (Math.random() - .5) * 16;
  particlePositions[i * 3 + 1] = (Math.random() - .5) * 8.2;
  particlePositions[i * 3 + 2] = (Math.random() - .5) * 6.7;
}
const particleGeometry = new THREE.BufferGeometry();
particleGeometry.setAttribute("position", new THREE.BufferAttribute(particlePositions, 3));
const particleMaterial = new THREE.PointsMaterial({
  color: 0xa4eff1, size: .035, transparent: true, opacity: .55, sizeAttenuation: true
});
const particles = new THREE.Points(particleGeometry, particleMaterial);
world.add(particles);

const sandPebbleMat = [
  new THREE.MeshStandardMaterial({ color: 0xb4a77e, roughness: .9 }),
  new THREE.MeshStandardMaterial({ color: 0x62766d, roughness: .9 }),
  new THREE.MeshStandardMaterial({ color: 0xd0bd91, roughness: .92 })
];
for (let i = 0; i < 125; i++) {
  const radius = .035 + Math.random() * .1;
  const pebble = new THREE.Mesh(new THREE.SphereGeometry(radius, 7, 5), sandPebbleMat[i % 3]);
  pebble.scale.set(1 + Math.random() * 1.6, .4 + Math.random() * .5, .8 + Math.random());
  pebble.position.set((Math.random() - .5) * 16.4, -4.36 + radius * .18, (Math.random() - .5) * 6.8);
  world.add(pebble);
}

function makePlant(x, z, height, color, phase) {
  const plant = new THREE.Group();
  plant.position.set(x, -4.39, z);
  world.add(plant);
  const curve = new THREE.CatmullRomCurve3([
    new THREE.Vector3(0, 0, 0),
    new THREE.Vector3(.16, height * .3, 0),
    new THREE.Vector3(-.12, height * .68, .04),
    new THREE.Vector3(.16, height, 0)
  ]);
  const stem = new THREE.Mesh(new THREE.TubeGeometry(curve, 18, .045, 7, false),
    new THREE.MeshStandardMaterial({ color, roughness: .65, emissive: color, emissiveIntensity: .12 }));
  plant.add(stem);
  for (let i = 0; i < 7; i++) {
    const t = .16 + i * .105;
    const p = curve.getPoint(t);
    const dir = (i % 2 ? 1 : -1);
    const leaf = new THREE.Mesh(
      new THREE.SphereGeometry(1, 12, 8),
      new THREE.MeshStandardMaterial({ color: i % 2 ? color : 0x45a991, roughness: .52, side: THREE.DoubleSide })
    );
    leaf.scale.set(.36 + .08 * Math.sin(i), .12, .075);
    leaf.position.copy(p);
    leaf.position.x += dir * .25;
    leaf.rotation.z = dir * -.38;
    leaf.rotation.y = dir * .24;
    plant.add(leaf);
  }
  plant.userData.phase = phase;
  return plant;
}
const plants = [
  makePlant(-7.15, -2.3, 2.15, 0x277b70, .4),
  makePlant(-6.3, -2.5, 3.0, 0x36a18a, 1.3),
  makePlant(-5.55, -2.7, 1.8, 0x408b70, 2.0),
  makePlant(6.7, -2.7, 2.65, 0x3aa18d, 1.1),
  makePlant(7.25, -2.5, 1.9, 0x277b70, 2.5),
  makePlant(4.8, -3.05, 1.15, 0x5c9b66, .6)
];

// Smooth stones at the foot of the plants.
for (let i = 0; i < 10; i++) {
  const rock = new THREE.Mesh(new THREE.DodecahedronGeometry(.24 + Math.random() * .32, 1),
    new THREE.MeshStandardMaterial({ color: i % 2 ? 0x475a53 : 0x766f5e, roughness: .92 }));
  rock.scale.set(1.2, .58, .9);
  rock.position.set(-7 + Math.random() * 14, -4.13, -2.9 + Math.random() * 1.2);
  world.add(rock);
}

// A few tiny bubbles rising near the greenery.
const bubbleMat = new THREE.MeshPhysicalMaterial({
  color: 0x9cecf3, roughness: .05, metalness: .05, transparent: true, opacity: .45,
  transmission: .45, thickness: .2
});
const bubbles = [];
for (let i = 0; i < 18; i++) {
  const r = .035 + Math.random() * .055;
  const bubble = new THREE.Mesh(new THREE.SphereGeometry(r, 10, 8), bubbleMat);
  bubble.position.set((Math.random() < .5 ? -1 : 1) * (4.8 + Math.random() * 2.2),
    -3.8 + Math.random() * 7, (Math.random() - .5) * 5.5);
  bubble.userData.speed = .22 + Math.random() * .42;
  bubble.userData.offset = Math.random() * 8;
  bubbles.push(bubble);
  world.add(bubble);
}

function shapeGeometry(points) {
  const shape = new THREE.Shape();
  shape.moveTo(points[0][0], points[0][1]);
  for (let i = 1; i < points.length; i++) shape.lineTo(points[i][0], points[i][1]);
  shape.closePath();
  return new THREE.ShapeGeometry(shape);
}
function makeFish(color, bellyColor, scale, x, y, z, phase, stripeColor) {
  const group = new THREE.Group();
  group.position.set(x, y, z);
  group.scale.setScalar(scale);
  world.add(group);

  const bodyMat = new THREE.MeshStandardMaterial({ color, roughness: .35, metalness: .06 });
  const finMat = new THREE.MeshStandardMaterial({ color, roughness: .42, side: THREE.DoubleSide, metalness: .04 });
  const bellyMat = new THREE.MeshStandardMaterial({ color: bellyColor, roughness: .48, side: THREE.DoubleSide });
  const body = new THREE.Mesh(new THREE.SphereGeometry(1, 28, 20), bodyMat);
  body.scale.set(.82, .42, .34);
  group.add(body);

  const belly = new THREE.Mesh(new THREE.SphereGeometry(1, 20, 12), bellyMat);
  belly.scale.set(.57, .19, .285);
  belly.position.set(-.01, -.22, .025);
  group.add(belly);

  const tail = new THREE.Group();
  tail.position.x = -.65;
  const tailShape = shapeGeometry([[0, 0], [-.42, .43], [-.84, .52], [-.68, 0], [-.84, -.52], [-.42, -.43]]);
  const tailMesh = new THREE.Mesh(tailShape, finMat);
  tailMesh.position.z = -.005;
  tail.add(tailMesh);
  group.add(tail);

  const dorsal = new THREE.Mesh(shapeGeometry([[-.43, .27], [-.25, .58], [.16, .38], [.4, .27]]), finMat);
  dorsal.position.z = 0;
  group.add(dorsal);
  const lowerFin = new THREE.Mesh(shapeGeometry([[-.42, -.27], [-.1, -.56], [.26, -.29]]), finMat);
  group.add(lowerFin);

  const pectoral = new THREE.Mesh(new THREE.SphereGeometry(1, 12, 8), bellyMat);
  pectoral.scale.set(.29, .09, .045);
  pectoral.position.set(-.12, -.04, .31);
  pectoral.rotation.z = -.35;
  group.add(pectoral);

  const eyeWhite = new THREE.MeshStandardMaterial({ color: 0xf5f3df, roughness: .22 });
  const pupilMat = new THREE.MeshStandardMaterial({ color: 0x081a22, roughness: .18 });
  for (const side of [-1, 1]) {
    const eye = new THREE.Mesh(new THREE.SphereGeometry(.095, 14, 12), eyeWhite);
    eye.position.set(.56, .115, side * .26);
    eye.scale.set(1, 1.05, .58);
    group.add(eye);
    const pupil = new THREE.Mesh(new THREE.SphereGeometry(.047, 12, 10), pupilMat);
    pupil.position.set(.592, .12, side * .302);
    group.add(pupil);
    const glint = new THREE.Mesh(new THREE.SphereGeometry(.014, 8, 6),
      new THREE.MeshBasicMaterial({ color: 0xffffff }));
    glint.position.set(.606, .143, side * .324);
    group.add(glint);
  }

  // Fine gill arcs and a handful of luminous vertical markings.
  const gillCurve = new THREE.QuadraticBezierCurve3(
    new THREE.Vector3(.31, .2, .29), new THREE.Vector3(.16, 0, .355), new THREE.Vector3(.3, -.2, .29)
  );
  const gill = new THREE.Mesh(new THREE.TubeGeometry(gillCurve, 12, .012, 5, false),
    new THREE.MeshStandardMaterial({ color: bellyColor, roughness: .55 }));
  group.add(gill);

  if (stripeColor) {
    const stripeMat = new THREE.MeshStandardMaterial({ color: stripeColor, roughness: .4, metalness: .03 });
    for (let i = 0; i < 3; i++) {
      const stripe = new THREE.Mesh(new THREE.TorusGeometry(.32 - i * .035, .018, 5, 24, Math.PI),
        stripeMat);
      stripe.position.set(-.18 + i * .2, .01, .301);
      stripe.rotation.z = Math.PI / 2;
      stripe.scale.set(.58, .8, 1);
      group.add(stripe);
    }
  }

  return {
    group, tail, phase,
    pos: new THREE.Vector3(x, y, z),
    vel: new THREE.Vector3((Math.random() - .5) * 1.35, (Math.random() - .5) * .45, (Math.random() - .5) * .35),
    scale
  };
}
const fishes = [
  makeFish(0xf2a54b, 0xffd88b, 1.04, -4.9, 1.25, 1.15, .3, 0xf6cb68),
  makeFish(0xe98745, 0xffd7a4, .83, -1.45, 2.25, -1.15, 2.1, null),
  makeFish(0x55c9bd, 0xa6eee0, .92, 2.6, 1.35, .9, 1.2, 0xd4fff0),
  makeFish(0x6ba8e8, 0xc6e4f0, .77, 5.15, 2.35, -1.35, 3.7, 0xd8edff),
  makeFish(0xe8c45a, 0xffedaa, .92, -3.25, -.45, -2.2, 4.4, 0xfff3c5),
  makeFish(0xd97083, 0xf5b5b4, .74, .2, -.95, 2.25, 5.6, 0xffd3cb),
  makeFish(0x74ce8a, 0xc5edac, .85, 4.15, -.85, .2, 6.9, 0xe3ffc4),
  makeFish(0xeda764, 1.0, -.72, -5.45, -2.15, -.3, 8.3, null),
  makeFish(0x9b8ce6, 0xd4caff, .79, 2.0, -2.35, -1.55, 9.1, 0xf0dfff)
];

// Fix the deliberately varied body colors while keeping scale/positions clear.
fishes[7] = makeFish(0xeda764, 0xffd3a4, .72, -5.45, -2.15, -.3, 8.3, null);

const raycaster = new THREE.Raycaster();
const pointerNDC = new THREE.Vector2();
const waterPlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
const mouseWorld = new THREE.Vector3();
let mouseActive = false;
let scaleX = 1;

function pointerMove(event) {
  const rect = renderer.domElement.getBoundingClientRect();
  pointerNDC.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
  pointerNDC.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
  raycaster.setFromCamera(pointerNDC, camera);
  mouseActive = raycaster.ray.intersectPlane(waterPlane, mouseWorld) !== null;
  if (mouseActive) {
    mouseWorld.x /= scaleX;
    cursor.style.left = event.clientX + "px";
    cursor.style.top = event.clientY + "px";
    cursor.classList.add("visible");
  }
}
renderer.domElement.addEventListener("pointermove", pointerMove);
renderer.domElement.addEventListener("pointerleave", () => {
  mouseActive = false;
  cursor.classList.remove("visible");
});

function resize() {
  const w = innerWidth, h = innerHeight, aspect = w / h;
  renderer.setSize(w, h, false);
  camera.aspect = aspect;
  camera.fov = 42;
  scaleX = aspect < .75 ? .42 : 1;
  world.scale.x = scaleX;
  const halfView = Math.tan(THREE.MathUtils.degToRad(camera.fov / 2));
  const targetWidth = tankW * scaleX + .6;
  const targetHeight = tankH + .8;
  const distance = Math.max(targetHeight / (2 * halfView), targetWidth / (2 * halfView * aspect));
  camera.position.set(0, 3.6, distance + 1.4);
  camera.lookAt(0, 0, 0);
  camera.updateProjectionMatrix();
}
addEventListener("resize", resize);
resize();

const clock = new THREE.Clock();
const temp = new THREE.Vector3();
function animate() {
  requestAnimationFrame(animate);
  const dt = Math.min(clock.getDelta(), .035);
  const t = clock.elapsedTime;

  particles.rotation.y = t * .012;
  for (const plant of plants) {
    plant.rotation.z = Math.sin(t * .75 + plant.userData.phase) * .035;
  }
  for (const bubble of bubbles) {
    bubble.position.y += bubble.userData.speed * dt;
    bubble.position.x += Math.sin(t * .8 + bubble.userData.offset) * dt * .045;
    if (bubble.position.y > 4.1) {
      bubble.position.y = -4.15;
      bubble.position.x = (Math.random() < .5 ? -1 : 1) * (4.8 + Math.random() * 2.2);
    }
  }

  for (const fish of fishes) {
    const p = fish.pos, v = fish.vel;
    const phase = fish.phase;
    const desired = new THREE.Vector3(
      Math.cos(t * .48 + phase) * .85 + Math.sin(t * .19 + phase) * .28,
      Math.sin(t * .72 + phase * 1.7) * .34,
      Math.sin(t * .35 + phase) * .25
    );
    const acceleration = desired.sub(v).multiplyScalar(.58);

    if (mouseActive) {
      temp.set(p.x - mouseWorld.x, p.y - mouseWorld.y, p.z - mouseWorld.z);
      const d = temp.length();
      const radius = 3.15;
      if (d < radius && d > .001) {
        const strength = (1 - d / radius);
        acceleration.add(temp.normalize().multiplyScalar(11 * strength * strength));
      }
    }

    if (p.x > 7.15) acceleration.x -= (p.x - 7.15) * 2.2;
    if (p.x < -7.15) acceleration.x += (-7.15 - p.x) * 2.2;
    if (p.y > 3.55) acceleration.y -= (p.y - 3.55) * 2.4;
    if (p.y < -3.48) acceleration.y += (-3.48 - p.y) * 2.4;
    if (p.z > 2.9) acceleration.z -= (p.z - 2.9) * 1.8;
    if (p.z < -2.9) acceleration.z += (-2.9 - p.z) * 1.8;

    v.addScaledVector(acceleration, dt);
    const speed = v.length();
    const maxSpeed = 2.35;
    if (speed > maxSpeed) v.multiplyScalar(maxSpeed / speed);
    p.addScaledVector(v, dt);
    fish.group.position.copy(p);

    const horizontalSpeed = Math.max(.0001, Math.hypot(v.x, v.y));
    fish.group.rotation.y = -Math.atan2(v.z, horizontalSpeed) * .38;
    fish.group.rotation.z = Math.atan2(v.y, v.x);
    fish.tail.rotation.y = Math.sin(t * 8.3 + phase) * .3;
    fish.tail.rotation.z = Math.sin(t * 8.3 + phase) * .1;
  }

  renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>
Reasoning trace (9,896 tokens)
**Creating a 3D Aquarium**

I need to create an HTML immersive 3D aquarium, focusing on aesthetics and functionality. First, I’ll import the necessary map and module. The fish should swim away from the mouse, and I want to ensure a visible screenshot. Using Three.js, I’ll add overlays, create a glass aquarium shape with gradients and lighting, and model fish geometries procedurally. The aquarium scene will have transparent tank edges, gravel, and plants. I'll also set up the camera perspective and control the fish's movement in relation to the mouse pointer.**Designing Fish Animation and Environment**

I’m working on the fish's design, using an ellipsoid body and a custom triangle fin geometry for the tail located at half the body size. The fish group will include bodies, tails, and fins, with animated tail rotations. For the water, I might explore using procedural canvas textures. The environment needs to feature a gradient background via CSS, a UI overlay, and luminous blue tank edges. I’ll create gravel and plants and ensure the renderer is responsive while rendering in WebGL, with a dark blue foggy background. The camera will be positioned appropriately within the tank dimensions.**Customizing Fish Meshes**

I’m setting up the frustum, which will be horizontal at a distance of about 18, with a field of view (FOV) of 42, giving a height of approximately 13.6 that fits the tank's height of 9. I’ll take a screenshot at 1280x800 and include overlay labels. The fish meshes need some customization, starting with building spheres for bodies. For the tail, I can use BufferGeometry to create a triangular fin shape or ShapeGeometry, maybe extruded and double-sided, ensuring the fish face the +X direction.**Creating texture and UI elements**

I’m thinking about creating a canvas texture using thin, curved cyan lines with low opacity, but maybe it’s not necessary. I want to set lighting, including ambient, directional, and point lights, and use ACES settings. I also need to handle mouse pointer mapping to the world through a camera ray-plane at z=0. For the cursor, I’m considering an “attract” ring overlay or a custom crosshair. Plus, I’ll add a UI message prompting users to move their cursor through the water.**Calculating tank dimensions**

I'm considering the dimensions for a tank with a width of 375 and a height aspect ratio of 0.47. This setup implies that the height will be 17, which leads to a vertical visual of 36. Hmm, that sounds distant, but it’s interesting how the ratios interact with each other. I wonder how these specs will affect the overall design. I'm just trying to get the right balance in the proportions!**Assessing camera framing**

I'm looking at the camera framing and noticing the physical dimensions: a width of 11.1 and a height of 9, which gives a ratio of 1.23. But then I see that the viewport dimensions are 375 in width and 800 in height. This means that the tank fits about 95% of the width and 77% of the height. This seems almost perfect for what I'm trying to achieve!**Designing a glass panel concept**

I'm contemplating a design that includes a very transparent front glass panel, although that might not be necessary. The side glass panels could have a flat design but with some opacity. I’m thinking about adding lighting and incorporating floating particles, around 120 points, using PointsMaterial. 

For the water effect, I might want to create fake curved horizontal lines with a canvas texture, alongside ambient spots generated from canvas gradient sprites. It seems like an interesting visual approach!**Formulating fish behavior code**

I'm working on a code for fish behavior and how they flee from danger. I should ensure the fish move quickly, adjusting speeds based on distance. It sounds like I need to implement some tail animation and consider adding different species names. I want to maintain a variety of body sizes and colors in my setup while using a proper shading technique. Also, maybe I could add subtle water effects to enhance the visual experience. Let’s get to coding!