Models 44

Claude Haiku 4.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
47.7 s
Cost
$0.084
Output tokens
16,700
Spent thinking
12,968 tokens
Speed
355.7 tok/s
Console errors
0
First token after
699 ms
First visible text after
45.5 s
Prompt tokens
644
Code size
10.8 KB
Finish reason
stop
Served by
Amazon Bedrock
Model id
anthropic/claude-haiku-4.5
Reasoning sent
{"enabled":true}
Max output
64,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 · 299 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 Scene</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background: #000;
        }
        canvas {
            display: block;
        }
    </style>
    <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>
</head>
<body>
    <script type="module">
        import * as THREE from 'three';
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

        // Scene setup
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.PCFShadowShadowMap;
        document.body.appendChild(renderer.domElement);

        // Set camera position
        camera.position.set(20, 10, 20);
        camera.lookAt(0, 2, 0);

        // Night sky and fog
        scene.background = new THREE.Color(0x0d0d1f);
        scene.fog = new THREE.Fog(0x0d0d1f, 60, 160);

        // Lighting
        const ambientLight = new THREE.AmbientLight(0x1a1a2e, 0.25);
        scene.add(ambientLight);

        const moonLight = new THREE.DirectionalLight(0x4488ff, 0.35);
        moonLight.position.set(-40, 50, -40);
        moonLight.castShadow = true;
        moonLight.shadow.mapSize.width = 2048;
        moonLight.shadow.mapSize.height = 2048;
        moonLight.shadow.camera.far = 200;
        moonLight.shadow.camera.left = -100;
        moonLight.shadow.camera.right = 100;
        moonLight.shadow.camera.top = 100;
        moonLight.shadow.camera.bottom = -100;
        scene.add(moonLight);

        // Campfire light with flickering
        const fireLight = new THREE.PointLight(0xff9922, 1.2, 40);
        fireLight.position.set(0, 1.2, 0);
        fireLight.castShadow = true;
        fireLight.shadow.mapSize.width = 1024;
        fireLight.shadow.mapSize.height = 1024;
        scene.add(fireLight);

        // Ground
        const groundGeometry = new THREE.PlaneGeometry(250, 250);
        const groundMaterial = new THREE.MeshLambertMaterial({ color: 0x2d5016 });
        const ground = new THREE.Mesh(groundGeometry, groundMaterial);
        ground.rotation.x = -Math.PI / 2;
        ground.receiveShadow = true;
        scene.add(ground);

        // Helper functions for low-poly objects
        function createRock(size = 1) {
            return new THREE.IcosahedronGeometry(size * 0.5, 1);
        }

        function createLog(length = 1, radius = 0.3) {
            return new THREE.CylinderGeometry(radius, radius, length, 6, 2);
        }

        function createTree(height = 3, baseRadius = 0.5) {
            return new THREE.ConeGeometry(baseRadius, height, 5, 2);
        }

        // Rocks around fire
        const rockPositions = [
            { x: 3.5, z: 0 }, { x: -3.5, z: 0 },
            { x: 0, z: 3.5 }, { x: 0, z: -3.5 },
            { x: 2.8, z: 2.8 }, { x: -2.8, z: 2.8 },
            { x: 2.8, z: -2.8 }, { x: -2.8, z: -2.8 },
        ];

        rockPositions.forEach(pos => {
            const rock = new THREE.Mesh(
                createRock(0.9),
                new THREE.MeshLambertMaterial({ color: 0x8b7355 })
            );
            rock.position.set(pos.x, 0.5, pos.z);
            rock.castShadow = true;
            rock.receiveShadow = true;
            scene.add(rock);
        });

        // Logs around fire
        const logPositions = [
            { x: 5, z: 1.5, rotZ: 0.2 },
            { x: -5, z: -1.5, rotZ: -0.2 },
            { x: 1.5, z: 5, rotZ: 0.1 },
            { x: -1.5, z: -5, rotZ: -0.1 },
        ];

        logPositions.forEach(pos => {
            const log = new THREE.Mesh(
                createLog(2, 0.35),
                new THREE.MeshLambertMaterial({ color: 0x5c4033 })
            );
            log.position.set(pos.x, 0.4, pos.z);
            log.rotation.z = pos.rotZ;
            log.castShadow = true;
            log.receiveShadow = true;
            scene.add(log);
        });

        // Create 40 trees (exceeds 30 minimum)
        for (let i = 0; i < 40; i++) {
            let x, z, distance;
            do {
                x = (Math.random() - 0.5) * 220;
                z = (Math.random() - 0.5) * 220;
                distance = Math.sqrt(x * x + z * z);
            } while (distance < 12);

            const treeHeight = 5 + Math.random() * 4;
            const treeRadius = 0.4 + Math.random() * 0.3;
            const tree = new THREE.Mesh(
                createTree(treeHeight, treeRadius),
                new THREE.MeshLambertMaterial({ color: 0x1a4d1a + Math.floor(Math.random() * 0x1a1a00) })
            );
            tree.position.set(x, treeHeight / 2, z);
            tree.castShadow = true;
            tree.receiveShadow = true;
            scene.add(tree);
        }

        // Campfire wood base
        const woodGroup = new THREE.Group();
        for (let i = 0; i < 3; i++) {
            const wood = new THREE.Mesh(
                createLog(1.2, 0.25),
                new THREE.MeshLambertMaterial({ color: 0x3d2817 })
            );
            wood.position.y = i * 0.35;
            wood.rotation.z = (i % 2) * (Math.PI / 3.5);
            wood.castShadow = true;
            wood.receiveShadow = true;
            woodGroup.add(wood);
        }
        scene.add(woodGroup);

        // Animated flames
        const flameGroup = new THREE.Group();
        for (let i = 0; i < 5; i++) {
            const flameGeometry = new THREE.ConeGeometry(0.5, 2, 4, 2);
            const flameMaterial = new THREE.MeshBasicMaterial({
                color: 0xff8800,
                transparent: true,
                opacity: 0.85,
            });
            const flame = new THREE.Mesh(flameGeometry, flameMaterial);
            flame.position.set(
                Math.sin(i * Math.PI * 2 / 5) * 0.4,
                1,
                Math.cos(i * Math.PI * 2 / 5) * 0.4
            );
            flameGroup.add(flame);
        }
        scene.add(flameGroup);

        // Spark particle system
        const sparkGeometry = new THREE.BufferGeometry();
        const sparkCount = 150;
        const sparkPositions = new Float32Array(sparkCount * 3);
        const sparkVelocities = [];

        for (let i = 0; i < sparkCount; i++) {
            sparkPositions[i * 3] = (Math.random() - 0.5) * 1.5;
            sparkPositions[i * 3 + 1] = Math.random() * 0.5;
            sparkPositions[i * 3 + 2] = (Math.random() - 0.5) * 1.5;

            sparkVelocities.push({
                x: (Math.random() - 0.5) * 0.2,
                y: Math.random() * 0.25 + 0.08,
                z: (Math.random() - 0.5) * 0.2,
                life: Math.random() * 2.5 + 0.5,
                maxLife: Math.random() * 2.5 + 0.5,
            });
        }

        sparkGeometry.setAttribute('position', new THREE.BufferAttribute(sparkPositions, 3));
        const sparkMaterial = new THREE.PointsMaterial({
            color: 0xffcc00,
            size: 0.15,
            sizeAttenuation: true,
        });
        const sparks = new THREE.Points(sparkGeometry, sparkMaterial);
        sparks.position.set(0, 0.3, 0);
        scene.add(sparks);

        // Stars
        const starGeometry = new THREE.BufferGeometry();
        const starCount = 300;
        const starPositions = new Float32Array(starCount * 3);

        for (let i = 0; i < starCount; i++) {
            starPositions[i * 3] = (Math.random() - 0.5) * 500;
            starPositions[i * 3 + 1] = Math.random() * 350 + 80;
            starPositions[i * 3 + 2] = (Math.random() - 0.5) * 500;
        }

        starGeometry.setAttribute('position', new THREE.BufferAttribute(starPositions, 3));
        const starMaterial = new THREE.PointsMaterial({
            color: 0xffffff,
            size: 0.4,
            sizeAttenuation: true,
        });
        const stars = new THREE.Points(starGeometry, starMaterial);
        scene.add(stars);

        // Moon
        const moonGeometry = new THREE.SphereGeometry(10, 16, 16);
        const moonMaterial = new THREE.MeshBasicMaterial({ color: 0xffffee });
        const moon = new THREE.Mesh(moonGeometry, moonMaterial);
        moon.position.set(70, 70, -100);
        scene.add(moon);

        // OrbitControls with limits
        const controls = new OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;
        controls.autoRotate = true;
        controls.autoRotateSpeed = 0.5;
        controls.maxPolarAngle = Math.PI * 0.6;
        controls.minDistance = 8;
        controls.maxDistance = 120;
        controls.target.set(0, 2, 0);

        // Animation loop
        let time = 0;
        function animate() {
            requestAnimationFrame(animate);
            time += 0.016;

            // Flickering fire light
            fireLight.intensity = 0.9 + Math.sin(time * 2.5) * 0.25 + Math.random() * 0.3;

            // Animate flames
            flameGroup.children.forEach((flame, i) => {
                flame.rotation.z = Math.sin(time * 2.5 + i) * 0.5 + (Math.random() - 0.5) * 0.3;
                flame.scale.y = 0.7 + Math.sin(time * 3 + i * 0.7) * 0.35;
                flame.position.y = 1 + Math.sin(time * 2 + i) * 0.2;
            });

            // Update sparks
            const sparkPos = sparkGeometry.getAttribute('position');
            for (let i = 0; i < sparkCount; i++) {
                const vel = sparkVelocities[i];
                sparkPos.array[i * 3] += vel.x;
                sparkPos.array[i * 3 + 1] += vel.y;
                sparkPos.array[i * 3 + 2] += vel.z;

                vel.life -= 0.016;
                if (vel.life <= 0) {
                    sparkPos.array[i * 3] = (Math.random() - 0.5) * 1.5;
                    sparkPos.array[i * 3 + 1] = 0.1;
                    sparkPos.array[i * 3 + 2] = (Math.random() - 0.5) * 1.5;
                    vel.life = vel.maxLife;
                    vel.x = (Math.random() - 0.5) * 0.2;
                    vel.y = Math.random() * 0.25 + 0.08;
                    vel.z = (Math.random() - 0.5) * 0.2;
                }
            }
            sparkPos.needsUpdate = true;

            controls.update();
            renderer.render(scene, camera);
        }

        // Handle window resize
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });

        animate();
    </script>
