Models 44

GPT-5 Nano

by OpenAIReasoningClean
Open in new window

Running live in a sandboxed frame. Click inside it before using the keyboard.

How it went

Total time
3 min 19 s
Cost
$0.0087
Output tokens
21,672
Spent thinking
17,216 tokens
Speed
111.1 tok/s
Console errors
0
First token after
4.5 s
First visible text after
2 min 43 s
Prompt tokens
555
Code size
14.4 KB
Finish reason
stop
Served by
OpenAI
Model id
openai/gpt-5-nano
Reasoning sent
{"effort":"high"}
Max output
128,000 tokens
Generated
Nov 12, 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 · 448 lines

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>Low-Poly Campfire in a Forest Clearing</title>
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <style>
    html, body {
      height: 100%;
      margin: 0;
      background: #000;
      overflow: hidden;
    }
    #scene {
      width: 100%;
      height: 100vh;
      display: block;
    }
    canvas { width: 100%; height: 100%; display: block; }

    /* Start overlay */
    #overlay {
      position: fixed;
      inset: 0;
      background: rgba(0, 0, 0, 0.75);
      display: flex;
      align-items: center;
      justify-content: center;
      text-align: center;
      z-index: 9999;
      color: #e8e8e8;
      padding: 2rem;
      cursor: default;
    }
    #overlay.hidden { display: none; }
    .overlayCard {
      background: rgba(20, 20, 30, 0.92);
      border-radius: 12px;
      padding: 2rem;
      max-width: 760px;
      width: min(92vw, 760px);
      box-shadow: 0 12px 40px rgba(0,0,0,.5);
    }
    h1 { margin: 0 0 0.6rem; font-size: clamp(20px, 4vw, 28px); }
    p { margin: 0.3rem 0; line-height: 1.4; font-size: clamp(12px, 2.8vw, 16px); }
    .hint { margin-top: 0.6rem; font-size: 12px; opacity: 0.9; }

    @media (max-width: 700px) {
      .overlayCard { padding: 1.4rem; }
    }
  </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>
  <div id="scene"></div>

  <div id="overlay" aria-label="Click to start the demo" role="button" tabindex="0">
    <div class="overlayCard">
      <h1>Night Campfire in the Forest</h1>
      <p>A low-poly, self-playing 3D scene. Click or press any key to start.</p>
      <p class="hint">Camera will auto-rotate around the campfire. You can look around after it starts (camera stays above the ground).</p>
    </div>
  </div>

  <script type="module">
  import * as THREE from 'three';
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

  // Global scene objects
  let scene, camera, renderer, controls;
  let fireLight;

  let campfireGroup;
  let flameMeshes = [];

  let sparks;           // particle system
  let stars;              // starfield
  let moon;               // moon
  let treesGroup;

  let started = false;
  const overlay = document.getElementById('overlay');
  const container = document.getElementById('scene');

  init();
  requestAnimationFrame(loop);

  function init() {
    // Scene
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0x0a0f1f);
    scene.fog = new THREE.FogExp2(0x0a0f1f, 0.002);

    // Camera
    camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 2000);
    camera.position.set(0, 6, 16);

    // Renderer
    renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    container.appendChild(renderer.domElement);

    // OrbitControls (start disabled)
    controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.dampingFactor = 0.05;
    controls.enablePan = false;
    controls.minDistance = 6;
    controls.maxDistance = 60;
    controls.maxPolarAngle = Math.PI * 0.65; // keep camera above ground
    controls.enabled = false;
    controls.autoRotate = true;
    controls.autoRotateSpeed = 0.6;

    // Lights
    const ambientLight = new THREE.AmbientLight(0x555566);
    scene.add(ambientLight);

    fireLight = new THREE.PointLight(0xffb46a, 2.0, 60, 2);
    fireLight.position.set(0, 1.6, 0);
    fireLight.castShadow = true;
    scene.add(fireLight);

    // Moonlight (soft)
    const moonLight = new THREE.DirectionalLight(0x9fbaff, 0.25);
    moonLight.position.set(-40, 60, -40);
    moonLight.castShadow = false;
    scene.add(moonLight);

    // Ground
    const groundSize = 120;
    const groundGeom = new THREE.PlaneGeometry(groundSize, groundSize, 64, 64);
    const groundMat = new THREE.MeshStandardMaterial({ color: 0x2a2a16, roughness: 1.0 });
    const ground = new THREE.Mesh(groundGeom, groundMat);
    ground.rotation.x = -Math.PI / 2;
    ground.receiveShadow = true;
    // small irregularities
    for (let i = 0; i < groundGeom.attributes.position.count; i++) {
      const jitter = (Math.random() - 0.5) * 0.15;
      groundGeom.attributes.position.setY(i, groundGeom.attributes.position.getY(i) + jitter);
    }
    groundGeom.computeVertexNormals();
    scene.add(ground);

    // Campfire group
    campfireGroup = new THREE.Group();
    scene.add(campfireGroup);

    // Rocks around campfire
    const ringR = 2.0;
    const rockCount = 12;
    for (let i = 0; i < rockCount; i++) {
      const theta = (i / rockCount) * Math.PI * 2;
      const rx = Math.cos(theta) * ringR;
      const rz = Math.sin(theta) * ringR;
      const rock = createRock(THREE.MathUtils.randFloat(0.55, 1.15), 0.15, THREE.MathUtils.randFloat(0.8, 1.2), 0x6b6b6b);
      rock.position.set(rx, 0.15, rz);
      rock.rotation.y = theta;
      rock.castShadow = true;
      rock.receiveShadow = true;
      campfireGroup.add(rock);
    }

    // Logs around fire
    const logGroup = new THREE.Group();
    campfireGroup.add(logGroup);
    for (let i = 0; i < 6; i++) {
      const angle = (i / 6) * Math.PI * 2;
      const r = 0.95;
      const log = createLog(THREE.MathUtils.randFloat(1.9, 2.4));
      log.position.set(Math.cos(angle) * r, 0.12, Math.sin(angle) * r);
      log.rotation.y = angle + Math.PI * 0.5;
      log.castShadow = true;
      log.receiveShadow = true;
      logGroup.add(log);
    }

    // Flames
    const flameGroup = new THREE.Group();
    campfireGroup.add(flameGroup);
    flameMeshes = [];
    for (let f = 0; f < 3; f++) {
      const height = 1.0 + f * 0.25;
      const flame = new THREE.Mesh(new THREE.ConeGeometry(0.28 - f * 0.05, 1.1 + f * 0.25, 8), new THREE.MeshStandardMaterial({
        color: 0xffa44d,
        emissive: 0xff6a00,
        emissiveIntensity: 0.8,
        roughness: 0.4,
        transparent: true,
        opacity: 0.95
      }));
      flame.position.set(0.0, 0.5 + f * 0.15, 0.0);
      flame.rotation.x = Math.PI; // facing upward
      flame.castShadow = true;
      flame.receiveShadow = true;
      flameGroup.add(flame);
      flameMeshes.push(flame);
    }

    // Base rock around fire
    const baseRock = createRock(0.9, 0.15, 1.0, 0x575757);
    baseRock.position.set(0, 0.15, 0);
    baseRock.castShadow = true;
    baseRock.receiveShadow = true;
    campfireGroup.add(baseRock);

    // Sparks
    sparks = createSparks(180);
    campfireGroup.add(sparks.points);

    // Sky: stars and moon
    stars = createStars(900);
    scene.add(stars.points);

    moon = createMoon();
    scene.add(moon);

    // Trees (>=30)
    treesGroup = new THREE.Group();
    scene.add(treesGroup);
    const targetTreeCount = 40; // more than 30
    for (let i = 0; i < targetTreeCount; i++) {
      const tree = makeLowPolyTree();
      const a = Math.random() * Math.PI * 2;
      const dist = THREE.MathUtils.randFloat(8, 60);
      tree.position.set(Math.cos(a) * dist, 0, Math.sin(a) * dist);
      tree.rotation.y = Math.random() * Math.PI * 2;
      tree.scale.setScalar(THREE.MathUtils.randFloat(0.65, 1.25));
      tree.traverse(n => {
        if (n.isMesh) { n.castShadow = true; n.receiveShadow = true; }
      });
      treesGroup.add(tree);
    }

    // Resize handling
    window.addEventListener('resize', onWindowResize, false);

    // Start overlay interactions
    overlay.addEventListener('pointerdown', () => startDemo(), { passive: true });
    window.addEventListener('keydown', (e) => { if (!started) startDemo(); }, { passive: true });
  }

  function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  }

  // Animation loop
  function loop() {
    // We use requestAnimationFrame in this module-based setup
    // The actual rendering happens in this function driven by loop calls
  }

  // A real loop with requestAnimationFrame, separate from "loop" to ensure render when needed
  (function startLoop() {
    // Use a self-contained render loop
    let raf;
    const tick = () => {
      // if overlay is visible, we still render faintly? We'll keep rendering minimal when not started.
      renderStep();
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
  })();

  function renderStep() {
    // Time-based updates
    const t = performance.now() * 0.001;

    // Flames flicker
    for (let i = 0; i < flameMeshes.length; i++) {
      const m = flameMeshes[i];
      const flicker = 0.85 + Math.sin(t * (1.0 + i * 0.25)) * 0.25;
      m.scale.y = flicker;
      m.material.emissiveIntensity = 0.8 + Math.abs(Math.sin(t * (2.0 + i * 0.4))) * 0.6;
      m.position.x = Math.sin(t * (2.0 + i * 0.3)) * 0.05;
      m.position.z = Math.cos(t * (2.0 + i * 0.3)) * 0.05;
    }

    // Sparks update
    if (sparks && sparks.update) sparks.update(t);

    // Moon drift
    if (moon) {
      moon.position.x = -60 + Math.sin(t * 0.07) * 6;
      moon.position.y = 60 + Math.cos(t * 0.05) * 6;
    }

    // Stars slight motion
    if (stars && stars.points) stars.points.rotation.y = t * 0.02;

    // Lighting flicker
    fireLight.intensity = 2.0 + Math.sin(t * 10.0) * 0.6;
    // controls update
    if (controls.enabled) controls.update();

    // Render
    renderer.render(scene, camera);
  }

  // Start the demo: enable controls and auto-rotate
  function startDemo() {
    if (started) return;
    started = true;
    overlay.classList.add('hidden');
    overlay.style.display = 'none';
    controls.enabled = true;
    // Reset initial view to a pleasant angle
    camera.position.set(0, 6, 16);
    controls.target.set(0, 0, 0);
  }

  // Helper constructors

  function createRock(size, y, height, color) {
    // Low-poly rock using an icosahedron
    const geom = new THREE.IcosahedronGeometry(THREE.MathUtils.randFloat(0.25, 0.55) * size, 0);
    const mat = new THREE.MeshStandardMaterial({ color: color || 0x6a6a6a, roughness: 1.0 });
    const r = new THREE.Mesh(geom, mat);
    r.scale.set(size, height, size);
    r.position.y = y;
    r.rotation.set(0, Math.random() * Math.PI, Math.random() * Math.PI);
    r.castShadow = true;
    r.receiveShadow = true;
    return r;
  }

  function createLog(length) {
    const geo = new THREE.CylinderGeometry(0.12, 0.18, length, 12);
    const mat = new THREE.MeshStandardMaterial({ color: 0x7b4a2d, roughness: 0.8 });
    const log = new THREE.Mesh(geo, mat);
    log.rotation.x = Math.PI / 2;
    log.castShadow = true;
    log.receiveShadow = true;
    return log;
  }

  function createMoon() {
    const r = 6;
    const geo = new THREE.SphereGeometry(r, 32, 16);
    const mat = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.6, emissive: 0xffffff, emissiveIntensity: 0.2 });
    const moonMesh = new THREE.Mesh(geo, mat);
    moonMesh.position.set(-60, 60, -200);
    moonMesh.castShadow = false;
    moonMesh.receiveShadow = false;
    return moonMesh;
  }

  function createStars(count) {
    const starGeo = new THREE.BufferGeometry();
    const positions = new Float32Array(count * 3);
    for (let i = 0; i < count; i++) {
      const x = THREE.MathUtils.randFloatSpread(1800);
      const y = THREE.MathUtils.randFloat(80, 600);
      const z = THREE.MathUtils.randFloatSpread(1800);
      positions[i * 3 + 0] = x;
      positions[i * 3 + 1] = y;
      positions[i * 3 + 2] = z;
    }
    starGeo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
    const starMat = new THREE.PointsMaterial({ color: 0xffffff, size: 1.2, sizeAttenuation: true, transparent: true, opacity: 0.95 });
    const starsPoints = new THREE.Points(starGeo, starMat);
    starsPoints.frustumCulled = false;
    return { points: starsPoints, geometry: starGeo, material: starMat };
  }

  function createSparks(count) {
    const geom = new THREE.BufferGeometry();
    const positions = new Float32Array(count * 3);
    const lifetimes = new Float32Array(count);
    for (let i = 0; i < count; i++) {
      positions[i * 3 + 0] = (Math.random() * 0.6) - 0.3;
      positions[i * 3 + 1] = Math.random() * 0.5 + 0.2;
      positions[i * 3 + 2] = (Math.random() * 0.6) - 0.3;
      lifetimes[i] = Math.random() * 1.2 + 0.4;
    }
    geom.setAttribute('position', new THREE.BufferAttribute(positions, 3));
    const mat = new THREE.PointsMaterial({ color: 0xfff6a8, size: 0.6, sizeAttenuation: true, transparent: true, opacity: 0.95 });
    const pts = new THREE.Points(geom, mat);
    pts.frustumCulled = false;

    function update(t) {
      const pos = geom.attributes.position.array;
      for (let i = 0; i < count; i++) {
        const idx = i * 3;
        // simple velocity
        pos[idx + 0] += (Math.random() - 0.5) * 0.02;
        pos[idx + 1] += 0.006 + Math.random() * 0.01;
        pos[idx + 2] += (Math.random() - 0.5) * 0.02;

        lifetimes[i] -= 0.008;
        if (lifetimes[i] <= 0.0) {
          pos[idx + 0] = (Math.random() * 0.6) - 0.3;
          pos[idx + 1] = Math.random() * 0.5 + 0.2;
          pos[idx + 2] = (Math.random() * 0.6) - 0.3;
          lifetimes[i] = Math.random() * 1.0 + 0.5;
        }
      }
      geom.attributes.position.needsUpdate = true;
    }

    return { points: pts, update: update };
  }

  function makeLowPolyTree() {
    const group = new THREE.Group();

    // trunk
    const trunkGeom = new THREE.BoxGeometry(0.4, 2.0, 0.4);
    const trunkMat = new THREE.MeshStandardMaterial({ color: 0x8b5a2b, roughness: 1.0 });
    const trunk = new THREE.Mesh(trunkGeom, trunkMat);
    trunk.position.y = 1.0;
    trunk.castShadow = true;
    trunk.receiveShadow = true;
    group.add(trunk);

    // foliage: stacked low-poly cones
    const greens = [0x2e8b57, 0x2a9d54, 0x1f7a3a];
    for (let i = 0; i < 3; i++) {
      const r = 1.6 - i * 0.4;
      const coneGeom = new THREE.ConeGeometry(r, 0.9 + i * 0.35, 6);
      const coneMat = new THREE.MeshStandardMaterial({ color: greens[i], roughness: 1.0 });
      const cone = new THREE.Mesh(coneGeom, coneMat);
      cone.position.y = 1.0 + i * 0.9;
      cone.rotation.y = Math.random() * Math.PI;
      cone.castShadow = true;
      cone.receiveShadow = true;
      group.add(cone);
    }

    return group;
  }

  </script>
