Models 44

Claude Haiku 5.5

by AnthropicReasoningClean
Open in new window

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

How it went

Total time
25.2 s
Cost
$0.0037
Output tokens
7,184
Spent thinking
0 tokens
Speed
293.7 tok/s
Console errors
0
First token after
723 ms
First visible text after
723 ms
Prompt tokens
648
Code size
13.6 KB
Finish reason
stop
Served by
Claude Platform on AWS
Model id
anthropic/claude-haiku-5.5
Reasoning sent
{"effort":"none"}
Max output
32,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 · 420 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>
  html, body {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
    overflow: hidden;
    background: #03131f;
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  }
  #canvas-wrap {
    position: fixed;
    inset: 0;
  }
  canvas { display: block; }
  #hud {
    position: fixed;
    left: 16px;
    bottom: 14px;
    color: rgba(200, 240, 255, 0.85);
    font-size: 13px;
    letter-spacing: 0.02em;
    pointer-events: none;
    text-shadow: 0 1px 4px rgba(0,0,0,0.6);
    max-width: calc(100vw - 32px);
  }
  #title {
    position: fixed;
    top: 16px;
    left: 0;
    right: 0;
    text-align: center;
    color: #d9f6ff;
    font-size: 20px;
    font-weight: 600;
    pointer-events: none;
    text-shadow: 0 2px 8px rgba(0,0,0,0.6);
  }
  #hint {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: #e8fbff;
    background: rgba(0, 30, 50, 0.55);
    padding: 12px 20px;
    border-radius: 10px;
    font-size: 16px;
    pointer-events: none;
    border: 1px solid rgba(160, 230, 255, 0.25);
    transition: opacity 0.6s ease;
    white-space: nowrap;
  }
  @media (max-width: 480px) {
    #title { font-size: 16px; top: 10px; }
    #hud { font-size: 11px; left: 10px; bottom: 8px; }
    #hint { font-size: 13px; padding: 10px 14px; }
  }
</style>
</head>
<body>
<div id="canvas-wrap"></div>
<div id="title">3D Aquarium</div>
<div id="hint">Move your mouse near the fish and watch them scatter</div>
<div id="hud">Move the mouse · Fish swim away from the cursor</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';

// ---------- Renderer / scene / camera ----------
const wrap = document.getElementById('canvas-wrap');
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.outputColorSpace = THREE.SRGBColorSpace;
wrap.appendChild(renderer.domElement);

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x03131f);
scene.fog = new THREE.FogExp2(0x03313f, 0.035);

const camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 200);
camera.position.set(0, 1.5, 22);
camera.lookAt(0, 0, 0);

// ---------- Lights ----------
scene.add(new THREE.HemisphereLight(0x9fe8ff, 0x0a2a3a, 0.9));
const sun = new THREE.DirectionalLight(0xbff0ff, 1.1);
sun.position.set(6, 14, 8);
scene.add(sun);
const caustic = new THREE.PointLight(0x6fd8ff, 25, 60, 1.5);
caustic.position.set(0, 10, 0);
scene.add(caustic);

// ---------- Tank: sand floor, rocks, bubbles, light rays ----------
const floorGeo = new THREE.PlaneGeometry(80, 40, 40, 20);
{
  // gentle undulation on the sand
  const pos = floorGeo.attributes.position;
  for (let i = 0; i < pos.count; i++) {
    const x = pos.getX(i), y = pos.getY(i);
    const h = Math.sin(x * 0.4) * 0.3 + Math.cos(y * 0.5) * 0.25 + Math.random() * 0.08;
    pos.setZ(i, h);
  }
  floorGeo.computeVertexNormals();
}
const sand = new THREE.Mesh(
  floorGeo,
  new THREE.MeshStandardMaterial({ color: 0xc9b58a, roughness: 1 })
);
sand.rotation.x = -Math.PI / 2;
sand.position.y = -7;
scene.add(sand);

