Interactive 3D aquariumOne-line
Qwen3 Coder Next
by Qwenreasoning defaultClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 47.8 s
- Cost
- $0.0038
- Output tokens
- 4,726
- Spent thinking
- 0 tokens
- Speed
- 99.7 tok/s
- Console errors
- 0
- First token after
- 431 ms
- First visible text after
- 431 ms
- Prompt tokens
- 433
- Code size
- 19.6 KB
- Finish reason
- stop
- Served by
- Parasail
- Model id
qwen/qwen3-coder-next- Reasoning sent
model default- Max output
- 235,929 tokens
- Generated
- Nov 13, 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 · 530 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive 3D Aquarium</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #001e2d;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
user-select: none;
}
#canvas-container {
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
#ui-layer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
pointer-events: none; /* Let clicks pass through to canvas when hidden */
transition: opacity 0.5s;
}
#start-message {
background: rgba(0, 30, 45, 0.8);
color: #fff;
padding: 2rem;
border-radius: 1rem;
text-align: center;
border: 2px solid #00a8cc;
box-shadow: 0 0 20px rgba(0, 168, 204, 0.5);
pointer-events: auto;
transition: opacity 0.5s;
}
h1 {
margin-top: 0;
color: #00a8cc;
text-transform: uppercase;
letter-spacing: 3px;
font-size: 2.5rem;
}
p {
font-size: 1.2rem;
line-height: 1.5;
}
.hidden {
opacity: 0;
pointer-events: none !important;
}
#stats {
position: absolute;
bottom: 20px;
left: 20px;
color: rgba(255, 255, 255, 0.6);
font-size: 0.9rem;
z-index: 20;
pointer-events: none;
}
</style>
</head>
<body>
<div id="canvas-container"></div>
<div id="ui-layer">
<div id="start-message">
<h1>Interactive Aquarium</h1>
<p>Welcome to the deep blue!</p>
<p>Move your mouse near the fish to scare them away.</p>
<p style="margin-top: 20px; color: #00a8cc; font-weight: bold;">
Click or Press any key to Start
</p>
</div>
</div>
<div id="stats">Fish Count: <span id="fish-count">0</span></div>
<!-- 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>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
// --- Configuration ---
const FISH_COUNT = 30;
const BUBBLE_COUNT = 100;
const SCARE_RADIUS = 15;
const SCARE_FORCE = 1.5;
const RETURN_FORCE = 0.02;
const MOUSE_INFLUENCE_RADIUS = 10;
// --- Scene Setup ---
const container = document.getElementById('canvas-container');
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x001e2d);
scene.fog = new THREE.FogExp2(0x001e2d, 0.015);
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 10, 30);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
// Lighting
const ambientLight = new THREE.AmbientLight(0x404040, 1.5);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 2);
dirLight.position.set(10, 20, 10);
dirLight.castShadow = true;
scene.add(dirLight);
const backLight = new THREE.PointLight(0x00a8cc, 1, 50);
backLight.position.set(-10, 10, -10);
scene.add(backLight);
// Environment
function createAquariumEnvironment() {
// Water Volume (Visual only)
const geometry = new THREE.BoxGeometry(40, 20, 40);
const material = new THREE.MeshPhysicalMaterial({
color: 0x00a8cc,
transparent: true,
opacity: 0.15,
roughness: 0.1,
metalness: 0.1,
side: THREE.BackSide,
transmission: 0.8
});
const tank = new THREE.Mesh(geometry, material);
tank.position.y = 0;
scene.add(tank);
// Sand Base
const sandGeo = new THREE.PlaneGeometry(40, 40);
const sandMat = new THREE.MeshStandardMaterial({
color: 0xd2b48c,
roughness: 1,
side: THREE.DoubleSide
});
const sand = new THREE.Mesh(sandGeo, sandMat);
sand.rotation.x = -Math.PI / 2;
sand.position.y = -10;
sand.receiveShadow = true;
scene.add(sand);
// Decor: Rocks and Coral
function createRock(x, z) {
const geo = new THREE.DodecahedronGeometry(Math.random() * 2 + 1, 0);
const mat = new THREE.MeshStandardMaterial({ color: 0x808080, roughness: 0.9 });
const rock = new THREE.Mesh(geo, mat);
rock.position.set(x, -9 + Math.random(), z);
rock.rotation.set(Math.random(), Math.random(), Math.random());
rock.scale.set(1, Math.random() * 0.5 + 0.5, 1);
scene.add(rock);
}
for (let i = 0; i < 15; i++) {
const angle = Math.random() * Math.PI * 2;
const radius = 15 + Math.random() * 10;
createRock(Math.cos(angle) * radius, Math.sin(angle) * radius);
}
// Plants
function createPlant(x, z) {
const group = new THREE.Group();
const stemMat = new THREE.MeshStandardMaterial({ color: 0x2e8b57 });
const stemGeo = new THREE.CylinderGeometry(0.1, 0.2, 8, 5);
const stem = new THREE.Mesh(stemGeo, stemMat);
stem.position.y = 4;
stem.castShadow = true;
group.add(stem);
// Leaves
const leafGeo = new THREE.ConeGeometry(0.5, 2, 4);
for(let i=0; i<5; i++){
const leaf = new THREE.Mesh(leafGeo, stemMat);
leaf.position.y = 6 - (i * 1.2);
leaf.rotation.z = Math.random();
leaf.rotation.x = Math.random();
leaf.position.x = (Math.random() - 0.5) * 1.5;
group.add(leaf);
}
group.position.set(x, -9, z);
scene.add(group);
}
for (let i = 0; i < 20; i++) {
const angle = Math.random() * Math.PI * 2;
const radius = 18 + Math.random() * 8;
createPlant(Math.cos(angle) * radius, Math.sin(angle) * radius);
}
}
createAquariumEnvironment();
// --- Fish Class ---
class Fish {
constructor() {
this.group = new THREE.Group();
// Body
const bodyGeo = new THREE.ConeGeometry(0.8, 3, 8);
bodyGeo.rotateX(Math.PI / 2);
const color = new THREE.Color().setHSL(Math.random() * 0.1 + 0.0, 0.8, 0.5); // Orange, Yellow, Red hues
const bodyMat = new THREE.MeshStandardMaterial({ color: color, roughness: 0.3, metalness: 0.1 });
this.body = new THREE.Mesh(bodyGeo, bodyMat);
this.group.add(this.body);
// Tail
const tailGeo = new THREE.ConeGeometry(0.5, 2, 4);
tailGeo.rotateX(Math.PI / 2);
this.tail = new THREE.Mesh(tailGeo, bodyMat);
this.tail.position.z = 1.5;
this.group.add(this.tail);
// Eye
const eyeGeo = new THREE.SphereGeometry(0.15, 8, 8);
const eyeMat = new THREE.MeshBasicMaterial({ color: 0x000000 });
this.leftEye = new THREE.Mesh(eyeGeo, eyeMat);
this.leftEye.position.set(1.2, 0.3, 0.4);
this.group.add(this.leftEye);
this.rightEye = new THREE.Mesh(eyeGeo, eyeMat);
this.rightEye.position.set(1.2, -0.3, 0.4);
this.group.add(this.rightEye);
// Fins
const finGeo = new THREE.BufferGeometry();
const finVertices = new Float32Array([
0, 0, -0.5, 1, 0, 0, 0, 0, 0.5,
0, 0, -0.5, 0, 0.5, 0, 0, 0, 0.5
]);
finGeo.setAttribute('position', new THREE.BufferAttribute(finVertices, 3));
const finMat = new THREE.MeshStandardMaterial({ color: color, side: THREE.DoubleSide });
this.fin = new THREE.Mesh(finGeo, finMat);
this.fin.position.set(0, 0.5, 0);
this.group.add(this.fin);
// Random Initial Properties
this.position = new THREE.Vector3(
(Math.random() - 0.5) * 30,
(Math.random() - 0.5) * 15 + 5, // Keep mostly in middle area
(Math.random() - 0.5) * 30
);
this.velocity = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).normalize().multiplyScalar(0.3);
this.targetVelocity = this.velocity.clone();
this.speed = 0.3 + Math.random() * 0.2;
this.wiggleSpeed = 0.1 + Math.random() * 0.2;
this.wiggleOffset = Math.random() * 100;
scene.add(this.group);
}
update(mousePos, time) {
// 1. Calculate Scare Force
const distToMouse = this.position.distanceTo(mousePos);
const scareForce = new THREE.Vector3();
if (distToMouse < MOUSE_INFLUENCE_RADIUS) {
// Vector from mouse to fish
const pushDirection = new THREE.Vector3().subVectors(this.position, mousePos).normalize();
const scareIntensity = (MOUSE_INFLUENCE_RADIUS - distToMouse) / MOUSE_INFLUENCE_RADIUS;
scareForce.copy(pushDirection).multiplyScalar(scareIntensity * SCARE_FORCE);
// Fish panics: increases speed
this.currentSpeed = this.speed * 2.5;
} else {
// Return to normal speed and natural movement
this.currentSpeed = this.speed;
// Natural wandering
const wanderForce = new THREE.Vector3(
Math.sin(time * 0.002 + this.wiggleOffset) * 0.05,
Math.cos(time * 0.003 + this.wiggleOffset) * 0.05,
Math.sin(time * 0.001 + this.wiggleOffset) * 0.05
);
this.targetVelocity.add(wanderForce).normalize();
}
// 2. Apply Physics
// Interpolate velocity towards (target + scare)
if (distToMouse >= MOUSE_INFLUENCE_RADIUS) {
this.velocity.lerp(this.targetVelocity, RETURN_FORCE);
} else {
this.velocity.lerp(scareForce, 0.1);
}
// Update Position
this.position.add(this.velocity.clone().multiplyScalar(this.currentSpeed));
// 3. Boundary Constraints (Soft Walls)
const bounds = { x: 18, y: 8, z: 18 };
const margin = 2;
if (this.position.x > bounds.x - margin) this.velocity.x -= 0.1;
if (this.position.x < -bounds.x + margin) this.velocity.x += 0.1;
if (this.position.y > bounds.y - margin) this.velocity.y -= 0.1;
if (this.position.y < -bounds.y + margin) this.velocity.y += 0.1;
if (this.position.z > bounds.z - margin) this.velocity.z -= 0.1;
if (this.position.z < -bounds.z + margin) this.velocity.z += 0.1;
// Keep velocity normalized to current speed
this.velocity.normalize().multiplyScalar(this.currentSpeed);
// 4. Update Mesh
this.group.position.copy(this.position);
// Rotate to face direction
const lookTarget = this.position.clone().add(this.velocity);
this.group.lookAt(lookTarget);
// Tail Wiggle Animation
const wiggle = Math.sin(time * 0.02 + this.wiggleOffset) * 0.5;
if (distToMouse < MOUSE_INFLUENCE_RADIUS) {
// Fast tail wiggle when scared
this.tail.rotation.y = Math.sin(time * 0.05) * 0.8;
} else {
this.tail.rotation.y = wiggle;
}
}
}
// --- Bubble Class ---
class Bubble {
constructor() {
const geo = new THREE.SphereGeometry(Math.random() * 0.5 + 0.1, 8, 8);
const mat = new THREE.MeshPhysicalMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.4,
transmission: 0.8,
roughness: 0
});
this.mesh = new THREE.Mesh(geo, mat);
this.reset();
scene.add(this.mesh);
}
reset() {
this.mesh.position.set(
(Math.random() - 0.5) * 35,
-10 + Math.random() * 20,
(Math.random() - 0.5) * 35
);
this.speed = 0.05 + Math.random() * 0.1;
this.size = this.mesh.scale.x;
}
update() {
this.mesh.position.y += this.speed;
// Gentle wave motion
this.mesh.position.x += Math.sin(this.mesh.position.y * 0.2 + Date.now() * 0.001) * 0.02;
if (this.mesh.position.y > 10) {
this.reset();
}
}
}
// --- Initialization ---
const fish = [];
for (let i = 0; i < FISH_COUNT; i++) {
fish.push(new Fish());
}
const bubbles = [];
for (let i = 0; i < BUBBLE_COUNT; i++) {
bubbles.push(new Bubble());
}
// Mouse Tracking
const mouse = new THREE.Vector3(1000, 1000, 1000); // Start off screen
const raycaster = new THREE.Raycaster();
const mousePlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); // Plane at z=0 for simplicity
// Handle Mouse Move
document.addEventListener('mousemove', (event) => {
// Convert screen coordinates to normalized device coordinates
const mouseX = (event.clientX / window.innerWidth) * 2 - 1;
const mouseY = -(event.clientY / window.innerHeight) * 2 + 1;
// Raycast to find intersection with a virtual plane in the aquarium
raycaster.setFromCamera({ x: mouseX, y: mouseY }, camera);
// Create a plane that faces the camera roughly at the center of the tank
const targetPlane = new THREE.Plane();
targetPlane.setFromNormalAndCoplanarPoint(
camera.getWorldDirection(new THREE.Vector3()),
new THREE.Vector3(0, 0, 0)
);
const intersectionPoint = new THREE.Vector3();
raycaster.ray.intersectPlane(targetPlane, intersectionPoint);
if (intersectionPoint) {
// Clamp the mouse interaction area to the tank
mouse.x = THREE.MathUtils.clamp(intersectionPoint.x, -20, 20);
mouse.y = THREE.MathUtils.clamp(intersectionPoint.y, -10, 10);
mouse.z = THREE.MathUtils.clamp(intersectionPoint.z, -20, 20);
}
});
// --- Animation & Logic ---
let isRunning = false;
let startTime = 0;
const startMessage = document.getElementById('start-message');
const uiLayer = document.getElementById('ui-layer');
function startDemo() {
if (isRunning) return;
isRunning = true;
startTime = performance.now();
// Hide UI
startMessage.classList.add('hidden');
setTimeout(() => {
uiLayer.classList.add('hidden');
}, 500);
}
// Start triggers
document.addEventListener('click', startDemo);
document.addEventListener('keydown', startDemo);
// Initial Demo Mode (Auto Swim)
let demoTime = 0;
function animate() {
requestAnimationFrame(animate);
const time = performance.now();
if (isRunning) {
// Update Fish
fish.forEach(f => f.update(mouse, time));
// Update Bubbles
bubbles.forEach(b => b.update());
// Slowly move camera for better view if not controlled
if (controls && !controls.enableDamping) {
camera.position.x = Math.sin(time * 0.0001) * 35;
camera.position.z = Math.cos(time * 0.0001) * 35;
camera.lookAt(0, 0, 0);
}
} else {
// Demo Mode Animation
demoTime += 0.01;
// Move fish in wide circles
fish.forEach((f, i) => {
const angle = (time * 0.0005) + (i * (Math.PI * 2 / FISH_COUNT));
const radius = 15 + (i % 3) * 2;
f.position.x = Math.cos(angle) * radius;
f.position.z = Math.sin(angle) * radius;
f.position.y = Math.sin(angle * 0.5) * 5;
f.group.position.copy(f.position);
f.group.lookAt(0, 0, 0); // Look at center
// Tail wiggle
f.tail.rotation.y = Math.sin(time * 0.01 + i) * 0.5;
});
}
renderer.render(scene, camera);
}
// Controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 15;
controls.maxDistance = 60;
controls.target.set(0, 0, 0);
// Resize Handler
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Start Loop
animate();
// Update Stats
document.getElementById('fish-count').innerText = FISH_COUNT;
</script>
</body>
</html>