</body>
</html>
Reasoning trace (17,216 tokens)
**Exploring demo implementation**

I need to consider how the self-playing demo mode will function, specifically regarding user interaction. In previous tasks, the "start" message disappears after a click, leading to automatic animations. I want to implement the user’s camera controls while starting in a "pre-start" state, but the orbit controls should remain disabled until the demo actually begins. Also, according to the requirement, we shouldn't depend on keyboard inputs to control the camera. Instead, it might be better to have an "auto-rotation" feature or an AI that moves the camera.**Designing the demo mode**

The "self-playing demo mode" will start with a message prompting the user to click or press a key. Once activated, the scene will incorporate some simple AI, like a camera that smoothly pans around a campfire or animated flames driven by a script. My usual plan includes OrbitControls, which I'll disable at first and then enable after the start. The AI could allow for a subtle orbit around the campfire or continuous movement at a fixed angle. I might even consider implementing an autopilot feature that doesn't require keyboard input.**Implementing camera controls**

The requirement specifies using "OrbitControls" to look around, ensuring the camera remains above the ground. My initial approach is to disable these controls at the start and enable them after launching the demo. I want to add an autopilot feature that orbits slowly around the campfire with settings like `controls.autoRotate = true` and a speed of `0.5`. I’m thinking of introducing a "paused" variable to control whether the orbit controls are disabled or enabled. Additionally, I should implement a small AI function to adjust the camera's orbit angle around the campfire, but I need to be mindful of potential conflicts with damping settings in OrbitControls.**Creating a simple autopilot**

