Models 44

GPT-5 Nano

by OpenAIReasoningClean
Open in new window

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

How it went

Total time
20.9 s
Cost
$0.0013
Output tokens
3,188
Spent thinking
320 tokens
Speed
170.3 tok/s
Console errors
0
First token after
2.2 s
First visible text after
5.7 s
Prompt tokens
423
Code size
9.8 KB
Finish reason
stop
Served by
OpenAI
Model id
openai/gpt-5-nano
Reasoning sent
{"effort":"low"}
Max output
128,000 tokens
Generated
Nov 12, 2026

What it was asked

The prompt
“a 3D aquarium with Three.js where the fish swim away when I move the mouse near them (Load Three.js only through this import map: <script type="importmap">{"imports":{"three":"https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js","three/addons/":"https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/"}}</script>)”
System prompt, sent with every prompt
You are generating a self-contained web demo. It will be rendered automatically in a sandboxed iframe and compared side by side with the output of other AI models given the exact same task.

Output rules:
- Reply with exactly ONE fenced code block containing the complete file: ```html (or ```svg when the task asks for an SVG). No text before or after the code block.
- Everything must be inline: CSS in <style>, JavaScript in <script>. No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task.
- Do not use localStorage, sessionStorage, cookies, IndexedDB, alert(), confirm(), prompt() or window.open(): they are blocked in the sandbox and will throw.
- The page must look right in a 1280×800 viewport and adapt gracefully down to 375px wide. No horizontal scrollbar.
- Something meaningful must be visible on first paint: a screenshot is taken automatically about 3 seconds after load, without any interaction.
- Everything that makes the demo interesting must be visible within the first seconds: nothing hidden behind a high score, a later level or a long play session.
- Games must start in a self-playing demo mode (a simple AI plays) behind a "Click or press a key to start" message, so the game can be judged in action without playing.
- The iframe only receives keyboard events after the user clicks it: if the demo uses the keyboard, display a clear "Click or press a key to start" message.
- The code must run with zero errors in the console of the latest Chrome.

Code run-1.html · 307 lines

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>3D Aquarium - Fish swim away from mouse</title>
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <style>
    :root {
      --bg: #0a1020;
      --water: #1b6ea8;
      --overlay: rgba(0,0,0,0.6);
      --text: #e9f0ff;
    }
    html, body {
      margin: 0;
      height: 100%;
      background: var(--bg);
      overflow: hidden;
      font-family: system-ui, -apple-system, "Segoe UI", Roboto;
    }
    #stage {
      width: 100%;
      height: 100%;
      display: block;
    }
    /* Overlay hint shown before start */
    #startup {
      position: fixed;
      top: 0; left: 0; right: 0; bottom: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      background: var(--overlay);
      color: white;
      font-size: 1.2rem;
      text-align: center;
      padding: 20px;
      z-index: 10;
      user-select: none;
    }
    #startup h1 { margin: 0 0 8px 0; font-size: 1.6rem; }
    @media (max-width: 700px) {
      #startup { font-size: 1rem; padding: 16px; }
      #startup h1 { font-size: 1.25rem; }
    }
  </style>
  <!-- Import map for Three.js as requested -->
  <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>
<canvas id="stage"></canvas>
<div id="startup" aria-label="Start instructions">
  <div>
    <h1>3D Aquarium</h1>
    <div>Move your mouse near the fish to scare them away. Click or press any key to start.</div>
  </div>
</div>