</body>
</html>
Reasoning trace (12,968 tokens)
Let me build a low-poly 3D scene of a campfire in a forest clearing at night using Three.js. I need to:

1. Set up Three.js with the provided import map
2. Create a ground plane
3. Add low-poly rocks and logs around the campfire
4. Create at least 30 low-poly trees
5. Implement an animated campfire with flames
6. Add a warm point light that flickers and casts shadows
7. Create a spark particle system
8. Add a night sky with stars and moon
9. Add fog for atmosphere
10. Implement OrbitControls with camera limits
11. Handle window resizing

Let me structure this carefully:

```html
<!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 Scene</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background: #000;
        }
        canvas {
            display: block;
        }
    </style>
    <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>
</head>
<body>
    <script type="module">
        import * as THREE from 'three';
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

        // Scene setup
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.PCFShadowShadowMap;
        document.body.appendChild(renderer.domElement);

Camera goes at (15, 8, 15) looking toward (0, 2, 0), and the background gets a deep night-blue tone with matching fog for depth.

I'm setting up the lighting: a dim ambient fill, a cool moonlight directional with shadows configured for a wide camera frustum, and a warm flickering point light for the campfire with its own shadow map. Next I need a helper function.

reate low-poly objects for the scene - rocks using a custom irregular vertex buffer, logs with squat cylinder geometry, and trees starting with a simple cone shape.

For the ground, I'll use a large plane with a dark green Lambert material to represent grass. Then I need to scatter rocks and logs in a circle around where the campfire will sit, so I'm defining an array of positions radiating outward in a rough octagon pattern (3 units out along axes and diagonals), then looping through to create rock meshes with a brownish-gray color at each spot.

For the logs, I'll place four of them at slightly farther positions with rotations so they angle naturally like they're arranged for sitting around the fire, using a brown wood-tone material. Both rocks and logs get shadow casting/receiving enabled.

Finally I'm starting on tree generation, planning a loop of 35 trees that need position validation to avoid overlap with the fire area.

x = (Math.random() - 0.5) * 180;
            z = (Math.random() - 0.5) * 180;
            distance = Math.sqrt(x * x + z * z);
        } while (distance < 10);

Each tree gets randomized height, scaled appropriately, with shadows enabled, all grouped into a single treeGroup for the scene. Then I move onto building the campfire's wood stack from three angled log pieces in a darker charred-brown tone.

Next comes the flame group, using cone geometries arranged in a cross pattern with slight random tilts to break up the uniformity. Bright orange, semi-transparent material gives them a glowing, flickering feel.

For sparks, I'll build a particle system with a buffer geometry—around 100 points scattered within a small radius above the fire. Each spark gets its own velocity data stored separately (x/z drift, upward y speed, and a lifespan) so they can be animated individually later.

Once the spark PointsMaterial is set with size 0.2 and sizeAttenuation enabled, I attach the points mesh and position it near the top of the fire.

Next, scattering stars across the sky: generating 200 random positions spread over a wide x/z range with y elevated well above the scene, then creating a simple white PointsMaterial for them and adding to the scene.

For the moon, I use a basic sphere with an unlit cream-colored material, placed high and off to the side.

Setting up OrbitControls with damping, restricting polar angle so the camera can't dip below ground, and bounding zoom distances, with the target centered near the fire.

The animation loop begins by tracking elapsed time and flickering the fire light intensity using a sine wave plus randomness for a natural campfire glow.

Each flame mesh gets rotated and scaled based on a sine function offset per flame, giving them a flickering, uneven motion. Spark particles update their positions using stored velocity vectors each frame, and when a spark's life runs out, I reset it back to the base of the fire with a fresh random velocity and lifespan.

With particle updates done, I attach a resize listener to keep the camera aspect and renderer size in sync with the window, then call animate() to kick things off.

Looking back at this, I want to double check the import map, confirm all planned features made it in, and verify the scene renders cleanly—fog and lighting especially need to play well together. I also recall I'm using a custom geometry for the rocks, so I should make sure that's solid too.

ols.js';

Setting up the scene, camera, and renderer with shadows enabled. Positioning camera at (20,10,20) looking toward the fire. Night sky background with fog for depth, dim ambient light plus a cool-toned moon directional light coming from the upper left.

Configuring the moon light's shadow camera bounds to cover the scene area, then adding a warm flickering point light for the campfire with its own shadow map. Building the ground plane with a dark green lambert material, rotating it flat and enabling shadow receiving.

Setting up helper functions for generating low-poly geometry: icosahedrons for rocks, cylinders for logs, and cones for trees, each parameterized by size. Defining an array of positions arranged in a circle around the campfire to place the rocks.

Building out the rocks first, meshing them with a brownish Lambert material and positioning each at y=0.5 with shadows enabled. Next, planning log placements for seating at wider distances with slight rotations for a natural scattered look, using a darker brown material.

Then scattering 40 trees randomly around the clearing, keeping them at least 12 units from center, varying heights/radii and green shades. Building the campfire wood base from stacked logs, adding animated cone-shaped flames arranged in a circle, and setting up a spark particle system with randomized velocities and life cycles for rising embers.

Adding a starfield with 300 points and a simple moon sphere positioned off in the distance. Configuring OrbitControls with damping, auto-rotation, and sensible polar/distance limits centered on the fire.

In the animation loop, flickering the fire light intensity, animating the flame cones with rotation/scale/position jitter, and updating spark positions with respawning logic when their life expires. Adding a resize listener to keep the renderer responsive.

Checking off the requirements: ground scenery with rocks/logs, 40 trees exceeding the 30 minimum, animated flames, flickering warm light, rising sparks, starry sky with moon, fog, and constrained OrbitControls. Verified shadows are properly enabled across lights and meshes, and the camera starts at a good diagonal angle (20,10,20) to view the fire. Fog fading between 60-160 units should nicely obscure the distant trees. Confirming the canvas fills the viewport with no overflow, ensuring no horizontal scrollbar even at narrow widths.