I’m thinking the simplest solution for the camera would be to implement my own basic autopilot that adjusts the camera’s position along a circular path around the campfire. I won't rely on OrbitControls for this autopilot; instead, I’ll just keep those controls enabled so users can still look around. However, the autopilot will need to continuously update the camera's position, ensuring a smooth movement around the campfire while providing an engaging experience for the user.**Designing the demo and environment**

I need to pay careful attention to how OrbitControls manage camera interactions, making sure the autopilot smoothly updates the camera position around the campfire. I’ll implement this using a PerspectiveCamera on a circular path and gradually adjust `controls.target`. To ensure user interaction, I’ll display a "Click to start" overlay and initialize the demo upon user interaction. The scene will include low-poly rocks and logs made from BoxGeometry or IcosahedronGeometry, an animated flame using a particle effect, and stones forming a ring around the campfire.**Building the scene and features**

I plan to create an engaging environment featuring dynamic elements like a campfire with animated flames using ConeGeometry and PointLight for warm light. I’ll animate sparks with a particle system as they rise, plus design a night sky with stars and a moon using PointsGeometry. The trees will be low-poly, randomly positioned around the campfire within a 25-unit radius. I’ll also set up the ground with a rough PlaneGeometry and implement proper shadow casting. The scene will adapt well to different screen sizes, ensuring a dramatic first impression. I'll include logging and overall environmental features to enhance the visual experience.**Embedding the importmap and setting up the code**