<script type="module">
  // Scene setup
  import * as THREE from 'three';
  // Basic helpers from addons to keep file self-contained
  // We'll use OrbitControls but we won't rely on user interaction; instead, we implement our own mouse-to-world mapping.
  // (Importing OrbitControls isn't strictly necessary; omit to keep self-contained.)
  // If you want to test with controls, you can uncomment the next line.
  // import { OrbitControls } from 'three/addons/orbitcontrols.js';

  // Core globals
  let scene, camera, renderer;
  let fishCount = 60;
  let fish = [];
  let waterPlane;
  let mouse = new THREE.Vector2(); // in NDC space
  let pointerWorld = new THREE.Vector3(); // projected point on water plane
  let raycaster = new THREE.Raycaster();
  let isRunning = false;
  let lastTime = 0;

  const container = document.getElementById('stage');
  const startup = document.getElementById('startup');

  function init() {
    // Renderer
    renderer = new THREE.WebGLRenderer({ canvas: container, antialias: true, alpha: false });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.outputColorSpace = THREE.SRGBColorSpace;

    // Scene
    scene = new THREE.Scene();
    scene.fog = new THREE.FogExp2(0x000a1a, 0.015);

    // Camera
    const aspect = window.innerWidth / window.innerHeight;
    camera = new THREE.PerspectiveCamera(60, aspect, 0.1, 200);
    camera.position.set(0, 8, 22);
    camera.lookAt(0, 0, 0);

    // Lights
    const ambient = new THREE.AmbientLight(0x88aaff, 0.6);
    scene.add(ambient);
    const dir = new THREE.DirectionalLight(0xffffff, 0.8);
    dir.position.set(5, 15, 10);
    scene.add(dir);

    // Water / floor plane
    const waterGeometry = new THREE.PlaneGeometry(60, 40);
    const waterMaterial = new THREE.MeshPhongMaterial({ color: 0x1b6ea8, side: THREE.DoubleSide, shininess: 100, transparent: true, opacity: 0.9 });
    waterPlane = new THREE.Mesh(waterGeometry, waterMaterial);
    waterPlane.rotation.x = -Math.PI / 2;
    waterPlane.position.y = -0.5;
    scene.add(waterPlane);

    // Simple seabed/floor grid for depth cue
    const grid = new THREE.GridHelper(60, 20, 0x114466, 0x0a1a2a);
    grid.rotation.x = -Math.PI / 2;
    grid.position.y = -0.51;
    scene.add(grid);

    // Create fish
    const fishGroup = new THREE.Group();
    for (let i = 0; i < fishCount; i++) {
      const f = createFish();
      fishGroup.add(f.mesh);
      fish.push(f);
    }
    scene.add(fishGroup);

    // Handle resizing
    window.addEventListener('resize', onWindowResize, { passive: true });

    // Mouse events for start/interaction
    window.addEventListener('pointermove', onPointerMove, { passive: true });
    window.addEventListener('keydown', onAnyKey, { passive: true });
    // Start hidden until user interacts
    // Start loop
    requestAnimationFrame(animate);
  }

  function createFish() {
    // Simple elongated cone/yellow-orange fish
    const body = new THREE.ConeGeometry(0.6, 2, 8);
    // The cone points up by default; rotate to horizontal
    const material = new THREE.MeshPhongMaterial({ color: 0xffa500, flatShading: true });
    const mesh = new THREE.Mesh(body, material);
    mesh.geometry.computeBoundingBox();
    mesh.castShadow = true;
    mesh.receiveShadow = true;

    // Slightly rotate to point forward
    mesh.rotation.z = Math.PI; // flip to face -Z
    // Random initial position within bounds
    const x = THREE.MathUtils.randFloatSpread(40);
    const y = THREE.MathUtils.randFloat(-2, 6);
    const z = THREE.MathUtils.randFloatSpread(40);
    mesh.position.set(x, y, z);

    // Velocity & personal behavior
    const f = {
      mesh: mesh,
      vel: new THREE.Vector3(
        THREE.MathUtils.randFloat(-0.07, 0.07),
        THREE.MathUtils.randFloat(-0.02, 0.02),
        THREE.MathUtils.randFloat(-0.07, 0.07)
      ),
      accelBounds: new THREE.Vector3(0.15, 0.15, 0.15),
      maxSpeed: 0.25,
      wanderAngle: THREE.MathUtils.randFloat(0, Math.PI * 2)
    };
    return f;
  }

  function onWindowResize() {
    const w = window.innerWidth;
    const h = window.innerHeight;
    camera.aspect = w / h;
    camera.updateProjectionMatrix();
    renderer.setSize(w, h);
  }

  function onPointerMove(event) {
    // convert to normalized device coordinates (-1, 1)
    const rect = renderer.domElement.getBoundingClientRect();
    mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
    mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
    // For ongoing update of world point we keep it in animate loop
  }

  function onAnyKey() {
    if (!isRunning) {
      isRunning = true;
      startup.style.display = 'none';
    }
  }

  function updateMouseWorldPoint() {
    // Cast ray from camera through mouse into scene, intersect with water plane (y = -0.5)
    raycaster.setFromCamera(new THREE.Vector2(mouse.x, mouse.y), camera);
    const planeY = -0.5;
    // Plane equation: y = planeY
    // Compute intersection with the horizontal plane
    const dir = raycaster.ray.direction;
    const camPos = raycaster.ray.origin;
    const t = (planeY - camPos.y) / dir.y;
    if (t > 0) {
      pointerWorld.copy(camPos).addScaledVector(dir, t);
    } else {
      // fallback to a point in front
      pointerWorld.set(0, planeY, 0);
    }
  }

  function animate(now) {
    requestAnimationFrame(animate);
    if (!lastTime) lastTime = now;
    const dt = Math.min(0.05, (now - lastTime) / 1000);
    lastTime = now;

    // Update pointer projectively
    updateMouseWorldPoint();

    // If not started, a subtle idle drift could be shown, but keep minimal
    if (isRunning) {
      moveFish(dt);
    }

    // Simple camera motion for life-like feeling (gentle)
    camera.position.x += Math.sin(now * 0.0002) * 0.001;
    camera.position.z += Math.cos(now * 0.0002) * 0.001;
    camera.lookAt(0, 0, 0);

    renderer.render(scene, camera);
  }

  function moveFish(dt) {
    const influenceRadius = 6.0;
    const repelStrength = 1.2;
    const avoidanceRadius = 1.8;
    for (let i = 0; i < fish.length; i++) {
      const f = fish[i];
      // Gentle wandering
      const wand = Math.sin((i + performance.now() * 0.001) * 1.2) * 0.08;
      f.vel.x += wand * dt;
      f.vel.z += wand * dt;

      // React to mouse pointer projection (pointerWorld is on water plane)
      const toPointer = new THREE.Vector3().subVectors(f.mesh.position, pointerWorld);
      const dist = toPointer.length();
      if (dist < influenceRadius) {
        // Move away from pointer (repulsion)
        toPointer.normalize();
        const force = repelStrength * (1 - dist / influenceRadius);
        f.vel.x += toPointer.x * force * dt * 2;
        f.vel.z += toPointer.z * force * dt * 2;
      }

      // Keep inside bounds (rough aquarium box)
      const maxX = 25, maxZ = 25, minX = -25, minZ = -25;
      if (f.mesh.position.x < minX) f.vel.x = Math.abs(f.vel.x);
      if (f.mesh.position.x > maxX) f.vel.x = -Math.abs(f.vel.x);
      if (f.mesh.position.z < minZ) f.vel.z = Math.abs(f.vel.z);
      if (f.mesh.position.z > maxZ) f.vel.z = -Math.abs(f.vel.z);

      // Cap speed
      if (f.vel.length() > f.maxSpeed) {
        f.vel.normalize().multiplyScalar(f.maxSpeed);
      }

      // Update position
      f.mesh.position.addScaledVector(f.vel, dt * 60); // scale to feel fast enough

      // Slight bobbing up/down for realism
      f.mesh.position.y += Math.sin(nowHint() + i * 0.5) * 0.002;

      // Optional slight tilt to show motion
      f.mesh.rotation.y = Math.atan2(-f.vel.x, -f.vel.z);

      // Quick reorient if stuck near floor
      if (f.mesh.position.y < -0.2) f.mesh.position.y = -0.2;
      if (f.mesh.position.y > 2.5) f.mesh.position.y = 2.5;
    }
  }

  // simple time-based function for bobbing
  const t0 = performance.now();
  function nowHint() {
    return (performance.now() - t0) * 0.001;
  }

  // Initialize
  init();

  // Accessibility: ensure startup overlay shown until user interacts
  // The overlay already exists and will be hidden on first keydown or click
  // Add click anywhere on the stage to start if user clicks directly on canvas
  container.addEventListener('click', () => { if (!isRunning) { isRunning = true; startup.style.display = 'none'; } }, { passive: true });

</script>
</body>
</html>
Reasoning trace (320 tokens)
**Creating a 3D aquarium**

I'm thinking about how to create a single HTML file that implements a 3D aquarium using Three.js. The goal is for fish to swim away from the mouse when it gets close. I'll start by loading Three.js via importmap and set up a self-playing demo controlled by AI for the fish's movement. I need to begin with a message like "Click or press a key to start." The canvas should adapt to both desktop and mobile dimensions while creating the fish with simple shapes. Mouse tracking will be vital too!**Mapping mouse interaction in the aquarium**

I'm figuring out how to set the aquarium size and map the 2D mouse position to a 3D point within the scene using a raycaster. The idea is to create an invisible plane at the water surface (y=0) to calculate where the mouse intersects. If fish get too close to this point, they should swim away from it. Plus, each fish should swim randomly as well. I plan to initialize with 60 fish, pausing until a click starts the animation without prompts or alerts. I'll need to craft the HTML with inline JS for a responsive canvas!