// Rocks
const rockMat = new THREE.MeshStandardMaterial({ color: 0x4a5a63, roughness: 0.95, flatShading: true });
for (let i = 0; i < 14; i++) {
  const r = 0.8 + Math.random() * 1.6;
  const rock = new THREE.Mesh(new THREE.DodecahedronGeometry(r, 0), rockMat);
  const a = Math.random() * Math.PI * 2;
  const d = 6 + Math.random() * 14;
  rock.position.set(Math.cos(a) * d, -7 + r * 0.3, Math.sin(a) * d * 0.4 - 3);
  rock.rotation.set(Math.random() * 3, Math.random() * 3, Math.random() * 3);
  rock.scale.y = 0.6 + Math.random() * 0.4;
  scene.add(rock);
}

// Seaweed
const weeds = [];
const weedMat = new THREE.MeshStandardMaterial({ color: 0x2f9e6a, roughness: 0.6, side: THREE.DoubleSide });
for (let i = 0; i < 22; i++) {
  const h = 2.5 + Math.random() * 4;
  const geo = new THREE.PlaneGeometry(0.35, h, 1, 8);
  geo.translate(0, h / 2, 0);
  const weed = new THREE.Mesh(geo, weedMat);
  const a = Math.random() * Math.PI * 2;
  const d = 3 + Math.random() * 16;
  weed.position.set(Math.cos(a) * d, -7, Math.sin(a) * d * 0.35 - 4);
  weed.rotation.y = Math.random() * Math.PI;
  weed.userData.phase = Math.random() * 10;
  weed.userData.base = geo.attributes.position.array.slice();
  scene.add(weed);
  weeds.push(weed);
}

// Bubbles
const bubbles = [];
const bubbleGeo = new THREE.SphereGeometry(0.12, 10, 8);
const bubbleMat = new THREE.MeshStandardMaterial({
  color: 0xdff8ff, transparent: true, opacity: 0.45, roughness: 0.1, metalness: 0.1
});
for (let i = 0; i < 40; i++) {
  const b = new THREE.Mesh(bubbleGeo, bubbleMat);
  resetBubble(b, true);
  scene.add(b);
  bubbles.push(b);
}
function resetBubble(b, initial = false) {
  b.position.set(
    (Math.random() - 0.5) * 30,
    initial ? -7 + Math.random() * 14 : -7.5,
    (Math.random() - 0.5) * 10 - 2
  );
  b.userData.speed = 0.6 + Math.random() * 0.8;
  b.userData.wobble = Math.random() * 10;
  b.scale.setScalar(0.5 + Math.random() * 1.2);
}

// Light rays from the surface
const rays = [];
const rayMat = new THREE.MeshBasicMaterial({
  color: 0xaef0ff, transparent: true, opacity: 0.06, depthWrite: false, side: THREE.DoubleSide
});
for (let i = 0; i < 8; i++) {
  const ray = new THREE.Mesh(new THREE.PlaneGeometry(2.5, 26), rayMat);
  ray.position.set(-14 + i * 4 + (Math.random() - 0.5) * 2, 2, -8 - Math.random() * 4);
  ray.rotation.z = -0.25 + Math.random() * 0.1;
  ray.userData.phase = Math.random() * 10;
  scene.add(ray);
  rays.push(ray);
}

// ---------- Fish ----------
function makeFish(color, accent) {
  const group = new THREE.Group();
  const bodyMat = new THREE.MeshStandardMaterial({ color, roughness: 0.4, metalness: 0.1 });
  const finMat = new THREE.MeshStandardMaterial({ color: accent, roughness: 0.5, transparent: true, opacity: 0.85, side: THREE.DoubleSide });
  const eyeMat = new THREE.MeshStandardMaterial({ color: 0x111111 });

  const body = new THREE.Mesh(new THREE.SphereGeometry(1, 20, 14), bodyMat);
  body.scale.set(1.5, 0.75, 0.55);
  group.add(body);

  const tail = new THREE.Mesh(new THREE.ConeGeometry(0.7, 1.1, 4), finMat);
  tail.rotation.z = -Math.PI / 2;
  tail.position.x = -1.7;
  tail.scale.set(1, 1, 0.25);
  group.add(tail);
  group.userData.tail = tail;

  const dorsal = new THREE.Mesh(new THREE.ConeGeometry(0.4, 0.8, 3), finMat);
  dorsal.position.set(0, 0.8, 0);
  dorsal.scale.z = 0.2;
  group.add(dorsal);

  const fin = new THREE.Mesh(new THREE.ConeGeometry(0.3, 0.7, 3), finMat);
  fin.rotation.z = Math.PI / 2.5;
  fin.position.set(0.1, -0.3, 0.4);
  fin.scale.z = 0.2;
  group.add(fin);

  for (const side of [-1, 1]) {
    const eye = new THREE.Mesh(new THREE.SphereGeometry(0.14, 8, 6), eyeMat);
    eye.position.set(0.9, 0.2, side * 0.42);
    group.add(eye);
  }
  return group;
}