I need to set up the importmap to load Three.js and its addons, particularly for OrbitControls. I’ll ensure that the import paths align correctly with the CDN. I'll focus on using `import { OrbitControls } from 'three/addons/controls/OrbitControls.js'` since GLTFLoader is less essential. 

Next, I’ll create a full-screen overlay prompting users to click to start. The overlay should appear initially but still allow something meaningful to be visible beneath it. I plan to set up the scene with fog and a camera, making sure the code runs smoothly without errors and shows a nice ground texture.**Creating campfire scene elements**

I’m working on the campfire scene. First, I’ll create triangular rocks and logs with appropriate colors, cast shadows, and arrange them around the campfire. I’ll set up a flame group with cone geometries for flames, adding a point light for illumination and particles for sparks.

For trees, I’ll implement a function to generate low-poly versions, ensuring they’re placed randomly but not too close to the fire. The moon will be a glowing sphere, and I plan to create a star field using points. Finally, I need to set up an overlay for interaction starting with click events.**Setting up CSS and scene**

I’m crafting the CSS for the scene, including full height, a responsive overlay, and styled elements for text. Then, I’ll set up the Three.js scene with fog and different light types, including ambient and point lights. For the ground, I'll create a large plane, and for decorative elements like rocks and logs, I’m implementing functions to generate them with random scales and positions.

