Models 44

Nemotron 3.5 Lightning (free)

by NVIDIAReasoning170 console errors
Open in new window

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

How it went

Total time
2 min 51 s
Cost
$0
Output tokens
8,277
Spent thinking
6,497 tokens
Speed
48.6 tok/s
Console errors
170
First token after
317 ms
First visible text after
2 min 31 s
Prompt tokens
448
Code size
6.0 KB
Finish reason
stop
Served by
Nvidia
Model id
nvidia/nemotron-3.5-lightning:free
Reasoning sent
model default
Max output
32,000 tokens
Generated
Nov 9, 2026

Console errors

Caught in the first 3 seconds after load, in a sandboxed iframe like the one above.

f.quaternion.setFromUnitVector is not a function

f.quaternion.setFromUnitVector is not a function

f.quaternion.setFromUnitVector is not a function

f.quaternion.setFromUnitVector is not a function

f.quaternion.setFromUnitVector is not a function

f.quaternion.setFromUnitVector is not a function

f.quaternion.setFromUnitVector is not a function

f.quaternion.setFromUnitVector is not a function

f.quaternion.setFromUnitVector is not a function

f.quaternion.setFromUnitVector is not a function

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 · 184 lines

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>3D Aquarium</title>
  <style>
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body {
      overflow-x: hidden;
      background: #0d0d24;
      font-family: sans-serif;
    }
    #container {
      width: 100vw;
      height: 100vh;
    }
    #start-message {
      position: absolute;
      top: 50%; left: 50%;
      transform: translate(-50%, -50%);
      color: #e0e0e0;
      font-size: 1.8rem;
      pointer-events: auto;
      user-select: none;
    }
  </style>