const palettes = [
  [0xff7a2f, 0xffd08a], [0x3aa8ff, 0xb8e6ff], [0xffd23f, 0xfff3b0],
  [0xff4d6d, 0xffb3c1], [0x9b5de5, 0xd9c2ff], [0x2ec4b6, 0xa8f0e8],
  [0xf15bb5, 0xffc2e5], [0xffffff, 0xffd08a]
];

const fishes = [];
const FISH_COUNT = 26;
for (let i = 0; i < FISH_COUNT; i++) {
  const [c, a] = palettes[i % palettes.length];
  const f = makeFish(c, a);
  const s = 0.55 + Math.random() * 0.55;
  f.scale.setScalar(s);
  f.position.set(
    (Math.random() - 0.5) * 30,
    (Math.random() - 0.5) * 12,
    (Math.random() - 0.5) * 8
  );
  f.userData = {
    vel: new THREE.Vector3((Math.random() - 0.5) * 1.5, (Math.random() - 0.5) * 0.4, (Math.random() - 0.5) * 0.6),
    home: new THREE.Vector3((Math.random() - 0.5) * 22, (Math.random() - 0.5) * 9, (Math.random() - 0.5) * 6),
    phase: Math.random() * Math.PI * 2,
    size: s,
    maxSpeed: 2.2 + Math.random() * 1.2,
    fear: 0,
  };
  scene.add(f);
  fishes.push(f);
}

// ---------- Mouse -> 3D point projected onto a plane at z=0 ----------
const mouseNDC = new THREE.Vector2(9999, 9999);
const mouseWorld = new THREE.Vector3(9999, 9999, 0);
const raycaster = new THREE.Raycaster();
const zPlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
let mouseActive = false;
let mouseIdleTimer = 0;

function onPointer(x, y) {
  mouseNDC.set((x / window.innerWidth) * 2 - 1, -(y / window.innerHeight) * 2 + 1);
  mouseActive = true;
  mouseIdleTimer = 0;
}
window.addEventListener('pointermove', e => onPointer(e.clientX, e.clientY));
window.addEventListener('pointerdown', e => onPointer(e.clientX, e.clientY));
window.addEventListener('pointerleave', () => { mouseActive = false; });
window.addEventListener('touchmove', e => {
  if (e.touches[0]) onPointer(e.touches[0].clientX, e.touches[0].clientY);
}, { passive: true });

function updateMouseWorld() {
  if (!mouseActive) return;
  raycaster.setFromCamera(mouseNDC, camera);
  const hit = new THREE.Vector3();
  if (raycaster.ray.intersectPlane(zPlane, hit)) {
    mouseWorld.copy(hit);
  }
}

// Ambient "demo" cursor used when the mouse is idle, so the effect is visible on first paint
const demoCursor = new THREE.Vector3();
function updateDemoCursor(t) {
  demoCursor.set(Math.sin(t * 0.35) * 14, Math.sin(t * 0.5) * 4, 0);
}

// ---------- Resize ----------
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
  // narrow screens: pull the camera back so the tank fits
  camera.position.z = window.innerWidth < 600 ? 30 : 22;
});
if (window.innerWidth < 600) camera.position.z = 30;

// ---------- Simulation ----------
const tmp = new THREE.Vector3();
const flee = new THREE.Vector3();
const FEAR_RADIUS = 6;
const BOUNDS = { x: 15, y: 5.5, z: 3.5 };

const clock = new THREE.Clock();
const hint = document.getElementById('hint');
let hintHidden = false;

