Interactive 3D aquariumOne-line
Dots3-Note Preview (free)
by Dots StudioReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 23.4 s
- Cost
- $0
- Output tokens
- 2,303
- Spent thinking
- 0 tokens
- Speed
- 101.8 tok/s
- Console errors
- 0
- First token after
- 725 ms
- First visible text after
- 725 ms
- Prompt tokens
- 430
- Code size
- 7.3 KB
- Finish reason
- stop
- Served by
- AtlasCloud
- Model id
dots-studio/dots-3-note-preview:free- Reasoning sent
{"effort":"none"}- Max output
- 32,000 tokens
- Generated
- Nov 9, 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 · 266 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 – Fish Flee from Mouse</title>
<style>
html, body { margin: 0; padding: 0; overflow: hidden; background: #001018; }
#info {
position: absolute; top: 12px; left: 12px; color: #cfe8ff;
font: 14px/1.4 system-ui, sans-serif; pointer-events: none;
text-shadow: 0 1px 2px #000;
}
#start {
position: absolute; inset: 0; display: flex; align-items: center;
justify-content: center; color: #eaf6ff; font: 20px system-ui, sans-serif;
background: rgba(0,0,0,0.35); cursor: pointer; z-index: 10;
}
</style>
</head>
<body>
<div id="info">Move the mouse near the fish — they will swim away.</div>
<div id="start">Click or press a key to start</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';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// ---------- Scene, Camera, Renderer ----------
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x001018);
scene.fog = new THREE.Fog(0x001018, 18, 45);
const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 200);
camera.position.set(0, 6, 14);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.setSize(innerWidth, innerHeight);
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.1;
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.06;
controls.target.set(0, 0, 0);
controls.minDistance = 6;
controls.maxDistance = 30;
controls.maxPolarAngle = Math.PI * 0.52; // keep above ground
// ---------- Lighting ----------
scene.add(new THREE.AmbientLight(0x406080, 0.6));
const sun = new THREE.DirectionalLight(0xbfe3ff, 1.1);
sun.position.set(8, 12, 6);
scene.add(sun);
const fill = new THREE.DirectionalLight(0x406080, 0.5);
fill.position.set(-8, 4, -6);
scene.add(fill);
// ---------- Ground / Water floor ----------
const ground = new THREE.Mesh(
new THREE.PlaneGeometry(60, 60),
new THREE.MeshStandardMaterial({ color: 0x0a1a2a, roughness: 1, metalness: 0 })
);
ground.rotation.x = -Math.PI / 2;
ground.position.y = -4;
scene.add(ground);
// ---------- Water surface (subtle ripple plane) ----------
const water = new THREE.Mesh(
new THREE.PlaneGeometry(60, 60),
new THREE.MeshStandardMaterial({
color: 0x103050,
transparent: true,
opacity: 0.35,
roughness: 0.05,
metalness: 0.2,
side: THREE.DoubleSide
})
);
water.rotation.x = -Math.PI / 2;
water.position.y = 0;
scene.add(water);
// ---------- Mouse ray ----------
const mouse = new THREE.Vector2(0.5, 0.5); // normalized device coords
const raycaster = new THREE.Raycaster();
const mouse3D = new THREE.Vector3(); // world position projected to y=0 plane
const mousePlane = new THREE.Plane(new THREE.Vector3(0,1,0), 0);
const mouseDir = new THREE.Vector3();
function updateMouse3D() {
raycaster.setFromCamera(mouse, camera);
raycaster.ray.intersectPlane(mousePlane, mouse3D);
}
// ---------- Fish factory ----------
function makeFish() {
const group = new THREE.Group();
// Body
const body = new THREE.Mesh(
new THREE.SphereGeometry(0.35, 16, 12),
new THREE.MeshStandardMaterial({ color: 0xffcc88, roughness: 0.4, metalness: 0.1 })
);
body.scale.set(1, 0.7, 1.6);
group.add(body);
// Tail
const tail = new THREE.Mesh(
new THREE.ConeGeometry(0.25, 0.6, 12),
new THREE.MeshStandardMaterial({ color: 0xffaa66, roughness: 0.5 })
);
tail.rotation.x = Math.PI / 2;
tail.position.z = -0.7;
group.add(tail);
// Fin
const fin = new THREE.Mesh(
new THREE.SphereGeometry(0.18, 10, 8),
new THREE.MeshStandardMaterial({ color: 0xffbb88, roughness: 0.5, transparent: true, opacity: 0.9 })
);
fin.position.y = 0.25;
fin.scale.set(0.6, 0.4, 0.4);
group.add(fin);
// Eye
const eye = new THREE.Mesh(
new THREE.SphereGeometry(0.05, 8, 6),
new THREE.MeshStandardMaterial({ color: 0x111, roughness: 0.2 })
);
eye.position.z = 0.5;
eye.position.y = 0.1;
group.add(eye);
// Store references for animation
group.userData = {
tail,
speed: 0.6 + Math.random() * 0.4,
dir: new THREE.Vector3(
(Math.random() - 0.5),
0,
(Math.random() - 0.5)
).normalize(),
fleeRange: 2.5,
fleeStrength: 4.0,
originalY: -1 + Math.random() * 1.5
};
// Random position within bounds
group.position.set(
(Math.random() - 0.5) * 16,
-3 + Math.random() * 2,
(Math.random() - 0.5) * 16
);
// Face direction
group.userData.dir.set(group.position.x > 0 ? -1 : 1, 0, 0);
group.lookAt(group.position.clone().add(group.userData.dir));
return group;
}
// ---------- Populate fish ----------
const fishCount = 24;
const fish = [];
for (let i = 0; i < fishCount; i++) {
const f = makeFish();
scene.add(f);
fish.push(f);
}
// ---------- Input ----------
addEventListener('pointermove', (e) => {
mouse.x = (e.clientX / innerWidth) * 2 - 1;
mouse.y = -(e.clientY / innerHeight) * 2 + 1;
updateMouse3D();
});
// ---------- Start overlay ----------
const startScreen = document.getElementById('start');
let started = false;
function begin() {
if (started) return;
started = true;
startScreen.style.display = 'none';
}
startScreen.addEventListener('pointerdown', begin);
addEventListener('keydown', begin);
// ---------- Animation ----------
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const dt = Math.min(clock.getDelta(), 0.05);
// gentle water ripple
water.rotation.z += dt * 0.2;
// Update fish
for (const f of fish) {
const d = f.userData;
const pos = f.position;
// Flee behavior
const dist = pos.distanceTo(mouse3D);
if (dist < d.fleeRange) {
const away = new THREE.Vector3()
.subVectors(pos, mouse3D)
.setY(0)
.normalize();
d.dir.lerp(away, 0.2).normalize();
} else {
// wander slowly
const wander = new THREE.Vector3(
Math.sin(performance.now() * 0.001 + pos.x) * 0.5,
0,
Math.cos(performance.now() * 0.001 + pos.z) * 0.5
).normalize();
d.dir.lerp(wander, 0.02).normalize();
}
// Move
pos.addScaledVector(d.dir, d.speed * dt);
// Boundaries
const limit = 9;
if (Math.abs(pos.x) > limit) { pos.x = Math.sign(pos.x) * limit; d.dir.x *= -1; }
if (Math.abs(pos.z) > limit) { pos.z = Math.sign(pos.z) * limit; d.dir.z *= -1; }
// Keep within water column
pos.y = Math.max(-3.5, Math.min(-0.5, pos.y + (d.originalY - pos.y) * 0.02));
// Orient fish
f.lookAt(pos.clone().add(d.dir));
// Tail animation
d.tail.rotation.y = Math.sin(performance.now() * 0.008 + pos.x) * 0.4;
}
controls.update();
renderer.render(scene, camera);
}
// ---------- Resize ----------
addEventListener('resize', () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
animate();
</script>
</body>
</html>