Models 44

GPT-6 Luna

by OpenAIReasoningClean
Open in new window

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

How it went

Total time
1 min 21 s
Cost
$0.0072
Output tokens
14,385
Spent thinking
6,171 tokens
Speed
185.5 tok/s
Console errors
0
First token after
3.2 s
First visible text after
48.8 s
Prompt tokens
423
Code size
24.5 KB
Finish reason
stop
Served by
OpenAI
Model id
openai/gpt-6-luna
Reasoning sent
{"effort":"high"}
Max output
64,000 tokens
Generated
Nov 10, 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 · 696 lines

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Blue Hour Aquarium</title>
  <style>
    * { box-sizing: border-box; }
    html, body {
      width: 100%;
      height: 100%;
      margin: 0;
      overflow: hidden;
      background: #031520;
      color: #eaf8f6;
      font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    }
    #aquarium {
      position: fixed;
      inset: 0;
      overflow: hidden;
      background:
        radial-gradient(ellipse at 50% 43%, #0b3942 0%, #06242f 45%, #03131e 100%);
    }
    canvas { display: block; width: 100%; height: 100%; }
    .hud {
      position: absolute;
      inset: 0;
      pointer-events: none;
      padding: clamp(22px, 4vw, 54px) clamp(22px, 5vw, 72px);
    }
    .topbar {
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
    .brand {
      display: flex;
      align-items: center;
      gap: 12px;
      color: rgba(232, 250, 247, .9);
      font-size: 11px;
      letter-spacing: .19em;
      line-height: 1.5;
      text-transform: uppercase;
    }
    .brand-mark {
      width: 34px;
      height: 34px;
      border: 1px solid rgba(137, 226, 220, .42);
      border-radius: 50%;
      display: grid;
      place-items: center;
      color: #8de2d8;
      font-size: 15px;
      box-shadow: 0 0 24px rgba(78, 205, 194, .12), inset 0 0 12px rgba(78, 205, 194, .08);
    }
    .brand small {
      display: block;
      color: rgba(190, 224, 220, .5);
      font-size: 9px;
      letter-spacing: .23em;
    }
    .live {
      display: flex;
      align-items: center;
      gap: 9px;
      padding: 10px 13px;
      border: 1px solid rgba(169, 225, 219, .16);
      border-radius: 30px;
      background: rgba(7, 31, 40, .28);
      color: rgba(226, 246, 241, .72);
      font-size: 9px;
      letter-spacing: .17em;
      text-transform: uppercase;
      backdrop-filter: blur(10px);
    }
    .live-dot {
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background: #79ddbd;
      box-shadow: 0 0 11px #79ddbd;
    }
    .intro {
      position: absolute;
      left: clamp(24px, 8vw, 112px);
      top: 22%;
      max-width: 390px;
      text-shadow: 0 2px 24px rgba(0, 13, 19, .3);
    }
    .eyebrow {
      color: #91d6cb;
      font-size: 10px;
      letter-spacing: .25em;
      text-transform: uppercase;
      margin-bottom: 17px;
    }
    h1 {
      margin: 0;
      color: #f0f5e9;
      font-family: Georgia, "Times New Roman", serif;
      font-weight: 400;
      font-size: clamp(42px, 5vw, 68px);
      letter-spacing: -.045em;
      line-height: .99;
    }
    .intro p {
      margin: 18px 0 0;
      color: rgba(226, 243, 236, .66);
      font-size: 13px;
      line-height: 1.75;
      letter-spacing: .015em;
    }
    .specimen {
      position: absolute;
      right: clamp(24px, 8vw, 112px);
      top: 25%;
      text-align: right;
      color: rgba(228, 244, 239, .77);
    }
    .specimen .label {
      margin-bottom: 8px;
      color: #90d8cf;
      font-size: 9px;
      letter-spacing: .24em;
      text-transform: uppercase;
    }
    .specimen strong {
      display: block;
      font-family: Georgia, "Times New Roman", serif;
      font-size: 23px;
      font-weight: 400;
      letter-spacing: .01em;
    }
    .specimen span {
      display: block;
      margin-top: 6px;
      color: rgba(221, 240, 235, .52);
      font-size: 10px;
      letter-spacing: .09em;
    }
    .bottom {
      position: absolute;
      left: clamp(24px, 5vw, 72px);
      right: clamp(24px, 5vw, 72px);
      bottom: clamp(22px, 4vh, 42px);
      display: flex;
      align-items: end;
      justify-content: space-between;
      color: rgba(217, 239, 232, .61);
      font-size: 9px;
      letter-spacing: .18em;
      text-transform: uppercase;
    }
    .location { line-height: 1.9; }
    .location b {
      color: rgba(231, 246, 239, .89);
      font-weight: 500;
    }
    .hint {
      display: flex;
      align-items: center;
      gap: 12px;
      padding: 13px 17px;
      border: 1px solid rgba(181, 231, 221, .17);
      border-radius: 30px;
      background: rgba(6, 29, 37, .34);
      backdrop-filter: blur(12px);
      color: rgba(232, 247, 239, .77);
      font-size: 9px;
      letter-spacing: .12em;
    }
    .pointer-icon {
      position: relative;
      width: 17px;
      height: 17px;
      border: 1px solid rgba(142, 230, 218, .8);
      border-radius: 50%;
      box-shadow: 0 0 12px rgba(95, 224, 211, .26);
    }
    .pointer-icon::after {
      content: "";
      position: absolute;
      width: 3px;
      height: 3px;
      left: 6px;
      top: 6px;
      border-radius: 50%;
      background: #a7efe0;
    }
    .edition {
      text-align: right;
      line-height: 1.9;
      color: rgba(217, 239, 232, .42);
    }
    @media (max-width: 650px) {
      .hud { padding: 20px 20px; }
      .brand { font-size: 9px; gap: 9px; }
      .brand-mark { width: 30px; height: 30px; }
      .live { padding: 8px 10px; font-size: 8px; }
      .intro { left: 25px; top: 20%; max-width: 245px; }
      h1 { font-size: clamp(39px, 11vw, 54px); }
      .intro p { max-width: 220px; font-size: 11px; }
      .specimen { right: 24px; top: auto; bottom: 23%; }
      .specimen strong { font-size: 18px; }
      .specimen span { font-size: 8px; }
      .bottom { left: 20px; right: 20px; bottom: 19px; }
      .hint { padding: 11px 13px; gap: 8px; font-size: 8px; letter-spacing: .08em; }
      .edition { display: none; }
      .location { font-size: 8px; }
    }
  </style>
</head>
<body>
  <div id="aquarium">
    <div class="hud">
      <div class="topbar">
        <div class="brand">
          <div class="brand-mark">✳</div>
          <div><small>Field notes · 03</small>Pelagic observatory</div>
        </div>
        <div class="live"><i class="live-dot"></i>Live habitat</div>
      </div>

      <div class="intro">
        <div class="eyebrow">An invitation to drift</div>
        <h1>A quiet world,<br>in motion.</h1>
        <p>Take a breath. Watch the school move through its blue-hour home.</p>
      </div>

      <div class="specimen">
        <div class="label">Now exploring</div>
        <strong>Open water</strong>
        <span>12 residents &nbsp;·&nbsp; Pacific reef</span>
      </div>

      <div class="bottom">
        <div class="location"><b>01 &nbsp; / &nbsp; The blue hour</b><br>Pacific · 18° 14′ N</div>
        <div class="hint"><i class="pointer-icon"></i>Move your pointer near the fish</div>
        <div class="edition">A small world beneath the surface<br>Observe gently · Drift slowly</div>
      </div>
    </div>
  </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 container = document.getElementById("aquarium");
    const scene = new THREE.Scene();

    const camera = new THREE.PerspectiveCamera(38, innerWidth / innerHeight, 0.1, 100);
    camera.position.set(0, 0, 19);
    camera.lookAt(0, 0, 0);

    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
    renderer.setSize(innerWidth, innerHeight);
    renderer.outputColorSpace = THREE.SRGBColorSpace;
    renderer.toneMapping = THREE.ACESFilmicToneMapping;
    renderer.toneMappingExposure = 1.12;
    renderer.setClearColor(0x031520, 0);
    container.prepend(renderer.domElement);

    scene.add(new THREE.HemisphereLight(0x91e5dd, 0x102b38, 2.0));
    const keyLight = new THREE.DirectionalLight(0xc6fff0, 2.3);
    keyLight.position.set(-5, 8, 8);
    scene.add(keyLight);
    const blueLight = new THREE.PointLight(0x42b8cf, 32, 30);
    blueLight.position.set(5, 2, 1);
    scene.add(blueLight);
    const warmLight = new THREE.PointLight(0xffb878, 12, 22);
    warmLight.position.set(-7, -2, 1);
    scene.add(warmLight);

    const habitat = new THREE.Group();
    scene.add(habitat);

    const background = new THREE.Mesh(
      new THREE.PlaneGeometry(200, 120),
      new THREE.ShaderMaterial({
        uniforms: { uTime: { value: 0 } },
        vertexShader: `
          varying vec2 vUv;
          void main() {
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
          }
        `,
        fragmentShader: `
          uniform float uTime;
          varying vec2 vUv;
          void main() {
            float depth = smoothstep(0.0, 1.0, vUv.y);
            vec3 deep = vec3(0.008, 0.039, 0.070);
            vec3 upper = vec3(0.025, 0.155, 0.174);
            vec3 col = mix(deep, upper, depth);
            float glow = exp(-pow((vUv.x - 0.46) * 3.4, 2.0)) *
                         exp(-pow((vUv.y - 0.89) * 4.6, 2.0));
            col += vec3(0.045, 0.12, 0.105) * glow;
            float wave = sin(vUv.x * 31.0 + sin(vUv.y * 14.0 + uTime * 0.24) * 2.1);
            float caustic = pow(max(0.0, wave * sin(vUv.y * 19.0 - uTime * 0.18)), 12.0);
            col += vec3(0.025, 0.105, 0.09) * caustic * smoothstep(0.48, 1.0, vUv.y);
            gl_FragColor = vec4(col, 1.0);
          }
        `,
        depthWrite: false
      })
    );
    background.position.set(0, 0, -8);
    scene.add(background);

    // A fine, quiet outline gives the scene the feeling of a glass habitat.
    const tankEdges = new THREE.LineSegments(
      new THREE.EdgesGeometry(new THREE.BoxGeometry(19.5, 10.2, 8)),
      new THREE.LineBasicMaterial({ color: 0x86ddd9, transparent: true, opacity: 0.19 })
    );
    habitat.add(tankEdges);

    const cornerMaterial = new THREE.MeshBasicMaterial({
      color: 0x8ee9dd, transparent: true, opacity: 0.48
    });
    for (const x of [-9.74, 9.74]) {
      for (const y of [-5.08, 5.08]) {
        const corner = new THREE.Mesh(new THREE.SphereGeometry(0.045, 10, 8), cornerMaterial);
        corner.position.set(x, y, 4.02);
        habitat.add(corner);
      }
    }

    const floor = new THREE.Mesh(
      new THREE.PlaneGeometry(19.2, 7.7),
      new THREE.MeshStandardMaterial({ color: 0x334b43, roughness: 0.96, metalness: 0.02 })
    );
    floor.rotation.x = -Math.PI / 2;
    floor.position.set(0, -4.9, 0);
    habitat.add(floor);

    const gravel = new THREE.InstancedMesh(
      new THREE.SphereGeometry(1, 7, 5),
      new THREE.MeshStandardMaterial({ color: 0x91a58a, roughness: 0.95 }),
      170
    );
    const dummy = new THREE.Object3D();
    let seed = 4812;
    const rand = () => {
      seed = (seed * 1664525 + 1013904223) >>> 0;
      return seed / 4294967296;
    };
    for (let i = 0; i < 170; i++) {
      const size = 0.018 + rand() * 0.055;
      dummy.position.set(-9.3 + rand() * 18.6, -4.83 + rand() * 0.035, -3.6 + rand() * 7.2);
      dummy.scale.set(size * 1.5, size * 0.55, size);
      dummy.rotation.set(rand(), rand() * 6, rand());
      dummy.updateMatrix();
      gravel.setMatrixAt(i, dummy.matrix);
    }
    habitat.add(gravel);

    function tube(points, radius, color, parent, segments = 22) {
      const curve = new THREE.CatmullRomCurve3(points.map(p => new THREE.Vector3(...p)));
      const mesh = new THREE.Mesh(
        new THREE.TubeGeometry(curve, segments, radius, 7, false),
        new THREE.MeshStandardMaterial({ color, roughness: 0.55, metalness: 0.04 })
      );
      parent.add(mesh);
      return mesh;
    }

    // Softly swaying ribbon plants.
    const plantGroups = [];
    function addSeaweed(x, z, color, count = 5) {
      const group = new THREE.Group();
      group.position.set(x, -4.82, z);
      habitat.add(group);
      plantGroups.push({ group, phase: rand() * 6 });
      for (let i = 0; i < count; i++) {
        const sway = (rand() - 0.5) * 0.7;
        const height = 1.45 + rand() * 1.9;
        const side = (rand() - 0.5) * 0.65;
        const points = [
          [(rand() - 0.5) * 0.3, 0, (rand() - 0.5) * 0.2],
          [side * 0.25, height * 0.35, sway * 0.35],
          [side + sway * 0.45, height * 0.72, -0.08],
          [side + sway, height, -0.12]
        ];
        const blade = tube(points, 0.025 + rand() * 0.018, color, group, 18);
        blade.material.emissive = new THREE.Color(color).multiplyScalar(0.12);
      }
    }
    addSeaweed(-8.55, -1.3, 0x2a9b83, 7);
    addSeaweed(-7.35, 1.4, 0x488d76, 5);
    addSeaweed(8.25, -1.1, 0x278e83, 8);
    addSeaweed(7.15, 2.0, 0x7c9b54, 5);

    // Branching coral and small rock gardens along the sand.
    function addCoral(x, z, color, height, branches) {
      const group = new THREE.Group();
      group.position.set(x, -4.75, z);
      habitat.add(group);
      const coralMat = new THREE.MeshStandardMaterial({
        color, roughness: 0.58, metalness: 0.02, emissive: color, emissiveIntensity: 0.06
      });
      const addBranch = (points, radius) => {
        const curve = new THREE.CatmullRomCurve3(points.map(p => new THREE.Vector3(...p)));
        group.add(new THREE.Mesh(new THREE.TubeGeometry(curve, 14, radius, 7, false), coralMat));
      };
      addBranch([[0, 0, 0], [0.05, height * 0.36, 0], [-0.08, height * 0.72, 0.02], [0.02, height, 0]], 0.085);
      for (let i = 0; i < branches; i++) {
        const side = i % 2 ? 1 : -1;
        const y = height * (0.22 + (i / branches) * 0.61);
        const length = 0.38 + rand() * 0.48;
        addBranch([
          [side * 0.02, y - 0.18, 0],
          [side * length * 0.42, y - 0.02, 0.01],
          [side * length, y + 0.27 + rand() * 0.2, 0.02]
        ], 0.047 + rand() * 0.014);
      }
    }
    addCoral(-8.1, 0.2, 0xd47e68, 1.7, 5);
    addCoral(7.8, -0.4, 0xc78a5e, 1.45, 4);
    addCoral(6.55, 2.3, 0x9c7094, 1.1, 4);

    const rockMat = new THREE.MeshStandardMaterial({ color: 0x52645b, roughness: 0.95 });
    for (const [x, z, scale] of [[-8.5, 1, 1], [-7.55, -2.5, 0.75], [8.5, 1.3, 1.1], [6.8, -2.7, 0.7], [4.7, 3.3, 0.65]]) {
      const rock = new THREE.Mesh(new THREE.DodecahedronGeometry(0.62, 1), rockMat);
      rock.position.set(x, -4.63, z);
      rock.scale.set(scale * 1.6, scale * 0.62, scale * 1.2);
      rock.rotation.set(rand(), rand() * 4, rand());
      habitat.add(rock);
    }

    function extrudedShape(shape, depth = 0.09, bevel = 0.018) {
      const geo = new THREE.ExtrudeGeometry(shape, {
        depth, bevelEnabled: true, bevelSegments: 2, steps: 1,
        bevelSize: bevel, bevelThickness: bevel, curveSegments: 12
      });
      geo.translate(0, 0, -depth / 2);
      return geo;
    }

    function makeFish(color, accent, size, x, y, z, phase) {
      const group = new THREE.Group();
      group.position.set(x, y, z);
      group.scale.setScalar(size);
      habitat.add(group);

      const bodyShape = new THREE.Shape();
      bodyShape.moveTo(-0.71, 0);
      bodyShape.quadraticCurveTo(-0.55, 0.43, -0.09, 0.4);
      bodyShape.quadraticCurveTo(0.43, 0.39, 0.71, 0.08);
      bodyShape.quadraticCurveTo(0.86, 0, 0.71, -0.08);
      bodyShape.quadraticCurveTo(0.4, -0.39, -0.09, -0.39);
      bodyShape.quadraticCurveTo(-0.56, -0.4, -0.71, 0);

      const bodyMat = new THREE.MeshStandardMaterial({
        color, roughness: 0.38, metalness: 0.08,
        emissive: color, emissiveIntensity: 0.075
      });
      const body = new THREE.Mesh(extrudedShape(bodyShape, 0.24, 0.035), bodyMat);
      group.add(body);

      const tail = new THREE.Group();
      tail.position.x = -0.65;
      group.add(tail);
      const tailShape = new THREE.Shape();
      tailShape.moveTo(0, 0);
      tailShape.quadraticCurveTo(-0.15, 0.31, -0.49, 0.43);
      tailShape.lineTo(-0.38, 0.06);
      tailShape.lineTo(-0.55, 0);
      tailShape.lineTo(-0.38, -0.06);
      tailShape.lineTo(-0.49, -0.43);
      tailShape.quadraticCurveTo(-0.15, -0.31, 0, 0);
      const finMat = new THREE.MeshStandardMaterial({
        color: accent, roughness: 0.42, metalness: 0.05,
        emissive: accent, emissiveIntensity: 0.12,
        side: THREE.DoubleSide
      });
      tail.add(new THREE.Mesh(extrudedShape(tailShape, 0.12, 0.012), finMat));

      const dorsalShape = new THREE.Shape();
      dorsalShape.moveTo(-0.36, 0.3);
      dorsalShape.quadraticCurveTo(-0.17, 0.65, 0.16, 0.58);
      dorsalShape.quadraticCurveTo(0.28, 0.51, 0.39, 0.31);
      dorsalShape.lineTo(-0.36, 0.3);
      const dorsal = new THREE.Mesh(extrudedShape(dorsalShape, 0.1, 0.01), finMat);
      dorsal.position.z = -0.015;
      group.add(dorsal);

      const pectoralShape = new THREE.Shape();
      pectoralShape.moveTo(0, 0.02);
      pectoralShape.quadraticCurveTo(0.12, -0.2, 0.42, -0.28);
      pectoralShape.quadraticCurveTo(0.31, -0.02, 0, 0.02);
      const pectoral = new THREE.Mesh(extrudedShape(pectoralShape, 0.035, 0.008), finMat);
      pectoral.position.set(-0.05, -0.05, 0.17);
      group.add(pectoral);

      // Fine curved markings on the near side catch the aquarium light.
      const stripeMat = new THREE.MeshBasicMaterial({ color: accent, transparent: true, opacity: 0.72 });
      for (let i = 0; i < 3; i++) {
        const sx = -0.28 + i * 0.19;
        const curve = new THREE.CatmullRomCurve3([
          new THREE.Vector3(sx - 0.08, 0.22, 0.139),
          new THREE.Vector3(sx + 0.02, 0.08, 0.145),
          new THREE.Vector3(sx - 0.01, -0.08, 0.145),
          new THREE.Vector3(sx - 0.1, -0.21, 0.13)
        ]);
        group.add(new THREE.Mesh(new THREE.TubeGeometry(curve, 12, 0.012, 5, false), stripeMat));
      }

      const eyeWhite = new THREE.MeshStandardMaterial({ color: 0xf4f2da, roughness: 0.26 });
      const pupilMat = new THREE.MeshStandardMaterial({ color: 0x10212a, roughness: 0.2 });
      const glintMat = new THREE.MeshBasicMaterial({ color: 0xffffff });
      for (const side of [-1, 1]) {
        const eye = new THREE.Mesh(new THREE.SphereGeometry(0.067, 12, 10), eyeWhite);
        eye.position.set(0.48, 0.14, side * 0.19);
        group.add(eye);
        const pupil = new THREE.Mesh(new THREE.SphereGeometry(0.037, 10, 8), pupilMat);
        pupil.position.set(0.501, 0.145, side * 0.239);
        group.add(pupil);
        const glint = new THREE.Mesh(new THREE.SphereGeometry(0.012, 7, 6), glintMat);
        glint.position.set(0.514, 0.162, side * 0.27);
        group.add(glint);
      }

      return {
        group, tail, phase,
        size,
        centerX: x, centerY: y,
        rangeX: 0.55 + rand() * 1.0,
        rangeY: 0.28 + rand() * 0.55,
        speed: 0.19 + rand() * 0.21,
        start: rand() * 6.28,
        facing: rand() > 0.5 ? 1 : -1,
        z
      };
    }

    const fishPalette = [
      [0xf5b94e, 0xffe39a], [0x45c9bd, 0xb1fff0], [0xf17c5b, 0xffc69a],
      [0x77a9dc, 0xc3e8ff], [0xd48ab4, 0xf8c7e2], [0xd4ca65, 0xfff0a5],
      [0x62b88f, 0xc3f3ad], [0xe68d50, 0xffd18f], [0x51b4d0, 0xb1f0ef],
      [0xb2a1e2, 0xe4d5ff], [0xef9b78, 0xffd0a9], [0x87c6b0, 0xd1f4dc]
    ];
    const fishes = [];
    for (let i = 0; i < 12; i++) {
      const [body, accent] = fishPalette[i];
      const size = 0.68 + rand() * 0.38;
      const x = -7.0 + rand() * 14;
      const y = -2.75 + rand() * 6.2;
      const z = -2.4 + rand() * 4.8;
      fishes.push(makeFish(body, accent, size, x, y, z, rand() * 6.28));
    }

    // A subtle pointer ripple sits just inside the front glass.
    const ripple = new THREE.Group();
    const rippleMat = new THREE.MeshBasicMaterial({
      color: 0x9df4e7, transparent: true, opacity: 0, depthWrite: false
    });
    const ringA = new THREE.Mesh(new THREE.TorusGeometry(0.3, 0.012, 8, 56), rippleMat);
    const ringB = new THREE.Mesh(new THREE.TorusGeometry(0.48, 0.007, 7, 56), rippleMat.clone());
    ringB.material.opacity = 0;
    ripple.add(ringA, ringB);
    ripple.position.z = 3.9;
    habitat.add(ripple);

    const pointer = new THREE.Vector2();
    const raycaster = new THREE.Raycaster();
    const pointerPlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
    const pointerWorld = new THREE.Vector3();
    let pointerActiveUntil = -1;
    let pointerInside = false;

    function movePointer(event) {
      const rect = renderer.domElement.getBoundingClientRect();
      pointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
      pointer.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
      raycaster.setFromCamera(pointer, camera);
      if (raycaster.ray.intersectPlane(pointerPlane, pointerWorld)) {
        pointerActiveUntil = performance.now() * 0.001 + 2.0;
        pointerInside = true;
      }
    }
    renderer.domElement.addEventListener("pointermove", movePointer);
    renderer.domElement.addEventListener("pointerenter", () => { pointerInside = true; });
    renderer.domElement.addEventListener("pointerleave", () => { pointerInside = false; });

    const bubbles = [];
    const bubbleGeo = new THREE.SphereGeometry(0.045, 10, 8);
    for (let i = 0; i < 38; i++) {
      const mat = new THREE.MeshBasicMaterial({
        color: 0xb9f5ec, transparent: true, opacity: 0.18 + rand() * 0.32
      });
      const bubble = new THREE.Mesh(bubbleGeo, mat);
      bubble.position.set(-9 + rand() * 18, -4.5 + rand() * 9, -3.3 + rand() * 6.6);
      const scale = 0.35 + rand() * 0.95;
      bubble.scale.setScalar(scale);
      habitat.add(bubble);
      bubbles.push({ mesh: bubble, speed: 0.18 + rand() * 0.48, drift: (rand() - 0.5) * 0.18, phase: rand() * 6 });
    }

    const clock = new THREE.Clock();
    function animate() {
      requestAnimationFrame(animate);
      const dt = Math.min(clock.getDelta(), 0.04);
      const time = clock.elapsedTime;
      background.material.uniforms.uTime.value = time;

      for (const plant of plantGroups) {
        plant.group.rotation.z = Math.sin(time * 0.7 + plant.phase) * 0.025;
      }

      const pointerStrength = pointerInside && time < pointerActiveUntil
        ? THREE.MathUtils.clamp((pointerActiveUntil - time) / 0.55, 0, 1)
        : 0;

      const ease = 1 - Math.exp(-dt * 1.7);
      for (const fish of fishes) {
        const phase = time * fish.speed + fish.start;
        let tx = fish.centerX + Math.sin(phase) * fish.rangeX;
        let ty = fish.centerY + Math.sin(phase * 1.31 + fish.phase) * fish.rangeY;

        if (pointerStrength > 0) {
          const dx = fish.group.position.x - pointerWorld.x;
          const dy = fish.group.position.y - pointerWorld.y;
          const distance = Math.sqrt(dx * dx + dy * dy + Math.pow(fish.z * 0.25, 2));
          const radius = 3.15 + fish.size * 0.35;
          if (distance < radius) {
            const force = Math.pow((radius - distance) / radius, 1.3) * pointerStrength;
            const len = Math.max(0.001, Math.sqrt(dx * dx + dy * dy));
            tx += (dx / len) * force * 3.2;
            ty += (dy / len) * force * 2.6 + force * 0.22;
          }
        }

        tx = THREE.MathUtils.clamp(tx, -8.9, 8.9);
        ty = THREE.MathUtils.clamp(ty, -4.1, 4.15);
        const oldX = fish.group.position.x;
        const oldY = fish.group.position.y;
        fish.group.position.x += (tx - oldX) * ease;
        fish.group.position.y += (ty - oldY) * ease;
        fish.group.position.z = fish.z + Math.sin(time * 0.9 + fish.phase) * 0.08;

        const vx = fish.group.position.x - oldX;
        const vy = fish.group.position.y - oldY;
        if (Math.abs(vx) + Math.abs(vy) > 0.00015) {
          fish.group.rotation.z = Math.atan2(vy, vx);
        }
        fish.tail.rotation.y = Math.sin(time * 7.2 + fish.phase) * 0.32;
        fish.group.position.y += Math.sin(time * 1.4 + fish.phase) * 0.0018;
      }

      for (const b of bubbles) {
        b.mesh.position.y += b.speed * dt;
        b.mesh.position.x += Math.sin(time * 0.8 + b.phase) * b.drift * dt;
        if (b.mesh.position.y > 4.8) {
          b.mesh.position.y = -4.7;
          b.mesh.position.x = -9 + rand() * 18;
        }
      }

      if (pointerInside) {
        raycaster.setFromCamera(pointer, camera);
        if (raycaster.ray.intersectPlane(pointerPlane, pointerWorld)) {
          ripple.position.x = pointerWorld.x;
          ripple.position.y = pointerWorld.y;
        }
      }
      const rippleOpacity = pointerStrength * 0.48;
      rippleMat.opacity += (rippleOpacity - rippleMat.opacity) * 0.12;
      ringB.material.opacity = rippleMat.opacity * 0.52;
      const pulse = 1 + Math.sin(time * 3.0) * 0.08;
      ringA.scale.setScalar(pulse);
      ringB.scale.setScalar(0.93 + Math.sin(time * 2.4 + 1) * 0.07);

      renderer.render(scene, camera);
    }
    animate();

    function resize() {
      const width = innerWidth;
      const height = innerHeight;
      camera.aspect = width / height;
      camera.position.z = width < 650 ? 23 : 19;
      camera.fov = width < 650 ? 42 : 38;
      camera.updateProjectionMatrix();
      renderer.setSize(width, height);
      renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
    }
    addEventListener("resize", resize);
  </script>
</body>
</html>
Reasoning trace (6,171 tokens)
**Designing a 3D aquarium**

I need to craft an HTML 3D aquarium, focusing on rendering quality with Three.js. I’ll import the necessary modules to create the scene, ensuring the fish meshes are animated. I want a beautiful aesthetic with a size of 1280x800, using a deep blue gradient background, particles, plants, and rocks.

For the fish, I might consider procedural geometry, making them vibrant and lively. The camera should capture the aquarium's dimensions and ensure visuals integrate well without any distracting transparent intersections.**Constructing a 3D aquarium frame**

I’m considering building a subtle, translucent rounded rectangle frame using BoxGeometry edges. The tank dimensions will be 20 x 11 x 9, with the camera positioned at z=15. The fish should swim in the x/y plane, and I’ll group their geometry with an ellipsoid shape for the body and triangle fins. The scene will have a deep blue background and CSS gradient with fog for atmosphere. I’ll also incorporate ambient, directional, and point lights to enhance the visuals while ensuring the fish bodies have an appealing shape and dimensions.**Shaping fish geometry**

I’m planning to shape the fish using quadratic curves for a smooth silhouette, with the local fish orientation aligned along +X. The tail-root will be positioned at x=-0.65, while the body maxes out at 0.65. I’ll use ExtrudeGeometry to center the depth by translating it to -depth/2. For the fish, I’ll create a standard mesh with a colored body and include a dorsal fin shape, triangle-shaped tail fin, and pectoral fins. I also want to add an eye dot with a highlight on the near side.**Designing an aquarium interface**

I'm thinking about a beautiful, non-intrusive interface for the aquarium. I like the idea of a top left glass panel with "PELAGIC / OBSERVATORY" on it, and maybe adding some sound to the right. I also want a bottom center interaction hint saying, "Move your pointer near a fish..." with fish names included. The main text should say "Aquarium" in a nice font. The content canvas should be 1280 wide, and I'd need to consider a mobile fit so it displays at full viewport height.**Considering geometrical design**

I'm thinking about using TubeGeometry for the vertical corner edges and forming a rectangle for the front and back lines. I'd like to implement EdgesGeometry with LineSegments and a transparent material for a nice effect, perhaps with a near color. The front may overlap the objects at the front edges only, which is an interesting consideration. It's nice to explore these details to enhance the design!**Designing the shader material**

I'm focusing on the shader material's uniforms and how to incorporate time for dynamic effects. I'm considering a gradient mix with specific colors to create a smooth transition, possibly lightening the top vertically. Adding several blurry light glows could enhance depth. I wonder if incorporating caustics with a sine function might add interesting smooth lines but balance is key to keep strength low. It feels like there's a lot to optimize here!**Considering aquarium design**

I'm exploring options to adjust the aquarium design for mobile views. Maybe I could set narrower dimensions and compress the fish positions while dynamically setting the scene group. It would be beneficial to maintain proportionality, so condensing horizontally might make everything look weird. A better approach might involve using a vertical camera to ensure the scene scales properly. The user needs the design to adapt without horizontal scrollbars, especially for desktop screenshots. I’ll keep that in mind!**Designing a fish model**

I'm thinking about avoiding plant shapes and focusing on creating a fish. The fish's body will have a mesh standard, and I want to use extruded geometry with a bevel for smoothness. The color should be distinct, and I might add accents on the belly mesh using the same shape. I need to consider the body material as well. For the dorsal fin, I'm visualizing a triangle shape, extruded and assigned a bright color.**Considering scene setup**

I’m working on the scene with a focus on the orientation and fish movements. I’m considering fog using a specific density, but might have to adjust it according to the far plane. The renderer is set for a particular color space, and I need to create a floor plane with a caustic shader. 

I’ve got to ensure the layout is responsive, especially for the HUD, and pay attention to text placement so it doesn’t block key visual elements. I also need to make sure my JS modules import correctly and handle resizing appropriately.**Setting up the scene**

I’m designing a background plane using ShaderMaterial and giving the fish random colors and positions. To create plant life, I’ll use TubeGeometry from CatmullRomCurve3 positions for the fronds, placing the plant group base at specified coordinates.

I’ll have to build the tubes recursively for coral, ensuring that branch colors and radii are set correctly. The sand floor will be wide and deep, positioned with the right orientation. I’ll also add rocks and ensure to include a tank frame while positioning everything correctly to create a vibrant underwater scene.**Updating parameters for simulation**

I'm considering how to set the target with an orbit range and offsets while preventing any sudden jumps. I'm setting Y to minimum -4.25 and maximum 4.15, keeping Z fixed with a slight adjustment. It seems like I want to simulate fish floating, managing the bubbles, and having plants sway with a group rotation based on a sine function influenced by time! It’s an interesting balance to create a smooth and engaging environment.**Designing a 3D scene**

I’m working on building a 3D scene with ambient lighting set to 2.4 to keep it bright. I’ll create plants with enough complexity, using TubeGeometry for coral branches. The colors will include red and purple, and I’ll strategically place them in the scene. Also, I’ll detail the fish and use EdgesGeometry for visibility along the corners. For user interaction, I want to instruct users to "MOVE TO INTERACT," ensuring everything is clear and accessible, especially on mobile.