I’ll also detail the campfire setup with flames and sparks, ensuring they have shadow effects for realism. A bit of extra work goes into making the sparks dynamic.**Updating velocity and environment**

I'm planning to apply velocity updates using arrays for position and lifetimes. When lifetimes go below zero, I'll reset positions near the campfire. I need to add a "stars" group with a buffer geometry for star positions, making sure they're set to a reasonable distance.

Then, I’ll create a moon and set up fog for ambiance. I'll implement an animation function to manage updates and light interactions. To enhance realism, I'll have the flames flicker over time, adding some nice visual dynamics to the scene!**Setting up the scene**

I'm creating a ground material for the scene, using a dark color and adding some random height variations for realism. The campfire group will be central, with rocks arranged in a circle and several logs placed around. I'll also add a flame group made of conical shapes with an emissive glow for effect, featuring flicker and sway animation. Lastly, I’ll include trees, stars, and a moon to complete the environment and add depth, ensuring everything casts shadows for a more immersive look!**Creating spark effects**

I'm defining a function to create sparks that will enhance the campfire scene. It will generate random positions around the fire and give each spark a specific lifetime. I need to use a Three.js PointsMaterial for these sparks, which will allow a nice visual effect, while ensuring they're translucent and properly sized. I’ll also implement an update function to animate these sparks, making them rise and eventually respawn to keep the effect dynamic and visually appealing. Finally, I want to ensure the spark function is defined correctly in relation to other functions!**Structuring function declarations**

