Interactive 3D aquariumOne-line
GPT-5 Nano
by OpenAIReasoning2 console errors
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 20.2 s
- Cost
- $0.0013
- Output tokens
- 3,205
- Spent thinking
- 0 tokens
- Speed
- 165.4 tok/s
- Console errors
- 2
- First token after
- 864 ms
- First visible text after
- 864 ms
- Prompt tokens
- 423
- Code size
- 11.4 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-5-nano- Reasoning sent
{"effort":"minimal"}- Max output
- 128,000 tokens
- Generated
- Nov 12, 2026
Console errors
Caught in the first 3 seconds after load, in a sandboxed iframe like the one above.
Failed to load resource: the server responded with a status of 404 () Failed to load resource: the server responded with a status of 404 ()
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 · 324 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>3D Aquarium - Fish Swim Away</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
:root {
--bg: #0b1020;
--panel: rgba(10, 20, 40, 0.7);
--text: #e9f0ff;
--accent: #66e0ff;
}
html, body {
margin: 0;
padding: 0;
height: 100%;
background: linear-gradient(#0a0f1a, #0b1020);
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial;
color: var(--text);
overflow: hidden;
}
.container {
position: relative;
width: 100%;
height: 100vh;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
/* Overlay */
.overlay {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background: linear-gradient(135deg, rgba(10,20,40,0.95), rgba(10,20,40,0.75));
border: 1px solid rgba(100,180,255,0.6);
border-radius: 12px;
padding: 18px 22px;
text-align: center;
max-width: 90vw;
box-shadow: 0 8px 30px rgba(0,0,0,0.4);
z-index: 2;
pointer-events: auto;
}
.overlay h1 {
margin: 0 0 6px 0;
font-size: 18px;
letter-spacing: .4px;
}
.overlay p {
margin: 0;
font-size: 13px;
color: #cbdcff;
}
.start {
margin-top: 12px;
display: inline-block;
padding: 8px 14px;
border-radius: 8px;
background: linear-gradient(135deg, #2f6eff, #1b4bdc);
color: white;
font-weight: bold;
cursor: pointer;
user-select: none;
border: none;
}
@media (max-width: 700px) {
.overlay { padding: 14px; }
.overlay h1 { font-size: 16px; }
.overlay p { font-size: 12px; }
}
</style>
<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>
<div class="container" id="scene-container" aria-label="3D aquarium scene">
<div class="overlay" id="start-overlay" role="dialog" aria-label="Start instructions">
<h1>3D Aquarium</h1>
<p>Move your mouse near the fish to make them swim away. Click or press any key to start the simulation.</p>
<button class="start" id="start-btn">Click or press a key to start</button>
</div>
<!-- The canvas will be created by Three.js -->
</div>
<script type="module">
// Import Three.js modules
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/GLTFLoader.js';
import { OrbitControls } from 'three/addons/OrbitControls.js';
// We'll implement a simple AI-like steering without full physics for robustness.
// Scene setup
const container = document.getElementById('scene-container');
const overlay = document.getElementById('start-overlay');
const startBtn = document.getElementById('start-btn');
let started = false;
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a1a2a);
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 4, 12);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
renderer.setSize(window.innerWidth, window.innerHeight, false);
renderer.setPixelRatio(window.devicePixelRatio);
container.appendChild(renderer.domElement);
// Simple underwater ambient light
const ambient = new THREE.AmbientLight(0x88aaff, 0.6);
scene.add(ambient);
// Directional light to create shading
const dir = new THREE.DirectionalLight(0xffffff, 0.4);
dir.position.set(5, 10, 8);
scene.add(dir);
// Water plane (soft gradient)
const waterGeo = new THREE.PlaneGeometry(60, 40);
const waterMat = new THREE.MeshPhongMaterial({ color: 0x1b2b57, side: THREE.DoubleSide, transparent: true, opacity: 0.25 });
const water = new THREE.Mesh(waterGeo, waterMat);
water.rotation.x = -Math.PI / 2;
water.position.y = -0.5;
scene.add(water);
// Aquarium boundary walls (very light)
const wallGeo = new THREE.BoxGeometry(60, 16, 1);
const wallMat = new THREE.MeshBasicMaterial({ color: 0x1b2b57, transparent: true, opacity: 0.25 });
const backWall = new THREE.Mesh(wallGeo, wallMat);
backWall.position.set(0, 4, -20);
scene.add(backWall);
// Fish factory: create several simple fish as animated colored spheres with tails
const fishCount = 14;
const fishList = [];
// Simple fish geometry: sphere body with small tail (cone) per fish
function createFish(i) {
// Body
const bodyGeo = new THREE.SphereGeometry(0.75, 12, 8);
const bodyMat = new THREE.MeshStandardMaterial({ color: new THREE.Color(`hsl(${(i * 26) % 360}, 80%, 60%)`), flatShading: true });
const body = new THREE.Mesh(bodyGeo, bodyMat);
body.castShadow = false;
body.receiveShadow = false;
// Tail
const tailGeo = new THREE.ConeGeometry(0.5, 1.2, 8);
tailGeo.rotateZ(Math.PI / 2);
const tailMat = new THREE.MeshStandardMaterial({ color: bodyMat.color });
const tail = new THREE.Mesh(tailGeo, tailMat);
tail.position.x = -0.95;
tail.rotation.z = Math.PI;
// Group
const group = new THREE.Group();
group.add(body);
group.add(tail);
// Initial position
group.position.set((i - fishCount/2) * 3.0 + (Math.random() - 0.5), 0.8 + Math.random(), (Math.random() - 0.5) * 18);
// Velocity vector
group.userData = {
velocity: new THREE.Vector3((Math.random() - 0.5) * 0.2, 0, (Math.random() - 0.5) * 0.7),
speed: 0.08 + Math.random() * 0.08,
targetDir: new THREE.Vector3(0, 0, -1)
};
// Add to scene
scene.add(group);
return group;
}
for (let i = 0; i < fishCount; i++) {
fishList.push(createFish(i));
}
// Mouse interaction: compute world position of mouse ray and influence nearby fish by steering away
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2(9999, 9999); // offscreen
function updateMousePosition(e) {
const rect = renderer.domElement.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
const y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
mouse.set(x, y);
}
// The iframe specs require a click to start keyboard/mouse events – we'll enforce overlay until started
function onStart() {
if (started) return;
started = true;
overlay.style.display = 'none';
// Small initial calibration: spawn a gentle drift
fishList.forEach((f, idx) => {
f.userData.velocity.applyAxisAngle(new THREE.Vector3(0,1,0), (Math.random()-0.5)*0.2);
});
// Allow keyboard focus for potential accessibility
renderer.domElement.focus({ preventDefault: true });
}
window.addEventListener('keydown', (e) => {
if (!started) onStart();
}, { passive: true });
startBtn.addEventListener('click', onStart);
renderer.domElement.addEventListener('mousemove', updateMousePosition, { passive: true });
// Resize handling
function onResize() {
const w = window.innerWidth;
const h = window.innerHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize(w, h, false);
}
window.addEventListener('resize', onResize, { passive: true });
onResize();
// Simple AI: fish steer away from a virtual predator around mouse position in world space
function steerAwayFromPoint(fish, pointWorld, dt) {
// compute vector from point to fish
const toFish = new THREE.Vector3().subVectors(fish.position, pointWorld);
const dist = toFish.length();
const influenceRadius = 6.0;
if (dist < influenceRadius && dist > 0.0001) {
const away = toFish.normalize();
const strength = (influenceRadius - dist) / influenceRadius; // 0..1
// Apply velocity change
fish.userData.velocity.addScaledVector(away, strength * 0.6);
// Cap speed
const maxSpeed = 0.25;
if (fish.userData.velocity.length() > maxSpeed) {
fish.userData.velocity.normalize().multiplyScalar(maxSpeed);
}
// Orient body roughly away from center
const targetDir = fish.userData.velocity.clone().normalize();
const currentDir = new THREE.Vector3(1,0,0).applyQuaternion(fish.quaternion);
// Lerp orientation toward velocity
const axis = new THREE.Vector3().crossVectors(currentDir, targetDir).normalize();
const angle = Math.acos(THREE.MathUtils.clamp(currentDir.dot(targetDir), -1, 1)) * 0.25;
if (!Number.isNaN(angle) && axis.lengthSq()) {
const q = new THREE.Quaternion().setFromAxisAngle(axis, angle);
fish.quaternion.multiply(q);
}
}
}
// Gentle ground clamp
function clampPosition(p) {
if (p.x < -28) p.x = -28;
if (p.x > 28) p.x = 28;
if (p.y < 0.3) p.y = 0.3;
if (p.y > 6) p.y = 6;
if (p.z < -18) p.z = -18;
if (p.z > 18) p.z = 18;
}
// Animation loop
let lastTime = performance.now();
function animate(now) {
requestAnimationFrame(animate);
const dt = Math.min(0.05, (now - lastTime) / 1000);
lastTime = now;
// Update controls (optional gentle orbit for visual appeal if started)
// We'll implement automatic subtle wandering around center
// Mouse-based avoidance
// Convert mouse normalized device coordinates to a world point roughly in front of camera
let avoidPoint = new THREE.Vector3(0, 0, -5);
if (renderer && renderer.domElement && started) {
// Unproject mouse
const ndc = new THREE.Vector3(mouse.x, mouse.y, 0.5);
ndc.unproject(camera);
const dir = ndc.sub(camera.position).normalize();
const dist = 8;
avoidPoint = camera.position.clone().add(dir.multiplyScalar(dist));
}
// Update each fish
for (let i = 0; i < fishList.length; i++) {
const f = fishList[i];
// Steering away from the avoid point
if (started) steerAwayFromPoint(f, avoidPoint, dt);
// Move
f.position.addScaledVector(f.userData.velocity, dt * 60 * 0.01);
// Gentle boundary wrapping to keep fish within view
clampPosition(f.position);
// Add a tiny sine-based vertical bob for liveliness
f.position.y += Math.sin(now * 0.001 + i) * 0.0008;
// Ensure body orientation faces movement direction (approx)
if (f.userData.velocity.lengthSq() > 0.0001) {
const dir = f.userData.velocity.clone().normalize();
const quat = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(1,0,0), dir);
f.quaternion.slerp(quat, 0.1);
}
}
// Minor camera drift for depth
camera.position.x = Math.sin(now * 0.00015) * 1.2;
camera.position.z = 12 + Math.cos(now * 0.00018) * 2;
camera.lookAt(0, 1.5, 0);
renderer.render(scene, camera);
}
// Start animation
requestAnimationFrame(animate);
// Ensure the canvas can receive focus to capture keyboard after click
renderer.domElement.setAttribute('tabindex', '0');
renderer.domElement.addEventListener('click', () => {
if (!started) onStart();
}, { passive: true });
</script>
</body>
</html>