Interactive 3D aquariumOne-line
Claude Haiku 4.5
by AnthropicReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 11.4 s
- Cost
- $0.018
- Output tokens
- 3,416
- Spent thinking
- 0 tokens
- Speed
- 317.8 tok/s
- Console errors
- 0
- First token after
- 667 ms
- First visible text after
- 667 ms
- Prompt tokens
- 462
- Code size
- 11.1 KB
- Finish reason
- stop
- Served by
- Amazon Bedrock
- Model id
anthropic/claude-haiku-4.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 · 342 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>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
width: 100%;
height: 100vh;
overflow: hidden;
font-family: Arial, sans-serif;
background: #1a1a1a;
}
#canvas {
display: block;
width: 100%;
height: 100%;
}
#info {
position: absolute;
top: 20px;
left: 20px;
color: #fff;
font-size: 14px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
pointer-events: none;
}
#info p {
margin: 5px 0;
}
</style>
</head>
<body>
<div id="info">
<p>🐠 3D Aquarium</p>
<p>Move your mouse to scare the fish</p>
</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';
let scene, camera, renderer;
let fish = [];
const mousePos = new THREE.Vector2(0, 0);
const TANK_SIZE = 100;
const FISH_COUNT = 12;
const SCARE_DISTANCE = 25;
function init() {
// Scene setup
scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a3d62);
scene.fog = new THREE.Fog(0x0a3d62, 150, 300);
// Camera setup
camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 0, 60);
// Renderer setup
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(50, 50, 50);
directionalLight.castShadow = true;
scene.add(directionalLight);
const pointLight = new THREE.PointLight(0x00aaff, 0.5);
pointLight.position.set(-30, 30, 30);
scene.add(pointLight);
// Create tank
createTank();
// Create fish
for (let i = 0; i < FISH_COUNT; i++) {
createFish();
}
// Event listeners
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('resize', onWindowResize);
// Start animation loop
animate();
}
function createTank() {
const size = TANK_SIZE;
const thickness = 2;
// Tank walls material
const wallMaterial = new THREE.MeshStandardMaterial({
color: 0x1a5f7a,
metalness: 0.3,
roughness: 0.4,
transparent: true,
opacity: 0.3,
});
// Front and back walls
const frontBack = new THREE.BoxGeometry(size, size, thickness);
const frontWall = new THREE.Mesh(frontBack, wallMaterial);
frontWall.position.z = size / 2;
scene.add(frontWall);
const backWall = new THREE.Mesh(frontBack, wallMaterial);
backWall.position.z = -size / 2;
scene.add(backWall);
// Left and right walls
const leftRight = new THREE.BoxGeometry(thickness, size, size);
const leftWall = new THREE.Mesh(leftRight, wallMaterial);
leftWall.position.x = -size / 2;
scene.add(leftWall);
const rightWall = new THREE.Mesh(leftRight, wallMaterial);
rightWall.position.x = size / 2;
scene.add(rightWall);
// Bottom
const bottom = new THREE.BoxGeometry(size, thickness, size);
const bottomMesh = new THREE.Mesh(bottom, wallMaterial);
bottomMesh.position.y = -size / 2;
scene.add(bottomMesh);
// Add bubbles for atmosphere
for (let i = 0; i < 30; i++) {
const bubbleGeom = new THREE.SphereGeometry(
Math.random() * 0.8 + 0.3,
8,
8
);
const bubbleMat = new THREE.MeshStandardMaterial({
color: 0x4dd0e1,
metalness: 0.8,
roughness: 0.2,
emissive: 0x1a7f8a,
});
const bubble = new THREE.Mesh(bubbleGeom, bubbleMat);
bubble.position.set(
(Math.random() - 0.5) * size * 1.5,
(Math.random() - 0.5) * size * 1.5,
(Math.random() - 0.5) * size * 1.5
);
bubble.userData.floatSpeed = Math.random() * 0.3 + 0.1;
bubble.userData.floatAmount = Math.random() * 2 + 1;
scene.add(bubble);
}
}
function createFish() {
const fishGroup = new THREE.Group();
// Fish body
const bodyGeom = new THREE.ConeGeometry(1.5, 4, 8);
const bodyMat = new THREE.MeshStandardMaterial({
color: new THREE.Color().setHSL(Math.random(), 0.7, 0.5),
metalness: 0.4,
roughness: 0.5,
emissive: new THREE.Color().setHSL(Math.random(), 0.5, 0.2),
});
const body = new THREE.Mesh(bodyGeom, bodyMat);
body.castShadow = true;
body.receiveShadow = true;
fishGroup.add(body);
// Fish tail
const tailGeom = new THREE.ConeGeometry(1.2, 1.5, 6);
const tailMat = new THREE.MeshStandardMaterial({
color: bodyMat.color,
metalness: 0.3,
roughness: 0.6,
});
const tail = new THREE.Mesh(tailGeom, tailMat);
tail.position.x = -2.5;
tail.rotation.z = Math.random() * 0.5;
tail.castShadow = true;
fishGroup.add(tail);
// Fish eye
const eyeGeom = new THREE.SphereGeometry(0.3, 8, 8);
const eyeMat = new THREE.MeshStandardMaterial({
color: 0xffffff,
emissive: 0xffff00,
});
const eye = new THREE.Mesh(eyeGeom, eyeMat);
eye.position.set(1.8, 0.4, 1);
fishGroup.add(eye);
// Randomize position
fishGroup.position.set(
(Math.random() - 0.5) * TANK_SIZE * 0.8,
(Math.random() - 0.5) * TANK_SIZE * 0.8,
(Math.random() - 0.5) * TANK_SIZE * 0.8
);
scene.add(fishGroup);
fish.push({
group: fishGroup,
velocity: new THREE.Vector3(
(Math.random() - 0.5) * 0.3,
(Math.random() - 0.5) * 0.2,
(Math.random() - 0.5) * 0.3
),
targetVelocity: new THREE.Vector3(),
scared: false,
scareTimer: 0,
tailWave: 0,
});
}
function onMouseMove(event) {
mousePos.x = (event.clientX / window.innerWidth) * 2 - 1;
mousePos.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function updateFish(f, deltaTime) {
const mouseWorldPos = new THREE.Vector3(
mousePos.x * 40,
mousePos.y * 30,
30
);
const distance = f.group.position.distanceTo(mouseWorldPos);
if (distance < SCARE_DISTANCE) {
f.scared = true;
f.scareTimer = 2;
const escapeDir = new THREE.Vector3()
.subVectors(f.group.position, mouseWorldPos)
.normalize();
f.targetVelocity.copy(escapeDir).multiplyScalar(0.5);
} else {
f.scareTimer -= deltaTime;
if (f.scareTimer <= 0) {
f.scared = false;
}
}
if (!f.scared) {
if (Math.random() < 0.01) {
f.targetVelocity.set(
(Math.random() - 0.5) * 0.2,
(Math.random() - 0.5) * 0.15,
(Math.random() - 0.5) * 0.2
);
}
}
f.velocity.lerp(f.targetVelocity, 0.05);
f.group.position.add(f.velocity);
// Boundary wrapping
const boundary = TANK_SIZE / 2 - 2;
if (f.group.position.x > boundary) f.group.position.x = -boundary;
if (f.group.position.x < -boundary) f.group.position.x = boundary;
if (f.group.position.y > boundary) f.group.position.y = -boundary;
if (f.group.position.y < -boundary) f.group.position.y = boundary;
if (f.group.position.z > boundary) f.group.position.z = -boundary;
if (f.group.position.z < -boundary) f.group.position.z = boundary;
// Face direction of movement
if (f.velocity.length() > 0.01) {
const targetRotation = Math.atan2(f.velocity.y, f.velocity.x);
f.group.rotation.z = targetRotation - Math.PI / 2;
f.group.rotation.y = Math.atan2(f.velocity.z, f.velocity.x);
}
// Tail animation
f.tailWave += 0.1;
if (f.group.children[1]) {
f.group.children[1].rotation.z = Math.sin(f.tailWave) * 0.3;
}
}
function animate() {
requestAnimationFrame(animate);
const deltaTime = 1 / 60;
fish.forEach((f) => updateFish(f, deltaTime));
// Animate bubbles
scene.children.forEach((child) => {
if (child.userData.floatSpeed) {
child.position.y += child.userData.floatSpeed;
child.position.x += Math.sin(Date.now() * 0.001) * 0.05;
if (child.position.y > TANK_SIZE / 2) {
child.position.y = -TANK_SIZE / 2;
}
}
});
renderer.render(scene, camera);
}
init();
</script>
</body>
</html>