I’m planning to implement all function declarations before their initial usage to avoid any issues in my code. This involves defining functions like createRock, createLog, and createMoon at the top. For example, createRock will take parameters such as size, y, height, and color. It's essential to ensure these declarations are accessible so they can be called later without causing scope errors. By organizing the code this way, I can maintain clarity and consistency while implementing the visual aspects of my scene.**Setting up the HTML structure**

I'm starting the HTML document to create a low-poly campfire scene in a forest clearing. It's designed to be responsive and engaging, with a dark background to enhance the visual elements. The overlay for user interaction prompts will be fixed at the center, inviting users to click or press any key to start the demo. I'm planning to utilize Three.js for the 3D visuals, and I’ll properly structure the CSS to ensure everything displays nicely across different devices. Let's keep everything clear and organized!**Building the 3D scene**

I'm establishing the 3D environment using Three.js, including a night sky with fog and a comprehensive setup for the campfire scene. I’m creating diverse elements like ambient and directional lighting, a ground plane with texture, and a ring of rocks for the campfire. I’m also adding logs, flames, a particle system for sparks, and incorporating the moon and stars. It’s crucial to ensure everything interacts nicely with shadows and responding to screen resizing as needed. Let's keep it visually engaging and functional!**Finalizing the code structure**

I'm wrapping up the code, but I noticed I’ve used the 'fireLight' and 'sparks' variables in the 'animate' function without declaring them first. It's clear I need to restructure this to ensure everything compiles correctly. I also need to define the various functions like 'createRock' and 'createLog.' I'll declare these global variables and implement the necessary functions to maintain consistency throughout the code. Once that’s established, it'll be time to write and review the complete HTML code!