Models 44

GPT-6.1 Terra

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 7 s
Cost
$0.030
Output tokens
5,954
Spent thinking
0 tokens
Speed
90.8 tok/s
Console errors
0
First token after
1.0 s
First visible text after
1.0 s
Prompt tokens
423
Code size
17.0 KB
Finish reason
stop
Served by
OpenAI
Model id
openai/gpt-6.1-terra
Reasoning sent
{"effort":"none"}
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 · 425 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Stillwater Aquarium</title>
<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>
<style>
  * { box-sizing: border-box; }
  html, body { margin: 0; width: 100%; height: 100%; overflow: hidden; }
  body {
    color: #edf8ef;
    background: #071e1e;
    font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  }
  #scene { position: absolute; inset: 0; width: 100%; height: 100%; }
  #scene canvas { display: block; width: 100%; height: 100%; }
  .vignette {
    position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(180deg, rgba(3,22,22,.66) 0%, transparent 20%, transparent 75%, rgba(3,18,18,.6) 100%),
                linear-gradient(90deg, rgba(4,24,23,.30), transparent 30%, transparent 75%, rgba(4,24,23,.25));
  }
  header {
    position: absolute; top: 32px; left: 42px; right: 42px;
    display: flex; justify-content: space-between; align-items: flex-start;
    pointer-events: none;
  }
  .brand { display: flex; align-items: center; gap: 12px; }
  .brand-icon {
    width: 38px; height: 38px; border: 1px solid rgba(214,239,219,.38);
    border-radius: 50%; display: grid; place-items: center;
    color: #d8ecda;
  }
  .brand-name { font-family: Georgia, serif; font-size: 21px; letter-spacing: -.3px; }
  .brand-sub { margin-top: 3px; color: #a9c5b8; font-size: 9px; letter-spacing: .19em; text-transform: uppercase; }
  .top-right {
    color: #c0d7c8; font-size: 10px; letter-spacing: .17em; text-transform: uppercase;
    display: flex; align-items: center; gap: 9px; padding-top: 12px;
  }
  .dot { width: 6px; height: 6px; background: #b7dbb3; border-radius: 50%; box-shadow: 0 0 9px #a3dfb7; }
  .copy {
    position: absolute; left: 42px; bottom: 100px; max-width: 380px;
    pointer-events: none; text-shadow: 0 2px 20px rgba(0,0,0,.24);
  }
  .eyebrow { color: #b7d5c3; font-size: 10px; letter-spacing: .2em; text-transform: uppercase; margin-bottom: 12px; }
  h1 { font: 400 clamp(39px, 5vw, 64px)/.98 Georgia, serif; letter-spacing: -2px; margin: 0; }
  .description { margin: 16px 0 0; max-width: 290px; color: #c6d9ce; font-size: 13px; line-height: 1.65; }
  .footer {
    position: absolute; bottom: 28px; left: 42px; right: 42px;
    display: flex; justify-content: space-between; align-items: end;
    color: #b5cec0; font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
    pointer-events: none;
  }
  .instruction { display: flex; align-items: center; gap: 10px; }
  .mouse {
    width: 17px; height: 24px; border: 1px solid #b8d1c0; border-radius: 9px;
    position: relative;
  }
  .mouse:after { content: ""; position: absolute; left: 7px; top: 4px; width: 1px; height: 5px; background: #b8d1c0; }
  .number { font-family: Georgia, serif; font-size: 15px; color: #d9e9dc; letter-spacing: 0; margin-right: 6px; }
  .cursor-ring {
    position: absolute; width: 56px; height: 56px; border: 1px solid rgba(221,249,226,.48);
    border-radius: 50%; pointer-events: none; opacity: 0; transform: translate(-50%, -50%);
    transition: opacity .4s; box-shadow: 0 0 20px rgba(162,234,196,.12), inset 0 0 20px rgba(162,234,196,.06);
  }
  .cursor-ring:before, .cursor-ring:after { content: ""; position: absolute; background: rgba(223,249,229,.6); left: 50%; top: 50%; transform: translate(-50%,-50%); }
  .cursor-ring:before { width: 10px; height: 1px; }
  .cursor-ring:after { width: 1px; height: 10px; }
  @media (max-width: 600px) {
    header { left: 22px; right: 22px; top: 23px; }
    .top-right { font-size: 9px; }
    .copy { left: 22px; bottom: 100px; max-width: 260px; }
    h1 { font-size: 45px; }
    .description { font-size: 12px; max-width: 240px; }
    .footer { left: 22px; right: 22px; bottom: 22px; }
    .footer-right { display: none; }
  }
</style>
</head>
<body>
<div id="scene"></div>
<div class="vignette"></div>
<header>
  <div class="brand">
    <div class="brand-icon">
      <svg width="23" height="23" viewBox="0 0 24 24" fill="none" aria-hidden="true">
        <path d="M3 12c2.7-3.7 6.2-5 9.3-4.2 2.4.6 4.1 2.1 5.3 4.2-1.2 2.1-2.9 3.6-5.3 4.2C9.2 17 5.7 15.7 3 12Z" stroke="currentColor" stroke-width="1.1"/>
        <path d="m17.5 12 3.5-3v6l-3.5-3Z" stroke="currentColor" stroke-width="1.1" stroke-linejoin="round"/>
        <circle cx="7.6" cy="11.1" r=".7" fill="currentColor"/>
      </svg>
    </div>
    <div><div class="brand-name">Stillwater</div><div class="brand-sub">A living aquarium</div></div>
  </div>
  <div class="top-right"><span class="dot"></span> Live habitat</div>
</header>
<div class="copy">
  <div class="eyebrow">A little moment of calm</div>
  <h1>Drift a<br>little closer.</h1>
  <p class="description">A quiet world beneath the surface. Take a breath and watch the little ones wander.</p>
</div>
<div class="footer">
  <div class="instruction"><span class="mouse"></span><span>Move your cursor to interact</span></div>
  <div class="footer-right"><span class="number">01</span> / An underwater escape</div>
</div>
<div class="cursor-ring" id="ring"></div>
<script type="module">
import * as THREE from 'three';

const container = document.getElementById('scene');
const ring = document.getElementById('ring');
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x103c3a);
scene.fog = new THREE.FogExp2(0x163e3b, 0.027);

const camera = new THREE.PerspectiveCamera(40, innerWidth / innerHeight, 0.1, 120);
camera.position.set(0, 2.7, 19.5);
camera.lookAt(0, 1.7, 0);

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

scene.add(new THREE.HemisphereLight(0xc5f4d8, 0x183b31, 2.1));
const sun = new THREE.DirectionalLight(0xe5f6d2, 3.1);
sun.position.set(-5, 12, 7);
scene.add(sun);
const fill = new THREE.DirectionalLight(0x6bb9aa, 1.2);
fill.position.set(7, 4, -9);
scene.add(fill);

const rand = (a, b) => a + Math.random() * (b - a);
const mat = (color, roughness = .8, metalness = 0) => new THREE.MeshStandardMaterial({ color, roughness, metalness });

const sand = new THREE.Mesh(new THREE.PlaneGeometry(100, 100), mat(0x8d9b73));
sand.rotation.x = -Math.PI / 2;
sand.position.y = -3.5;
scene.add(sand);

const sandDetail = new THREE.Group();
scene.add(sandDetail);
const pebbleGeo = new THREE.SphereGeometry(1, 8, 6);
const pebbleMats = [mat(0x9da789), mat(0xb8b89a), mat(0x647d69), mat(0x778b72)];
for (let i = 0; i < 220; i++) {
  const p = new THREE.Mesh(pebbleGeo, pebbleMats[i % pebbleMats.length]);
  p.position.set(rand(-16,16), -3.45, rand(-11, 9));
  const s = rand(.025,.11);
  p.scale.set(s * 1.6, s * .38, s);
  sandDetail.add(p);
}

const rockMaterials = [mat(0x617b6d), mat(0x798c79), mat(0x526d61), mat(0x8a9b82)];
function rock(x, z, sx, sy, sz, index) {
  const mesh = new THREE.Mesh(new THREE.IcosahedronGeometry(1, 2), rockMaterials[index % rockMaterials.length]);
  mesh.position.set(x, -3.42 + sy * .35, z);
  mesh.scale.set(sx, sy, sz);
  mesh.rotation.set(rand(-.3,.3), rand(0,6), rand(-.25,.25));
  scene.add(mesh);
}
rock(-7.6,-2.8,2.2,1.0,1.3,0);
rock(-6.2,-3.5,1.5,.72,1.2,1);
rock(-4.5,-5.5,1.8,.9,1.2,2);
rock(7.6,-2.7,2.5,1.05,1.5,2);
rock(5.6,-4,1.4,.7,1.1,1);
rock(9,1,1.7,.72,1.2,0);
rock(1,-7,2,.55,1.3,3);
rock(-10,2,1.9,.8,1.4,2);

function leafMesh(length, width, color) {
  const segments = 9;
  const positions = [], indices = [];
  for (let i = 0; i <= segments; i++) {
    const t = i / segments;
    const w = Math.sin(Math.PI * Math.pow(t, .75)) * width;
    const bend = Math.sin(t * 2.5) * length * .13;
    positions.push(-w, t * length, bend, w, t * length, bend);
  }
  for (let i = 0; i < segments; i++) {
    const n = i * 2;
    indices.push(n,n+1,n+2, n+1,n+3,n+2);
  }
  const g = new THREE.BufferGeometry();
  g.setAttribute('position', new THREE.Float32BufferAttribute(positions,3));
  g.setIndex(indices);
  g.computeVertexNormals();
  return new THREE.Mesh(g, new THREE.MeshStandardMaterial({color, side:THREE.DoubleSide, roughness:.82}));
}
const plants = [];
function plant(x, z, height, count, type = 0) {
  const group = new THREE.Group();
  group.position.set(x, -3.4, z);
  scene.add(group);
  const colors = type ? [0x668c62,0x84a877,0x547b59] : [0x356d50,0x4c8960,0x6b9a6a,0x3e7856];
  for (let i=0; i<count; i++) {
    const len = height * rand(.5,1.15);
    const leaf = leafMesh(len, rand(.10,.23) * (type ? 1.4 : 1), colors[i % colors.length]);
    leaf.rotation.y = rand(0, Math.PI*2);
    leaf.rotation.z = rand(-.55,.55);
    leaf.position.x = rand(-.18,.18);
    leaf.position.z = rand(-.18,.18);
    group.add(leaf);
  }
  plants.push({group, phase:rand(0,6), speed:rand(.45,.9)});
}
plant(-9.3,-4.8,4.8,16);
plant(-7.9,-5.4,3.3,12,1);
plant(-5.7,-6,3.8,14);
plant(-3.9,-7,2.8,11,1);
plant(9.5,-5,5.2,18);
plant(7.8,-5.8,3.5,14,1);
plant(5.8,-6.5,4.2,15);
plant(3.8,-7.3,2.8,12,1);
plant(-10,1.5,2.2,10,1);
plant(10,1.5,2.7,12);
plant(1.9,-9,2.5,10);
plant(-1.4,-9,2,9,1);

const fishBodyGeo = new THREE.SphereGeometry(1, 24, 16);
const eyeGeo = new THREE.SphereGeometry(.065, 12, 8);
const pupilMat = mat(0x102823, .3);
const eyeWhiteMat = mat(0xe5e0c8, .45);
const palettes = [
  [0xeaa66c,0xf3c58f,0xb96349],
  [0xe8b77b,0xf5d6a7,0xc57b5c],
  [0xd7e0bd,0xf0e8c8,0x91ae9b],
  [0xd89073,0xf4b89a,0xa95951],
  [0xd3c79a,0xf3e3bc,0x9aa98a]
];

function finGeometry(points) {
  const shape = new THREE.Shape();
  shape.moveTo(points[0][0],points[0][1]);
  for (let i=1;i<points.length;i++) shape.lineTo(points[i][0],points[i][1]);
  shape.closePath();
  return new THREE.ShapeGeometry(shape);
}
const tailGeo = finGeometry([[0,0],[.52,.36],[.43,.05],[.52,-.36]]);
const dorsalGeo = finGeometry([[-.45,.05],[.02,.41],[.46,.34],[.56,.05]]);
const bottomGeo = finGeometry([[-.1,0],[.34,-.26],[.57,0]]);
const sideFinGeo = finGeometry([[0,0],[.38,-.19],[.3,.1]]);

function makeFish(index) {
  const pal = palettes[index % palettes.length];
  const root = new THREE.Group();
  const model = new THREE.Group();
  root.add(model);
  const bodyMat = new THREE.MeshStandardMaterial({color:pal[0], roughness:.48, metalness:.05});
  const bellyMat = new THREE.MeshStandardMaterial({color:pal[1], roughness:.6});
  const finMat = new THREE.MeshStandardMaterial({color:pal[2], side:THREE.DoubleSide, transparent:true, opacity:.87, roughness:.65});
  const body = new THREE.Mesh(fishBodyGeo, bodyMat);
  body.scale.set(.83,.37,.24);
  model.add(body);
  const belly = new THREE.Mesh(fishBodyGeo, bellyMat);
  belly.scale.set(.62,.19,.225);
  belly.position.set(-.08,-.17,0);
  model.add(belly);
  const tailPivot = new THREE.Group();
  tailPivot.position.x = .68;
  const tail = new THREE.Mesh(tailGeo,finMat);
  tailPivot.add(tail);
  model.add(tailPivot);
  const dorsal = new THREE.Mesh(dorsalGeo,finMat);
  dorsal.position.set(-.05,.24,0);
  model.add(dorsal);
  const bottom = new THREE.Mesh(bottomGeo,finMat);
  bottom.position.set(.05,-.27,0);
  model.add(bottom);
  for (const side of [-1,1]) {
    const white = new THREE.Mesh(eyeGeo,eyeWhiteMat);
    white.position.set(-.52,.10,side*.19);
    white.scale.set(1,1,.45);
    model.add(white);
    const eye = new THREE.Mesh(eyeGeo,pupilMat);
    eye.scale.set(.55,.55,.35);
    eye.position.set(-.54,.105,side*.228);
    model.add(eye);
    const fin = new THREE.Mesh(sideFinGeo,finMat);
    fin.position.set(-.09,-.03,side*.22);
    fin.rotation.y = side * .32;
    model.add(fin);
  }
  const scale = rand(.58,1.05) * (index < 4 ? 1.12 : 1);
  root.scale.setScalar(scale);
  root.position.set(rand(-7,7),rand(-1.6,4.2),rand(-4.5,3));
  scene.add(root);
  const angle = rand(0,Math.PI*2);
  return {
    root, model, tail:tailPivot,
    velocity:new THREE.Vector3(Math.cos(angle),rand(-.12,.12),Math.sin(angle)*.35).normalize().multiplyScalar(rand(.6,1)),
    phase:rand(0,10), speed:rand(.6,1.05), turn:rand(0,10),
    screen:new THREE.Vector3()
  };
}
const fishes = Array.from({length:17}, (_,i) => makeFish(i));

const bubbleMat = new THREE.MeshPhysicalMaterial({
  color:0xc9f8e9, metalness:.1, roughness:.08, transparent:true,
  opacity:.33, transmission:.3, side:THREE.DoubleSide, depthWrite:false
});
const bubbleGeo = new THREE.SphereGeometry(1,12,10);
const bubbles = [];
for (let i=0; i<48; i++) {
  const mesh = new THREE.Mesh(bubbleGeo,bubbleMat);
  const size = rand(.025,.10);
  mesh.scale.setScalar(size);
  mesh.position.set(rand(-8,8),rand(-3.5,8),rand(-6,2));
  scene.add(mesh);
  bubbles.push({mesh, speed:rand(.25,.75), phase:rand(0,6), x:mesh.position.x});
}

const dustCount = 320;
const dustPos = new Float32Array(dustCount*3);
for (let i=0;i<dustCount;i++) {
  dustPos[i*3]=rand(-14,14);
  dustPos[i*3+1]=rand(-3,9);
  dustPos[i*3+2]=rand(-10,5);
}
const dustGeo = new THREE.BufferGeometry();
dustGeo.setAttribute('position',new THREE.BufferAttribute(dustPos,3));
const dust = new THREE.Points(dustGeo,new THREE.PointsMaterial({color:0xc5e5cd,size:.035,transparent:true,opacity:.42,depthWrite:false}));
scene.add(dust);

function lightShaft(x, width, opacity) {
  const geometry = new THREE.PlaneGeometry(width, 20);
  const material = new THREE.MeshBasicMaterial({color:0xc3ebcd,transparent:true,opacity,depthWrite:false,side:THREE.DoubleSide});
  const shaft = new THREE.Mesh(geometry,material);
  shaft.position.set(x,5,-8);
  shaft.rotation.z = -.26;
  scene.add(shaft);
}
lightShaft(-5,1.2,.025);
lightShaft(-2.8,2.2,.018);
lightShaft(4.5,1.6,.023);
lightShaft(7, .8,.025);

const mouse = {x:0,y:0,active:false};
let pointerTimer;
window.addEventListener('pointermove', e => {
  mouse.x = e.clientX / innerWidth * 2 - 1;
  mouse.y = -(e.clientY / innerHeight * 2 - 1);
  mouse.active = true;
  ring.style.left = e.clientX + 'px';
  ring.style.top = e.clientY + 'px';
  ring.style.opacity = '1';
  clearTimeout(pointerTimer);
  pointerTimer = setTimeout(() => { mouse.active = false; ring.style.opacity = '0'; }, 3500);
});
window.addEventListener('pointerleave', () => { mouse.active = false; ring.style.opacity = '0'; });

const clock = new THREE.Clock();
const targetVelocity = new THREE.Vector3();
const escape = new THREE.Vector3();
const temp = new THREE.Vector3();
function animate() {
  requestAnimationFrame(animate);
  const dt = Math.min(clock.getDelta(),.04);
  const time = clock.elapsedTime;
  plants.forEach(p => {
    p.group.rotation.z = Math.sin(time * p.speed + p.phase) * .035;
    p.group.rotation.x = Math.sin(time * p.speed * .7 + p.phase) * .025;
  });
  bubbles.forEach(b => {
    b.mesh.position.y += b.speed * dt;
    b.mesh.position.x = b.x + Math.sin(time * .8 + b.phase) * .12;
    if (b.mesh.position.y > 8) b.mesh.position.y = -3.4;
  });
  dust.rotation.y = Math.sin(time*.04)*.025;
  fishes.forEach((f,i) => {
    const p = f.root.position;
    f.turn += dt * .3;
    targetVelocity.set(
      Math.sin(f.turn + f.phase) * .13,
      Math.sin(time * .45 + f.phase) * .12,
      Math.cos(f.turn * .7 + f.phase) * .07
    );
    if (p.x > 9) targetVelocity.x -= (p.x - 9) * .8;
    if (p.x < -9) targetVelocity.x += (-9 - p.x) * .8;
    if (p.y > 5.1) targetVelocity.y -= (p.y - 5.1) * .8;
    if (p.y < -2.5) targetVelocity.y += (-2.5 - p.y) * .8;
    if (p.z > 4) targetVelocity.z -= (p.z - 4) * .6;
    if (p.z < -6) targetVelocity.z += (-6 - p.z) * .6;
    if (mouse.active) {
      f.screen.copy(p).project(camera);
      const dx = (f.screen.x - mouse.x) * (innerWidth / innerHeight);
      const dy = f.screen.y - mouse.y;
      const distance = Math.sqrt(dx*dx+dy*dy);
      if (distance < .34) {
        const force = (1 - distance / .34) * 4.5;
        escape.set(dx,dy,0);
        if (escape.lengthSq() < .0001) escape.set(1,.3,0);
        escape.normalize();
        targetVelocity.addScaledVector(escape,force);
      }
    }
    f.velocity.addScaledVector(targetVelocity,dt);
    const speed = f.velocity.length();
    const maxSpeed = mouse.active ? 2.4 : 1.2;
    if (speed > maxSpeed) f.velocity.multiplyScalar(maxSpeed/speed);
    if (speed < f.speed*.5) f.velocity.multiplyScalar((f.speed*.5)/Math.max(speed,.001));
    p.addScaledVector(f.velocity,dt);
    const desired = Math.atan2(f.velocity.z, -f.velocity.x);
    let diff = desired - f.root.rotation.y;
    diff = Math.atan2(Math.sin(diff),Math.cos(diff));
    f.root.rotation.y += diff * Math.min(1,dt*2.5);
    f.model.rotation.z = THREE.MathUtils.lerp(f.model.rotation.z, -f.velocity.y*.18, dt*2);
    f.tail.rotation.y = Math.sin(time*9 + f.phase) * (.24 + Math.min(speed,2)*.12);
    f.model.position.y = Math.sin(time*2 + f.phase)*.025;
  });
  renderer.render(scene,camera);
}
window.addEventListener('resize', () => {
  camera.aspect = innerWidth/innerHeight;
  camera.position.z = innerWidth < 600 ? 24 : 19.5;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth,innerHeight);
  renderer.setPixelRatio(Math.min(devicePixelRatio,2));
});
if (innerWidth < 600) { camera.position.z = 24; camera.lookAt(0,1.7,0); }
animate();
</script>
</body>
</html>