Claude Haiku 5.5
by AnthropicReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 26.5 s
- Cost
- $0.0040
- Output tokens
- 7,868
- Spent thinking
- 0 tokens
- Speed
- 305.4 tok/s
- Console errors
- 0
- First token after
- 719 ms
- First visible text after
- 719 ms
- Prompt tokens
- 854
- Code size
- 14.7 KB
- Finish reason
- stop
- Served by
- Claude Platform on AWS
- Model id
anthropic/claude-haiku-5.5- Reasoning sent
{"effort":"none"}- Max output
- 32,000 tokens
- Generated
- Nov 10, 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 · 450 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Low-Poly Campfire at Night</title>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background: #05070f;
font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
}
canvas { display: block; }
#hint {
position: fixed;
left: 50%;
bottom: 18px;
transform: translateX(-50%);
color: #ffd9a0;
background: rgba(20, 10, 0, 0.55);
padding: 8px 14px;
border-radius: 8px;
font-size: 14px;
pointer-events: none;
white-space: nowrap;
max-width: 92vw;
overflow: hidden;
text-overflow: ellipsis;
}
#title {
position: fixed;
top: 14px;
left: 16px;
color: #cfd8ff;
background: rgba(5, 8, 20, 0.5);
padding: 6px 10px;
border-radius: 6px;
font-size: 13px;
pointer-events: none;
}
</style>
</head>
<body>
<div id="title">Campfire · drag to look around · scroll to zoom</div>
<div id="hint">Drag to orbit the camera around the campfire</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';
// ---------- Renderer, scene, camera ----------
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const skyColor = new THREE.Color(0x070a18);
scene.background = skyColor;
scene.fog = new THREE.Fog(0x070a18, 28, 85);
const camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 300);
camera.position.set(9, 4.5, 11);
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 1.2, 0);
controls.enableDamping = true;
controls.dampingFactor = 0.06;
controls.minDistance = 4;
controls.maxDistance = 30;
controls.minPolarAngle = 0.2;
controls.maxPolarAngle = Math.PI / 2 - 0.05; // stay above ground
controls.update();
// ---------- Seeded random for stable layout ----------
let seed = 1337;
function rand() {
seed = (seed * 16807) % 2147483647;
return (seed - 1) / 2147483646;
}
function rrange(a, b) { return a + (b - a) * rand(); }
// ---------- Lighting ----------
const hemi = new THREE.HemisphereLight(0x3a4a7a, 0x0b0a08, 0.35);
scene.add(hemi);
const moonLight = new THREE.DirectionalLight(0x9db4ff, 0.45);
moonLight.position.set(-20, 30, -15);
moonLight.castShadow = true;
moonLight.shadow.mapSize.set(1024, 1024);
moonLight.shadow.camera.left = -25;
moonLight.shadow.camera.right = 25;
moonLight.shadow.camera.top = 25;
moonLight.shadow.camera.bottom = -25;
moonLight.shadow.camera.far = 90;
scene.add(moonLight);
const fireLight = new THREE.PointLight(0xff8a2a, 60, 22, 1.6);
fireLight.position.set(0, 1.1, 0);
fireLight.castShadow = true;
fireLight.shadow.mapSize.set(512, 512);
fireLight.shadow.bias = -0.002;
scene.add(fireLight);
// ---------- Materials ----------
const groundMat = new THREE.MeshStandardMaterial({ color: 0x1d2a1c, flatShading: true, roughness: 1 });
const rockMat = new THREE.MeshStandardMaterial({ color: 0x5a5f66, flatShading: true, roughness: 0.9 });
const logMat = new THREE.MeshStandardMaterial({ color: 0x4a2f1c, flatShading: true, roughness: 0.95 });
const trunkMat = new THREE.MeshStandardMaterial({ color: 0x3b2716, flatShading: true, roughness: 1 });
const leafMatA = new THREE.MeshStandardMaterial({ color: 0x1f4a25, flatShading: true, roughness: 0.9 });
const leafMatB = new THREE.MeshStandardMaterial({ color: 0x2a5c2e, flatShading: true, roughness: 0.9 });
const leafMatC = new THREE.MeshStandardMaterial({ color: 0x173a20, flatShading: true, roughness: 0.9 });
const ashMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, flatShading: true, roughness: 1 });
// ---------- Ground ----------
const groundGeo = new THREE.CircleGeometry(80, 24);
groundGeo.rotateX(-Math.PI / 2);
// Jitter vertices for low-poly terrain
{
const pos = groundGeo.attributes.position;
for (let i = 0; i < pos.count; i++) {
const x = pos.getX(i), z = pos.getZ(i);
const d = Math.hypot(x, z);
if (d > 0.5) {
pos.setY(i, (rand() - 0.5) * 0.35 * Math.min(1, d / 6));
}
}
groundGeo.computeVertexNormals();
}
const ground = new THREE.Mesh(groundGeo, groundMat);
ground.receiveShadow = true;
scene.add(ground);
// Clearing dirt ring around fire
const clearingGeo = new THREE.CircleGeometry(4.2, 16);
clearingGeo.rotateX(-Math.PI / 2);
const clearing = new THREE.Mesh(clearingGeo, ashMat);
clearing.position.y = 0.02;
clearing.receiveShadow = true;
scene.add(clearing);
// ---------- Helpers ----------
function addMesh(geo, mat, x, y, z, { castShadow = true, receiveShadow = true } = {}) {
const m = new THREE.Mesh(geo, mat);
m.position.set(x, y, z);
m.castShadow = castShadow;
m.receiveShadow = receiveShadow;
scene.add(m);
return m;
}
// ---------- Rocks ----------
const rockPositions = [
[2.6, 0, 1.2, 0.6], [-2.4, 0, 2.0, 0.5], [1.4, 0, -2.6, 0.7],
[-2.8, 0, -1.0, 0.55], [0.4, 0, 3.0, 0.45], [-1.2, 0, -3.1, 0.6],
[3.4, 0, -0.8, 0.5], [-3.6, 0, 0.6, 0.4]
];
rockPositions.forEach(([x, , z, s]) => {
const geo = new THREE.DodecahedronGeometry(s, 0);
const m = addMesh(geo, rockMat, x, s * 0.35, z);
m.rotation.set(rrange(0, 3), rrange(0, 3), rrange(0, 3));
m.scale.set(1, rrange(0.6, 0.9), rrange(0.9, 1.2));
});
// ---------- Logs (seating around the fire) ----------
function addLog(x, z, angle) {
const geo = new THREE.CylinderGeometry(0.22, 0.25, 2.2, 6);
geo.rotateZ(Math.PI / 2);
const m = addMesh(geo, logMat, x, 0.25, z);
m.rotation.y = angle;
return m;
}
addLog(2.1, 0.4, 0.2);
addLog(-2.0, -0.6, -0.5);
addLog(0.3, -2.3, 1.2);
addLog(-0.4, 2.4, -0.1);
// ---------- Campfire: stone ring + logs + flames ----------
for (let i = 0; i < 10; i++) {
const a = (i / 10) * Math.PI * 2;
const r = 0.85;
const s = rrange(0.18, 0.26);
const m = addMesh(new THREE.DodecahedronGeometry(s, 0), rockMat,
Math.cos(a) * r, 0.08, Math.sin(a) * r);
m.rotation.set(rrange(0, 3), rrange(0, 3), rrange(0, 3));
}
// Crossed firewood logs
const fireLogGeo = new THREE.CylinderGeometry(0.1, 0.12, 1.3, 5);
const fireLog1 = addMesh(fireLogGeo, trunkMat, 0, 0.14, 0);
fireLog1.rotation.set(0, 0.6, Math.PI / 2 - 0.12);
const fireLog2 = addMesh(fireLogGeo, trunkMat, 0, 0.16, 0);
fireLog2.rotation.set(0, -0.6, Math.PI / 2 + 0.1);
fireLog2.rotation.x = 0.15;
const fireLog3 = addMesh(fireLogGeo, trunkMat, 0, 0.3, 0);
fireLog3.rotation.set(0.5, 0.2, 0.9);
// Flame layers (low-poly cones with additive-ish glow)
const flameGroup = new THREE.Group();
flameGroup.position.set(0, 0.35, 0);
scene.add(flameGroup);
const flameColors = [0xff4a12, 0xff8a1e, 0xffc94a];
const flameLayers = [];
for (let i = 0; i < 3; i++) {
const geo = new THREE.ConeGeometry(0.38 - i * 0.09, 1.1 - i * 0.15, 5, 1);
const mat = new THREE.MeshBasicMaterial({
color: flameColors[i],
transparent: true,
opacity: 0.9 - i * 0.12,
depthWrite: false,
});
const cone = new THREE.Mesh(geo, mat);
cone.position.y = 0.45 + i * 0.06;
flameGroup.add(cone);
flameLayers.push(cone);
}
// Inner glowing core
const coreMat = new THREE.MeshBasicMaterial({ color: 0xffe9a8, transparent: true, opacity: 0.85, depthWrite: false });
const core = new THREE.Mesh(new THREE.IcosahedronGeometry(0.22, 0), coreMat);
core.position.y = 0.4;
flameGroup.add(core);
// ---------- Trees ----------
function makePineTree(scale) {
const g = new THREE.Group();
const trunkH = 1.2 * scale;
const trunk = new THREE.Mesh(new THREE.CylinderGeometry(0.12 * scale, 0.18 * scale, trunkH, 5), trunkMat);
trunk.position.y = trunkH / 2;
trunk.castShadow = true;
g.add(trunk);
const mats = [leafMatA, leafMatB, leafMatC];
const tiers = 3 + Math.floor(rand() * 2);
for (let t = 0; t < tiers; t++) {
const r = (1.1 - t * 0.2) * scale;
const h = (1.4 - t * 0.12) * scale;
const cone = new THREE.Mesh(new THREE.ConeGeometry(r, h, 6), mats[t % mats.length]);
cone.position.y = trunkH + t * 0.75 * scale + h / 2 - 0.2 * scale;
cone.castShadow = true;
cone.receiveShadow = true;
g.add(cone);
}
return g;
}
function makeRoundTree(scale) {
const g = new THREE.Group();
const trunkH = 1.4 * scale;
const trunk = new THREE.Mesh(new THREE.CylinderGeometry(0.14 * scale, 0.2 * scale, trunkH, 5), trunkMat);
trunk.position.y = trunkH / 2;
trunk.castShadow = true;
g.add(trunk);
const crown = new THREE.Mesh(new THREE.IcosahedronGeometry(1.0 * scale, 0), rand() > 0.5 ? leafMatA : leafMatB);
crown.position.y = trunkH + 0.7 * scale;
crown.scale.set(1, 0.9 + rand() * 0.4, 1);
crown.castShadow = true;
crown.receiveShadow = true;
g.add(crown);
return g;
}
let treeCount = 0;
let attempts = 0;
while (treeCount < 42 && attempts < 1000) {
attempts++;
const a = rand() * Math.PI * 2;
const r = rrange(6.5, 40);
const x = Math.cos(a) * r;
const z = Math.sin(a) * r;
// keep clear of the fire area
if (Math.hypot(x, z) < 6) continue;
const scale = rrange(0.8, 1.6);
const tree = rand() > 0.45 ? makePineTree(scale) : makeRoundTree(scale);
tree.position.set(x, 0, z);
tree.rotation.y = rand() * Math.PI * 2;
scene.add(tree);
treeCount++;
}
// ---------- Stars ----------
{
const count = 1200;
const positions = new Float32Array(count * 3);
const colors = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
// upper hemisphere
const theta = rand() * Math.PI * 2;
const phi = Math.acos(rand() * 0.95 + 0.05);
const R = 180;
positions[i * 3] = R * Math.sin(phi) * Math.cos(theta);
positions[i * 3 + 1] = R * Math.cos(phi);
positions[i * 3 + 2] = R * Math.sin(phi) * Math.sin(theta);
const tint = 0.7 + rand() * 0.3;
colors[i * 3] = tint;
colors[i * 3 + 1] = tint;
colors[i * 3 + 2] = Math.min(1, tint + rand() * 0.2);
}
const geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geo.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const mat = new THREE.PointsMaterial({ size: 1.4, vertexColors: true, sizeAttenuation: false, fog: false });
const stars = new THREE.Points(geo, mat);
scene.add(stars);
}
// ---------- Moon ----------
const moonGroup = new THREE.Group();
moonGroup.position.set(-90, 95, -120);
scene.add(moonGroup);
const moonMesh = new THREE.Mesh(
new THREE.IcosahedronGeometry(7, 1),
new THREE.MeshBasicMaterial({ color: 0xf2f1e6, fog: false })
);
moonGroup.add(moonMesh);
const halo = new THREE.Mesh(
new THREE.CircleGeometry(16, 24),
new THREE.MeshBasicMaterial({ color: 0xaec6ff, transparent: true, opacity: 0.12, depthWrite: false, fog: false })
);
halo.lookAt(camera.position);
moonGroup.add(halo);
// ---------- Sparks (particle system) ----------
const SPARK_COUNT = 160;
const sparkPos = new Float32Array(SPARK_COUNT * 3);
const sparkColor = new Float32Array(SPARK_COUNT * 3);
const sparkVel = new Float32Array(SPARK_COUNT * 3);
const sparkLife = new Float32Array(SPARK_COUNT);
const sparkMaxLife = new Float32Array(SPARK_COUNT);
function respawnSpark(i, initial = false) {
const spread = 0.25;
sparkPos[i * 3] = rrange(-spread, spread);
sparkPos[i * 3 + 1] = rrange(0.5, 0.9);
sparkPos[i * 3 + 2] = rrange(-spread, spread);
sparkVel[i * 3] = rrange(-0.25, 0.25);
sparkVel[i * 3 + 1] = rrange(0.8, 1.8);
sparkVel[i * 3 + 2] = rrange(-0.25, 0.25);
sparkMaxLife[i] = rrange(1.6, 3.2);
sparkLife[i] = initial ? rrange(0, sparkMaxLife[i]) : sparkMaxLife[i];
}
for (let i = 0; i < SPARK_COUNT; i++) respawnSpark(i, true);
const sparkGeo = new THREE.BufferGeometry();
sparkGeo.setAttribute('position', new THREE.BufferAttribute(sparkPos, 3));
sparkGeo.setAttribute('color', new THREE.BufferAttribute(sparkColor, 3));
const sparkMat = new THREE.PointsMaterial({
size: 0.09,
vertexColors: true,
transparent: true,
opacity: 1,
blending: THREE.AdditiveBlending,
depthWrite: false,
sizeAttenuation: true,
});
const sparks = new THREE.Points(sparkGeo, sparkMat);
sparks.frustumCulled = false;
scene.add(sparks);
const hotColor = new THREE.Color(0xffd27a);
const coolColor = new THREE.Color(0xff5a1a);
const tmpColor = new THREE.Color();
function updateSparks(dt) {
for (let i = 0; i < SPARK_COUNT; i++) {
sparkLife[i] -= dt;
if (sparkLife[i] <= 0) {
respawnSpark(i);
continue;
}
const k = i * 3;
sparkVel[k] += rrange(-0.8, 0.8) * dt * 0.5; // slight turbulence
sparkVel[k + 2] += rrange(-0.8, 0.8) * dt * 0.5;
sparkVel[k + 1] -= 0.35 * dt; // buoyancy decay
sparkPos[k] += sparkVel[k] * dt;
sparkPos[k + 1] += sparkVel[k + 1] * dt;
sparkPos[k + 2] += sparkVel[k + 2] * dt;
const t = Math.max(0, sparkLife[i] / sparkMaxLife[i]); // 1 -> 0
tmpColor.copy(coolColor).lerp(hotColor, t);
const fade = t * t;
sparkColor[k] = tmpColor.r * fade;
sparkColor[k + 1] = tmpColor.g * fade;
sparkColor[k + 2] = tmpColor.b * fade;
}
sparkGeo.attributes.position.needsUpdate = true;
sparkGeo.attributes.color.needsUpdate = true;
}
// ---------- Animation ----------
const clock = new THREE.Clock();
let time = 0;
function animate() {
requestAnimationFrame(animate);
const dt = Math.min(clock.getDelta(), 0.05);
time += dt;
// Flame flicker: layered sine noise
const flick = 0.85
+ Math.sin(time * 13.1) * 0.06
+ Math.sin(time * 27.3 + 1.7) * 0.04
+ Math.sin(time * 5.7 + 0.3) * 0.05;
fireLight.intensity = 60 * flick;
fireLight.distance = 22 * (0.96 + flick * 0.05);
flameLayers.forEach((cone, i) => {
const s = 1 + Math.sin(time * (7 + i * 2.3) + i) * 0.08;
cone.scale.set(s, 0.9 + Math.sin(time * (9 + i) + i * 2) * 0.14, s);
cone.rotation.y += dt * (0.6 + i * 0.3);
cone.position.x = Math.sin(time * (3 + i) + i) * 0.03;
cone.position.z = Math.cos(time * (2.6 + i) + i) * 0.03;
});
core.rotation.y += dt * 2;
core.scale.setScalar(0.9 + Math.sin(time * 15) * 0.1);
updateSparks(dt);
// Subtle moon halo orientation toward camera
halo.lookAt(camera.position);
controls.update();
renderer.render(scene, camera);
}
// ---------- Resize ----------
function onResize() {
const w = window.innerWidth;
const h = window.innerHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
}
window.addEventListener('resize', onResize);
animate();
</script>
</body>
</html>