</head>
<body>
  <div id="container"></div>
  <div id="start-message">Click or press a key to start</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("container");
    const scene = new THREE.Scene();

    const width = container.clientWidth || window.innerWidth;
    const height = container.clientHeight || window.innerHeight;
    const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 1000);
    camera.position.set(0, 0, 10);

    const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setSize(width, height);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    renderer.setClearColor(0x0d0d24, 1);
    container.appendChild(renderer.domElement);

    scene.add(new THREE.AmbientLight(0xffffff, 0.5));
    const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
    dirLight.position.set(5, 5, 5);
    scene.add(dirLight);

    scene.fog = new THREE.Fog(0x0d0d24, 5, 20);

    // Fish
    const fishCount = 6;
    const fish = [];
    const fishGeometry = new THREE.IcosahedronGeometry(0.25, 0);
    const fishMaterial = new THREE.MeshStandardMaterial({ color: 0xff6b6b, flatShading: true });

    for (let i = 0; i < fishCount; i++) {
      const m = new THREE.Mesh(fishGeometry, fishMaterial);
      m.position.set(
        (Math.random() - 0.5) * 6,
        (Math.random() - 0.5) * 6,
        (Math.random() - 0.5) * 6
      );
      m.userDirection = new THREE.Vector3(
        (Math.random() - 0.5) * 0.08,
        (Math.random() - 0.5) * 0.08,
        (Math.random() - 0.5) * 0.08
      );
      m.baseSpeed = 0.015 + Math.random() * 0.015;
      m.speed = m.baseSpeed;
      m.target = new THREE.Vector3();
      scene.add(m);
      fish.push(m);
    }

    // Mouse tracking
    const mouse = { x: null, y: null };
    document.addEventListener("mousemove", (e) => {
      mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
      mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
    });

    const startMessage = document.getElementById("start-message");
    let isStarted = false;

    // Click or keypress to start interaction mode
    container.addEventListener("click", () => {
      isStarted = true;
      startMessage.style.display = "none";
    });
    document.addEventListener("keydown", (e) => {
      if (e.code === "Space" || e.code === "Enter" || e.code === "KeyC") {
        isStarted = true;
        startMessage.style.display = "none";
      }
    });

    const clock = new THREE.Clock();

    function animate() {
      requestAnimationFrame(animate);
      const delta = Math.min(clock.getDelta(), 0.1);
      const time = clock.getElapsedTime();

      // Mouse repulsion when started
      if (isStarted && mouse.x !== null && mouse.y !== null) {
        const mouseWorld = new THREE.Vector3(mouse.x, mouse.y, 1).unproject(camera);
        for (const f of fish) {
          const dist = f.position.distanceTo(mouseWorld);
          if (dist < 3 && dist > 0.01) {
            const repel = new THREE.Vector3()
              .subVectors(f.position, mouseWorld)
              .normalize()
              .multiplyScalar(0.6 / dist);
            f.userDirection.add(repel);
          }
        }
      }

      // Update fish
      for (const f of fish) {
        // Move forward
        f.position.add(f.userDirection.clone().multiplyScalar(f.speed));

        // Boundary bounce (margin from edge)
        const margin = 0.3;
        const size = 3;
        if (f.position.x > size - margin) { f.position.x = size - margin; f.userDirection.x *= -1; }
        if (f.position.x < -size + margin) { f.position.x = -size + margin; f.userDirection.x *= -1; }
        if (f.position.y > size - margin) { f.position.y = size - margin; f.userDirection.y *= -1; }
        if (f.position.y < -size + margin) { f.position.y = -size + margin; f.userDirection.y *= -1; }
        if (f.position.z > size - margin) { f.position.z = size - margin; f.userDirection.z *= -1; }
        if (f.position.z < -size + margin) { f.position.z = -size + margin; f.userDirection.z *= -1; }

        // Gentle random turning
        f.userDirection.multiplyScalar(0.85);
        f.userDirection.addScaledVector(
          new THREE.Vector3(
            (Math.random() - 0.5) * 0.01,
            (Math.random() - 0.5) * 0.01,
            (Math.random() - 0.5) * 0.01
          ),
          0.15
        );

        // Face direction
        if (f.userDirection.lengthSq() > 0.0001) {
          f.quaternion.setFromUnitVector(new THREE.Vector3(0, 0, 1), f.userDirection.normalize());
        }

        // Gentle bobbing
        f.position.y += Math.sin(time * 2 + f.id * Math.PI) * 0.01;
      }

      // Camera subtle orbit
      camera.position.y = Math.sin(time * 0.4) * 1.5;
      camera.position.x = Math.cos(time * 0.3) * 0.5;
      camera.lookAt(0, 0, 0);

      renderer.render(scene, camera);
    }

    window.addEventListener("resize", () => {
      const w = container.clientWidth || window.innerWidth;
      const h = container.clientHeight || window.innerHeight;
      camera.aspect = w / h;
      camera.updateProjectionMatrix();
      renderer.setSize(w, h);
    });

    animate();
  </script>
</body>
</html>
Reasoning trace (6,497 tokens)
Here's a thinking process:

1.  **Analyze User Request:**
   - Create a 3D aquarium with Three.js
   - Fish swim away when mouse moves near them
   - Must use specific import map for Three.js (0.170.0)
   - Output must be a single fenced code block with `.html`
   - Inline CSS, JS, no external files/network except the CDN URLs in import map
   - No localStorage, sessionStorage, cookies, alert, confirm, prompt, window.open
   - Must look good at 1280x800 and adapt to 375px, no horizontal scroll
   - Something meaningful visible on first paint (screenshot at ~3s)
   - Interesting elements visible quickly
   - Games: must have self-playing demo mode with "Click or press a key to start" message
   - iframe only gets keyboard events after user clicks: display "Click or press a key to start"
   - Must run with zero errors in latest Chrome console

2.  **Key Constraints & Design Decisions:**
   - Import map is pre-specified: `<script type="importmap">...</script>` - I need to include exactly that.
   - Three.js from CDN via import map.
   - Fish that swim away from mouse - I'll need to create fish entities, maybe simple geometries (icosphere or box with texture/color), with basic movement.
   - Mouse proximity detection: raycasting or just checking distance between mouse position and fish positions in the render loop.
   - Self-playing demo mode: fish swim around randomly/automatically until user clicks/presses a key, then mouse interaction takes over. Or maybe the "demo" is just fish swimming naturally, and the "start" message is to enable mouse interaction.
   - "Click or press a key to start" message - required because iframe only gets keyboard events after click. I'll make it prominent.
   - Responsive: 1280x800 down to 375px, no horizontal scroll. I'll use responsive canvas sizing, maybe CSS viewport units.
   - First paint meaningful: the "click to start" message + background/ambient scene visible immediately.
   - No alert/confirm/prompt.
   - Zero console errors.

