Models 44

Claude Haiku 5.5

by AnthropicReasoningClean
Open in new window

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>