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
- 33.4 s
- Cost
- $0
- Output tokens
- 3,313
- Spent thinking
- 0 tokens
- Speed
- 102.3 tok/s
- Console errors
- 0
- First token after
- 1.0 s
- First visible text after
- 1.0 s
- Prompt tokens
- 566
- Code size
- 12.2 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
Build a low-poly 3D scene of a campfire in a forest clearing at night, with Three.js in a single HTML file. Load Three.js only through this import map (do not use any other URL or version): <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> Requirements: - A ground with a few low-poly rocks and logs around a campfire in the center. - At least 30 low-poly trees placed randomly around the clearing (not on the fire). - The campfire has animated flames and a warm point light that flickers and casts shadows. - Sparks rising from the fire as a simple particle system. - A dark night sky with stars and a moon, plus fog that fades the distant trees. - OrbitControls to look around, with limits so the camera stays above the ground. - The renderer fills the window and handles resizing correctly.
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 · 337 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Low-Poly Campfire Scene</title>
<style>
body { margin: 0; overflow: hidden; background: #000; }
canvas { display: block; }
#start-msg {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
color: #fff;
font-family: sans-serif;
font-size: 1.2rem;
background: rgba(0,0,0,0.6);
padding: 1rem 2rem;
border-radius: 8px;
cursor: pointer;
z-index: 10;
text-align: center;
}
</style>
</head>
<body>
<div id="start-msg">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 Setup ---
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a0a1a);
scene.fog = new THREE.FogExp2(0x0a0a1a, 0.015);
// --- Camera ---
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 500);
camera.position.set(0, 8, 12);
// --- Renderer ---
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// --- Controls ---
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 6;
controls.maxDistance = 40;
controls.maxPolarAngle = Math.PI * 0.45; // prevent going below ground
controls.target.set(0, 1, 0);
// --- Lighting ---
// Ambient
const ambient = new THREE.AmbientLight(0x222233, 0.4);
scene.add(ambient);
// Moonlight (directional)
const moon = new THREE.DirectionalLight(0x4455aa, 0.6);
moon.position.set(10, 20, 5);
moon.castShadow = true;
moon.shadow.mapSize.set(1024, 1024);
moon.shadow.camera.near = 0.5;
moon.shadow.camera.far = 50;
moon.shadow.camera.left = -15;
moon.shadow.camera.right = 15;
moon.shadow.camera.top = 15;
moon.shadow.camera.bottom = -15;
scene.add(moon);
// Campfire point light (flickering)
const fireLight = new THREE.PointLight(0xff6622, 2.5, 0, 1.5);
fireLight.position.set(0, 1.5, 0);
fireLight.castShadow = true;
fireLight.shadow.mapSize.set(512, 512);
fireLight.shadow.camera.near = 0.1;
fireLight.shadow.camera.far = 10;
scene.add(fireLight);
// --- Ground ---
const groundGeo = new THREE.PlaneGeometry(100, 100);
const groundMat = new THREE.MeshStandardMaterial({ color: 0x2a3a1a, roughness: 0.9 });
const ground = new THREE.Mesh(groundGeo, groundMat);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
// --- Sky: Stars + Moon ---
function createSky() {
// Star field
const starGeo = new THREE.BufferGeometry();
const starCount = 800;
const positions = new Float32Array(starCount * 3);
for (let i = 0; i < starCount; i++) {
positions[i * 3] = (Math.random() - 0.5) * 200;
positions[i * 3 + 1] = Math.random() * 60 + 20;
positions[i * 3 + 2] = (Math.random() - 0.5) * 200;
}
starGeo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const starMat = new THREE.PointsMaterial({ color: 0xffffff, size: 0.3, sizeAttenuation: true });
const stars = new THREE.Points(starGeo, starMat);
scene.add(stars);
// Moon
const moonGeo = new THREE.SphereGeometry(1.5, 16, 16);
const moonMat = new THREE.MeshBasicMaterial({ color: 0xffffcc });
const moonMesh = new THREE.Mesh(moonGeo, moonMat);
moonMesh.position.set(20, 30, -10);
scene.add(moonMesh);
}
createSky();
// --- Campfire Center ---
const fireGroup = new THREE.Group();
scene.add(fireGroup);
// Fire base (stones)
const stoneMat = new THREE.MeshStandardMaterial({ color: 0x444444, roughness: 0.9 });
for (let i = 0; i < 8; i++) {
const angle = (i / 8) * Math.PI * 2;
const radius = 0.8;
const x = Math.cos(angle) * radius;
const z = Math.sin(angle) * radius;
const stone = new THREE.Mesh(
new THREE.DodecahedronGeometry(0.25, 0),
stoneMat
);
stone.position.set(x, 0.2, z);
stone.castShadow = true;
stone.receiveShadow = true;
fireGroup.add(stone);
}
// Logs
const logMat = new THREE.MeshStandardMaterial({ color: 0x5a3a1a, roughness: 0.8 });
for (let i = 0; i < 4; i++) {
const angle = (i / 4) * Math.PI * 2;
const log = new THREE.Mesh(
new THREE.CylinderGeometry(0.1, 0.15, 1.2, 6),
logMat
);
log.position.set(Math.cos(angle) * 0.5, 0.3, Math.sin(angle) * 0.5);
log.rotation.z = Math.PI / 2;
log.rotation.y = angle;
log.castShadow = true;
log.receiveShadow = true;
fireGroup.add(log);
}
// --- Animated Flames ---
const flameCount = 12;
const flames = [];
const flameMat = new THREE.MeshBasicMaterial({ color: 0xff5522, transparent: true, opacity: 0.8, side: THREE.DoubleSide });
for (let i = 0; i < flameCount; i++) {
const flame = new THREE.Mesh(
new THREE.PlaneGeometry(0.3, 0.6),
flameMat
);
flame.position.set(
(Math.random() - 0.5) * 0.4,
0.5 + Math.random() * 0.3,
(Math.random() - 0.5) * 0.4
);
flame.rotation.y = Math.random() * Math.PI;
flame.userData.phase = Math.random() * Math.PI * 2;
flames.push(flame);
fireGroup.add(flame);
}
// --- Sparks Particle System ---
const sparkCount = 60;
const sparkGeo = new THREE.BufferGeometry();
const sparkPositions = new Float32Array(sparkCount * 3);
const sparkVelocities = new Float32Array(sparkCount * 3);
const sparkLifetimes = new Float32Array(sparkCount);
for (let i = 0; i < sparkCount; i++) {
sparkPositions[i * 3] = (Math.random() - 0.5) * 0.5;
sparkPositions[i * 3 + 1] = 0.5;
sparkPositions[i * 3 + 2] = (Math.random() - 0.5) * 0.5;
sparkVelocities[i * 3] = (Math.random() - 0.5) * 0.02;
sparkVelocities[i * 3 + 1] = 0.02 + Math.random() * 0.03;
sparkVelocities[i * 3 + 2] = (Math.random() - 0.5) * 0.02;
sparkLifetimes[i] = Math.random() * 2.0;
}
sparkGeo.setAttribute('position', new THREE.BufferAttribute(sparkPositions, 3));
const sparkMat = new THREE.PointsMaterial({
color: 0xffaa33,
size: 0.08,
transparent: true,
opacity: 0.9,
depthWrite: false
});
const sparks = new THREE.Points(sparkGeo, sparkMat);
fireGroup.add(sparks);
// --- Trees (Low-Poly) ---
const treeCount = 30;
const trees = [];
const treeMat = new THREE.MeshStandardMaterial({ color: 0x2a4a1a, roughness: 0.8 });
const trunkMat = new THREE.MeshStandardMaterial({ color: 0x3a2a1a, roughness: 0.9 });
function createTree(x, z) {
const tree = new THREE.Group();
// Trunk
const trunk = new THREE.Mesh(
new THREE.CylinderGeometry(0.15, 0.2, 1.5, 5),
trunkMat
);
trunk.position.y = 0.75;
trunk.castShadow = true;
trunk.receiveShadow = true;
tree.add(trunk);
// Foliage (low-poly cone)
const foliage = new THREE.Mesh(
new THREE.ConeGeometry(0.8, 1.8, 5),
treeMat
);
foliage.position.y = 2.0;
foliage.castShadow = true;
foliage.receiveShadow = true;
tree.add(foliage);
tree.position.set(x, 0, z);
return tree;
}
// Place trees avoiding the fire area
for (let i = 0; i < treeCount; i++) {
let x, z, dist;
do {
x = (Math.random() - 0.5) * 60;
z = (Math.random() - 0.5) * 60;
dist = Math.sqrt(x * x + z * z);
} while (dist < 5 || dist > 30); // Avoid fire and too far
const tree = createTree(x, z);
scene.add(tree);
trees.push(tree);
}
// --- Rocks ---
const rockMat = new THREE.MeshStandardMaterial({ color: 0x555555, roughness: 0.95 });
for (let i = 0; i < 10; i++) {
const angle = Math.random() * Math.PI * 2;
const radius = 3 + Math.random() * 5;
const x = Math.cos(angle) * radius;
const z = Math.sin(angle) * radius;
const rock = new THREE.Mesh(
new THREE.DodecahedronGeometry(0.3 + Math.random() * 0.4, 0),
rockMat
);
rock.position.set(x, 0.3, z);
rock.rotation.y = Math.random() * Math.PI;
rock.castShadow = true;
rock.receiveShadow = true;
scene.add(rock);
}
// --- Animation Loop ---
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
const time = clock.elapsedTime;
// Animate flames
flames.forEach((flame, i) => {
flame.scale.y = 0.8 + Math.sin(time * 10 + flame.userData.phase) * 0.3;
flame.material.opacity = 0.6 + Math.sin(time * 15 + flame.userData.phase) * 0.2;
});
// Flicker fire light
fireLight.intensity = 2.0 + Math.sin(time * 20) * 0.5 + Math.random() * 0.3;
// Update sparks
const positions = sparkGeo.attributes.position.array;
for (let i = 0; i < sparkCount; i++) {
positions[i * 3] += sparkVelocities[i * 3];
positions[i * 3 + 1] += sparkVelocities[i * 3 + 1];
positions[i * 3 + 2] += sparkVelocities[i * 3 + 2];
sparkLifetimes[i] -= delta;
if (sparkLifetimes[i] <= 0 || positions[i * 3 + 1] > 4) {
// Reset spark
positions[i * 3] = (Math.random() - 0.5) * 0.5;
positions[i * 3 + 1] = 0.5;
positions[i * 3 + 2] = (Math.random() - 0.5) * 0.5;
sparkLifetimes[i] = 2.0;
}
}
sparkGeo.attributes.position.needsUpdate = true;
controls.update();
renderer.render(scene, camera);
}
// --- Resize Handler ---
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// --- Start on User Interaction ---
let started = false;
function start() {
if (started) return;
started = true;
document.getElementById('start-msg').style.display = 'none';
animate();
}
window.addEventListener('click', start);
window.addEventListener('keydown', start);
</script>
</body>
</html>