3.  **Structure the HTML:**
   - `<!DOCTYPE html>`
   - `<html>`
   - `<head>`: import map, meta tags for viewport, title, inline style for body/container
   - `<body>`: a fullscreen canvas container. The Three.js canvas will be appended.
   - `<style>`: reset, box-sizing, body/margin 0, overflow hidden, responsive container.
   - `<script type="importmap">...</script>` exactly as given.
   - Then a script tag that imports Three.js using the import map: `<script type="module"> import * as THREE from "three"; ... </script>`
   - Three.js setup: scene, camera (PerspectiveCamera), renderer (WebGLRenderer), set size, append to container.
   - Lights: ambient + directional so fish are visible.
   - Fish: I'll create a few fish (maybe 5-8). Each fish has position, velocity, geometry, material. They swim in random directions, bounce off boundaries.
   - Mouse interaction: on mouse move, for each fish, if mouse is within some distance, apply a force pushing fish away.
   - Animation loop: update fish positions, handle boundaries, renderer.render.
   - "Click or press a key to start" message: centered, visible initially. When user clicks or presses a key, hide message and enable mouse interaction (or just start the demo mode). Actually, the requirement: "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." And "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."
   - So I'll have the fish swimming autonomously from the start (demo mode). The message says "Click or press a key to start". When clicked/pressed, mouse interaction becomes active (fish swim away from mouse). But since the demo is already running, maybe the message just needs to be there to unblock keyboard/click events. I'll make it so that initially fish swim randomly. On first click/keypress, I'll switch to mouse-avoidance mode, or just keep the random swimming but enable the mouse effect. Actually, I can have the fish always swim randomly, and the mouse avoidance is always active but maybe more pronounced after start. To keep it simple: fish swim autonomously always. The "Click or press a key to start" message is just to satisfy the iframe constraint. When user clicks, the message disappears, and mouse interaction begins (fish swim away from mouse). I'll make the mouse interaction the main feature after click.
   - Let's design: 
     - Initially: message visible, fish swim in simple patterns (or stationary with subtle movement), mouse has no effect.
     - On click/keypress: message hides, mouse avoidance activates. Fish swim randomly but steer away from mouse cursor proximity.
   - This satisfies "self-playing demo mode behind a message" and "mouse interaction after click".

   - Fish geometry: I'll use `TetrahedronGeometry` or `IcosahedronGeometry` scaled small, with a color material. No textures to keep it inline and simple.
   - Fish movement: each fish has a target direction, moves forward, rotates slightly, if too close to boundary, turn away. Mouse avoidance: add a repulsion vector when mouse is near.
   - Keep it lightweight, no textures, no audio, no external assets.

   - Resizing: on window resize, update camera aspect and renderer size. I'll use CSS `width: 100%; height: 100%;` on the canvas container, and set renderer size in the loop based on container clientWidth/clientHeight.

   - Viewport adaptivity: 1280x800 down to 375px. I'll make the canvas fill the body, use `transform: scale()` or just let Three.js camera aspect adapt. No horizontal scroll: set `overflow-x: hidden` on body, and ensure canvas width doesn't exceed viewport. I'll use `max-width: 100%` and `height: auto` or just full viewport.

   - First paint: The scene with background color, floor/ground maybe not needed. Just water color background, floating fish, and the "Click or press a key to start" message. Visible immediately on load.

   - Console errors: careful with Three.js r170 APIs. Import map points to `three.module.js` and `jsm/` for examples. I might not need examples. I'll use core Three.js. Be careful with `WebGLRenderer` parameters. Use `antialias: true` maybe, but keep simple.

   - Let's sketch the code mentally:

   ```html
   <!DOCTYPE html>
   <html lang="en">
   <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>3D Aquarium</title>
     <style>
       * { box-sizing: border-box; margin: 0; padding: 0; }
       body { overflow-x: hidden; background: #1a1a2e; font-family: sans-serif; }
       #container { width: 100vw; height: 100vh; }
       #start-message {
         position: absolute;
         top: 50%; left: 50%;
         transform: translate(-50%, -50%);
         color: #fff;
         font-size: 2rem;
         text-align: center;
         pointer-events: auto;
       }
     </style>
   </head>
   <body>
     <div id="container"></div>
     <div id="start-message">Click or press a key to start</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";
       
       // Scene, camera, renderer
       const container = document.getElementById("container");
       const scene = new THREE.Scene();
       const camera = new THREE.PerspectiveCamera(60, container.clientWidth / container.clientHeight, 0.1, 1000);
       camera.position.set(0, 0, 10);
       
       const renderer = new THREE.WebGLRenderer({ antialias: true });
       renderer.setSize(container.clientWidth, container.clientHeight);
       renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
       container.appendChild(renderer.domElement);
       
       // Lights
       scene.add(new THREE.AmbientLight(0xffffff, 0.5));
       const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
       dirLight.position.set(5, 5, 5);
       scene.add(dirLight);
       
       // Water background
       scene.fog = new THREE.Fog(0x1a1a2e, 10, 50);
       
       // Fish data
       const fishCount = 8;
       const fish = [];
       const fishGeometry = new THREE.IcosahedronGeometry(0.3, 0);
       const fishMaterial = new THREE.MeshStandardMaterial({ color: 0xff6b6b, flatShading: true });
       
       for (let i = 0; i < fishCount; i++) {
         const fishMesh = new THREE.Mesh(fishGeometry, fishMaterial);
         fishMesh.position.set(
           (Math.random() - 0.5) * 8,
           (Math.random() - 0.5) * 8,
           (Math.random() - 0.5) * 8
         );
         fishMesh.userDirection = new THREE.Vector3(
           (Math.random() - 0.5) * 0.1,
           (Math.random() - 0.5) * 0.1,
           (Math.random() - 0.5) * 0.1
         );
         fishMesh.baseSpeed = 0.02 + Math.random() * 0.02;
         fishMesh.speed = fishMesh.baseSpeed;
         fishMesh.alive = true;
         scene.add(fishMesh);
         fish.push(fishMesh);
       }
       
       // Mouse tracking
       const mouse = { x: null, y: null };
       document.addEventListener("mousemove", (e) => {
         mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
         mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
       });
       
       // Start message
       const startMessage = document.getElementById("start-message");
       let isStarted = false;
       
       function startAquarium() {
         isStarted = true;
         startMessage.style.display = "none";
       }
       
       // Click or keypress to start
       container.addEventListener("click", startAquarium);
       document.addEventListener("keydown", startAquarium);
       
       // Animation loop
       const clock = new THREE.Clock();
       
       function animate() {
         requestAnimationFrame(animate);
         const delta = clock.getDelta();
         const time = clock.getElapsedTime();
         
         // Mouse repulsion
         if (isStarted && mouse.x !== null && mouse.y !== null) {
           const mousePos = new THREE.Vector3(mouse.x, mouse.y, 1).unproject(camera);
           for (const f of fish) {
             const distance = f.position.distanceTo(mousePos);
             if (distance < 3 && distance > 0) {
               const repel = new THREE.Vector3()
                 .subVectors(f.position, mousePos)
                 .normalize()
                 .multiplyScalar(0.5 / distance);
               f.userDirection.add(repel);
             }
           }
         }
         
         // Update fish
         for (const f of fish) {
           // Move forward
           f.position.add(f.userDirection.clone().multiplyScalar(f.speed));
           
           // Boundary bounce
           const margin = 0.5;
           if (f.position.x > 4 - margin) { f.position.x = 4 - margin; f.userDirection.x *= -1; }
           if (f.position.x < -4 + margin) { f.position.x = -4 + margin; f.userDirection.x *= -1; }
           if (f.position.y > 4 - margin) { f.position.y = 4 - margin; f.userDirection.y *= -1; }
           if (f.position.y < -4 + margin) { f.position.y = -4 + margin; f.userDirection.y *= -1; }
           if (f.position.z > 4 - margin) { f.position.z = 4 - margin; f.userDirection.z *= -1; }
           if (f.position.z < -4 + margin) { f.position.z = -4 + margin; f.userDirection.z *= -1; }
           
           // Gentle turning
           f.userDirection.multiplyScalar(0.9); // damping
           f.userDirection.addScaledVector(
             new THREE.Vector3(
               (Math.random() - 0.5) * 0.01,
               (Math.random() - 0.5) * 0.01,
               (Math.random() - 0.5) * 0.01
             ),
             0.1
           );
           
           // Rotate fish to face direction
           f.rotation.copy(f.userDirection);
         }
         
         camera.position.y = Math.sin(time * 0.5) * 2;
         camera.lookAt(0, 0, 0);
         
         renderer.render(scene, camera);
       }
       
       window.addEventListener("resize", () => {
         camera.aspect = container.clientWidth / container.clientHeight;
         camera.updateProjectionMatrix();
         renderer.setSize(container.clientWidth, container.clientHeight);
       });
       
       animate();
     </script>
   </body>
   </html>
   ```

   Wait, I need to check constraints carefully:
   - "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." -> I have that. Keyboard events are blocked until container click. I have `container.addEventListener("click", startAquarium)` and `document.addEventListener("keydown", startAquarium)`. But wait: "The iframe only receives keyboard events after the user clicks it" - this means the sandbox iframe needs a click first. My code listens on `container` which is inside the iframe, so clicking the canvas should unblock keyboard events. But the message says "Click or press a key to start". I'll make sure the message is visible and clickable.
   - "Games must start in a self-playing demo mode (a simple AI plays) behind a 'Click or press a key to start' message" -> The fish swim autonomously even before start? I'll have them swim from the start, but the mouse avoidance only activates after `isStarted = true`. Or maybe the demo mode is just the fish swimming randomly, and after click, mouse avoidance kicks in. That works.
   - "Something meaningful must be visible on first paint" -> The scene with fish and background message is visible immediately.
   - "No horizontal scrollbar" -> I have `overflow-x: hidden` on body.
   - "Adapt gracefully down to 375px wide" -> The canvas fills 100vw, but on narrow screens, Three.js camera aspect will adjust. I should ensure the container doesn't cause overflow. Using `width: 100vw` might cause horizontal scroll on mobile if there's address bar movement. Better to use `width: 100%` and `max-width: 100%`? Actually, for a webGL canvas filling viewport, `100vw` is common but can cause overflow on some devices. I'll use `width: 100%; height: 100%;` and `body { margin: 0; overflow: hidden; }`. That should be fine.
   - "Load Three.js only through this import map" -> I'll include exactly the import map script.
   - "CSS in <style>, JavaScript in <script>" -> inline only.
   - "No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task." -> The import map CDNs are allowed. I'm not loading any images or fonts. Fish colors are solid.
   - "Do not use localStorage, sessionStorage, cookies, alert(), confirm(), prompt(), window.open()" -> I won't.
   - "zero errors in the console of the latest Chrome" -> Need to be careful with Three.js r170. Check for deprecated APIs. `IcosahedronGeometry` is fine. `MeshStandardMaterial` is fine. `WebGLRenderer` fine. `Fog` fine. `Vector3`, `Vector2` fine. I should avoid `unproject` maybe? It's fine in r170. Actually, `unproject` exists. But I used `mouse.x` and `mouse.y` as normalized device coordinates, then `new THREE.Vector3(mouse.x, mouse.y, 1).unproject(camera)`. That should work. Alternatively, I can just use raycasters or simple distance calc without unproject. Let me simplify mouse interaction: instead of unprojecting, I can just use the mouse NDC coordinates and compute distance to fish in screen space or world space via a simple approach. Actually, unproject is fine. But I'll double-check: `camera.unproject` is a method on `Camera`. Yes.

   Let me refine the code to be more robust and avoid potential errors:

   - I'll move the mouse tracking inside the import map script area or keep it separate. It's all in one `<script type="module">`.
   - I'll make the fish use `MeshBasicMaterial` maybe so lighting doesn't affect them too much, or keep `MeshStandardMaterial` with simple lights. I'll keep `MeshStandardMaterial`.
   - I'll reduce fish count to maybe 6 for performance.
   - I'll make the start message more visible and ensure pointer-events work properly.
   - I'll handle the case where `mouse.x` is null initially.
   - I'll make sure `renderer.setSize` doesn't cause issues on resize.
   - I'll add `preventDefault` where needed? Not necessary.
   - I'll ensure the "Click or press a key to start" message is there on first paint, and disappears on click/keypress.

   Let's restructure the code more cleanly:

   Actually, I realize there's a subtle constraint: "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." This implies that without a click, keyboard events won't be received by the iframe. So the message must be there, and the user must click the iframe first. My code listens on `container` for click, which is the canvas div. That should work. Also `document.addEventListener("keydown", ...)` might not work until the iframe is clicked, but the message instruction says to display it, and clicking the container should unblock it. I'll keep both click on container and keydown on document, but the keydown might not fire until click. That's fine; the message is there to instruct.

   Wait: "The iframe only receives keyboard events after the user clicks it" - this is a sandbox constraint. So `document.addEventListener("keydown")` outside the iframe won't affect it. But inside the iframe, after the user clicks the canvas, keyboard events are received. So I should listen for click on the canvas, then maybe enable keyboard interaction. But my demo doesn't really use keyboard for control; it's mouse-based. The message just needs to be there. I'll keep the click handler on container, and also the message. I won't rely on keydown for anything critical.

   Actually, to be safe, I'll just have the click on the container hide the message, and I won't listen for keydown globally, or I will but it's fine. The requirement says "if the demo uses the keyboard, display a clear 'Click or press a key to start' message." My demo uses mouse, not keyboard for control. But the constraint "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." It's a conditional: if keyboard is used, display message. Since I'm using mouse, maybe I don't strictly need the keypress handling, but it's safer to include it as the message says "Click or press a key to start" and the general pattern for these demos. I'll include both click and keydown to start, just in case.

   Let's finalize the code structure.

   One thing: I need to make sure the import map script is exactly as specified, and the Three.js import uses `"three"` key. Yes.

   Let's write the final HTML.

   I'll also add `resize` listener that updates camera aspect and renderer size. I'll use `container.clientWidth` and `container.clientHeight`.

   For the fish swimming away from mouse: I'll use a simple repulsion. To avoid `unproject` complexity, I can compute mouse world position using raycasting or just use the mouse's NDC and add a small offset. Actually, `unproject` is fine. But I'll keep it simple: I'll create a plane or just use the camera's position and mouse direction. Let me just use a basic approach: on mousemove, I'll update `mouse.ndc`. In the loop, I'll raycast from camera through mouse to get world position, then repel fish within distance.

   Alternatively, I can just use `mouse.x` and `mouse.y` as world coordinates by scaling them. But proper world position is better.

   Let's do raycasting:
   ```
   const raycaster = new THREE.Raycaster();
   raycaster.setFromCamera(new THREE.Vector2(mouse.x, mouse.y), camera);
   const intersects = raycaster.intersectObjects(fish, true);
   // then push fish away
   ```
   But raycasting every frame on all fish might be okay for 6 fish.

   Actually, simpler: I'll just compute a target point at distance `mouseDist` along the camera ray, and for each fish, if distance to that point < threshold, apply repulsion vector pointing from that point to fish. That avoids raycasting per fish.

   Let's just use the unproject method. It's straightforward.

   Wait, `camera.unproject` requires a `Vector3` in NDC (x: -1 to 1, y: -1 to 1, z: 0 to 1). I'll set `mouse.z = 1` (far plane). Then unproject gives a world point. That should work.

   Let's test mentally:
   ```
   mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
   mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
   ```
   Then in loop:
   ```
   if (isStarted && mouse.x !== null) {
     const mouseWorld = new THREE.Vector3(mouse.x, mouse.y, 1).unproject(camera);
     // repel
   }
   ```
   This should place the mouse world point somewhere in front of the camera. The fish are within radius ~4, camera at z=10. It should be fine.

   One issue: `unproject` multiplies by inverse projection matrix, so it's a world space point on the far plane. The distance from fish to that point will be meaningful.

   I'll keep it.

   Now, about the "self-playing demo mode": I'll have the fish swim randomly from the moment the scene loads, even before the click. The "Click or press a key to start" message will be visible. On click, the message hides, and mouse avoidance activates. This satisfies "behind a 'Click or press a key to start' message, so the game can be judged in action without playing" - the demo mode (autonomous swimming) is visible before click, and after click, the mouse interaction shows the feature.

   Actually, re-read: "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." This means the initial state should be a self-playing demo, and the message is there so that if someone needs to start it, they can. But the demo should already be playing. So fish should swim from the start. The message just needs to be there, and clicking it starts... what? Maybe the mouse interaction. I'll make it so that before click, fish swim in simple patterns without mouse avoidance. After click, mouse avoidance kicks in. That way, the "demo mode" is the autonomous swimming, and the "start" enables the interactive feature. That seems reasonable.

   Let's code it.

   Final checks on constraints:
   - Single fenced code block with ```html. I'll output exactly that.
   - Inline CSS and JS.
   - Import map exactly as given.
   - No external resources besides CDN in import map.
   - No alert/confirm/prompt/etc.
   - Responsive, no horizontal scroll.
   - First paint meaningful.
   - Zero console errors.

   I'll now produce the HTML code.

   Let's write it carefully.

   One thing: I should use `const` and `let` properly, avoid `var`. Use template literals where appropriate.

   I'll also add `font-family` to the start message so it looks decent.

   I'll set body `background: #0d0d24` or similar dark water color. scene fog matching.

   Let's draft the final code:

   ```html
   <!DOCTYPE html>
   <html lang="en">
   <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>3D Aquarium</title>
     <style>
       * { box-sizing: border-box; margin: 0; padding: 0; }
       body {
         overflow-x: hidden;
         background: #0d0d24;
         font-family: sans-serif;
       }
       #container {
         width: 100vw;
         height: 100vh;
       }
       #start-message {
         position: absolute;
         top: 50%; left: 50%;
         transform: translate(-50%, -50%);
         color: #e0e0e0;
         font-size: 1.8rem;
         pointer-events: auto;
         user-select: none;
       }
     </style>
   </head>
   <body>
     <div id="container"></div>
     <div id="start-message">Click or press a key to start</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("container");
       const scene = new THREE.Scene();
       
       const width = container.clientWidth || window.innerWidth;
       const height = container.clientHeight || window.innerHeight;
       const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 1000);
       camera.position.set(0, 0, 10);
       
       const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
       renderer.setSize(width, height);
       renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
       renderer.setClearColor(0x0d0d24, 1);
       container.appendChild(renderer.domElement);
       
       scene.add(new THREE.AmbientLight(0xffffff, 0.5));
       const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
       dirLight.position.set(5, 5, 5);
       scene.add(dirLight);
       
       scene.fog = new THREE.Fog(0x0d0d24, 5, 20);
       
       // Fish
       const fishCount = 6;
       const fish = [];
       const fishGeometry = new THREE.IcosahedronGeometry(0.25, 0);
       const fishMaterial = new THREE.MeshStandardMaterial({ color: #ff6b6b, flatShading: true });
       
       // ... etc
   ```

   Wait, I need to be careful with hex colors in JS: `#ff6b6b` should be `0xff6b66` or just `0xff6b6b` as number. In Three.js, `new THREE.MeshStandardMaterial({ color: 0xff6b6b })` is fine. I'll use numeric.

   Let's write the complete code mentally and ensure no syntax errors.

   I'll produce the code now.