function animate() {
  const dt = Math.min(clock.getDelta(), 0.05);
  const t = clock.elapsedTime;

  // Hide the hint once the user interacts
  if (!hintHidden && mouseActive) {
    hint.style.opacity = '0';
    hintHidden = true;
  }

  mouseIdleTimer += dt;
  if (mouseIdleTimer > 4) mouseActive = false;
  updateMouseWorld();
  const cursor = mouseActive ? mouseWorld : demoCursor;
  if (!mouseActive) updateDemoCursor(t);

  for (const f of fishes) {
    const d = f.userData;

    // Fear: distance from cursor (in the fish's own plane) drives a flee vector
    tmp.copy(f.position).sub(cursor);
    tmp.z *= 0.5; // cursor is effectively a 2D probe, reduce depth influence
    const dist = tmp.length();
    const fearTarget = THREE.MathUtils.clamp(1 - dist / FEAR_RADIUS, 0, 1);
    d.fear += (fearTarget - d.fear) * Math.min(1, dt * 6);

    if (d.fear > 0.01) {
      flee.copy(tmp).normalize().multiplyScalar(d.fear * 14);
      d.vel.addScaledVector(flee, dt);
    }

    // Gentle wander toward home point
    tmp.copy(d.home).sub(f.position);
    d.vel.addScaledVector(tmp, dt * 0.05);

    // Wobbling swimming noise
    d.vel.x += Math.sin(t * 0.7 + d.phase) * dt * 0.4;
    d.vel.y += Math.cos(t * 0.9 + d.phase * 1.3) * dt * 0.25;
    d.vel.z += Math.sin(t * 0.5 + d.phase * 0.7) * dt * 0.2;

    // Speed limit (faster when scared)
    const maxS = d.maxSpeed * (1 + d.fear * 1.5);
    const sp = d.vel.length();
    if (sp > maxS) d.vel.multiplyScalar(maxS / sp);
    if (sp < 0.6) d.vel.multiplyScalar(1.02);

    f.position.addScaledVector(d.vel, dt);

    // Soft bounds: turn around smoothly near walls
    if (Math.abs(f.position.x) > BOUNDS.x) d.vel.x -= Math.sign(f.position.x) * dt * 8;
    if (f.position.y > BOUNDS.y || f.position.y < -BOUNDS.y) d.vel.y -= Math.sign(f.position.y) * dt * 6;
    if (Math.abs(f.position.z) > BOUNDS.z) d.vel.z -= Math.sign(f.position.z) * dt * 4;

    // Orient along velocity
    if (d.vel.lengthSq() > 0.0001) {
      const heading = Math.atan2(d.vel.z, d.vel.x);
      const pitch = Math.atan2(d.vel.y, Math.hypot(d.vel.x, d.vel.z));
      // body faces +x; rotate around y for heading, z for pitch
      f.rotation.set(0, -heading, 0);
      f.rotation.z = THREE.MathUtils.lerp(f.rotation.z, pitch * 0.6, 0.15);
    }
    // Wiggle tail
    const wiggle = Math.sin(t * (8 + d.vel.length() * 2) + d.phase) * 0.35;
    d.tail && (d.tail.rotation.y = wiggle);
    f.children[1].rotation.y = wiggle; // tail mesh
    f.children[1].rotation.z = -Math.PI / 2;
  }

  // Bubbles
  for (const b of bubbles) {
    b.position.y += b.userData.speed * dt;
    b.position.x += Math.sin(t * 2 + b.userData.wobble) * dt * 0.3;
    if (b.position.y > 9) resetBubble(b);
  }

  // Seaweed sway
  for (const w of weeds) {
    const arr = w.geometry.attributes.position.array;
    const base = w.userData.base;
    const h = w.geometry.parameters.height;
    for (let i = 0; i < arr.length; i += 3) {
      const yNorm = base[i + 1] / h;
      arr[i] = base[i] + Math.sin(t * 1.2 + w.userData.phase + yNorm * 2) * yNorm * 0.6;
    }
    w.geometry.attributes.position.needsUpdate = true;
  }

  // Light rays shimmer
  for (const r of rays) {
    r.material.opacity = 0.04 + Math.sin(t * 0.6 + r.userData.phase) * 0.025;
  }

  // Caustic light drifting
  caustic.position.x = Math.sin(t * 0.3) * 6;
  caustic.intensity = 22 + Math.sin(t * 1.3) * 5;

  // Subtle camera drift for life
  camera.position.x = Math.sin(t * 0.1) * 1.2;
  camera.position.y = 1.5 + Math.sin(t * 0.13) * 0.4;
  camera.lookAt(0, 0, 0);

  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>