GPT-6 Luna
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 2 min 56 s
- Cost
- $0.0074
- Output tokens
- 14,675
- Spent thinking
- 7,967 tokens
- Speed
- 89.7 tok/s
- Console errors
- 0
- First token after
- 12.3 s
- First visible text after
- 1 min 58 s
- Prompt tokens
- 555
- Code size
- 19.3 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-6-luna- Reasoning sent
{"effort":"high"}- 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 · 492 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Northwood — Campfire at Night</title>
<style>
:root {
color-scheme: dark;
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
* { box-sizing: border-box; }
html, body { width: 100%; height: 100%; margin: 0; overflow: hidden; background: #07111b; }
canvas { display: block; width: 100%; height: 100%; }
.hud {
position: fixed;
z-index: 2;
color: #e8e3d8;
pointer-events: none;
text-shadow: 0 2px 12px rgba(0,0,0,.55);
}
.brand { top: 28px; left: 32px; display: flex; align-items: center; gap: 13px; }
.mark {
width: 33px; height: 33px; border: 1px solid rgba(230,204,157,.55);
border-radius: 50%; display: grid; place-items: center; color: #e5a45a;
font-size: 17px; line-height: 1; background: rgba(10,18,25,.26);
}
.brand h1 { margin: 0; font-size: 13px; letter-spacing: .22em; font-weight: 650; }
.brand p { margin: 5px 0 0; color: rgba(226,224,215,.58); font-size: 10px; letter-spacing: .19em; }
.night {
top: 33px; right: 34px; color: rgba(230,233,236,.68);
font-size: 10px; letter-spacing: .18em; text-align: right;
}
.night strong { display: block; margin-bottom: 6px; color: #e3d6b8; font-size: 11px; font-weight: 500; }
.hint {
right: 30px; bottom: 25px; display: flex; align-items: center; gap: 10px;
color: rgba(235,235,228,.58); font-size: 10px; letter-spacing: .14em;
}
.mouse {
width: 17px; height: 24px; border: 1px solid rgba(235,235,228,.48);
border-radius: 10px; position: relative;
}
.mouse::after {
content: ""; position: absolute; top: 4px; left: 7px; width: 2px; height: 5px;
border-radius: 2px; background: #e6a85f; opacity: .9;
}
.footer {
position: fixed; left: 32px; bottom: 30px; color: rgba(235,235,228,.38);
font-size: 9px; letter-spacing: .21em; pointer-events: none;
}
@media (max-width: 600px) {
.brand { top: 19px; left: 18px; gap: 9px; }
.mark { width: 29px; height: 29px; }
.brand h1 { font-size: 11px; }
.brand p { font-size: 8px; letter-spacing: .13em; }
.night { top: 23px; right: 17px; font-size: 8px; letter-spacing: .12em; }
.night strong { font-size: 9px; }
.footer { left: 18px; bottom: 20px; font-size: 8px; }
.hint { right: 17px; bottom: 17px; font-size: 8px; gap: 7px; }
.mouse { width: 14px; height: 20px; }
.mouse::after { left: 5px; }
}
</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 class="hud brand">
<div class="mark">✳</div>
<div>
<h1>NORTHWOOD</h1>
<p>AN OPEN-AIR RETREAT</p>
</div>
</div>
<div class="hud night"><strong>✦ AFTER DARK</strong>DEEP IN THE PINES</div>
<div class="footer">A QUIET PLACE TO GATHER</div>
<div class="hud hint"><span class="mouse"></span><span>DRAG TO LOOK · SCROLL TO ZOOM</span></div>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x07111b);
scene.fog = new THREE.Fog(0x08121b, 24, 78);
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 180);
camera.position.set(14, 11, 18);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.22;
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 1.15, 0);
controls.enableDamping = true;
controls.dampingFactor = 0.055;
controls.minDistance = 8.5;
controls.maxDistance = 43;
controls.minPolarAngle = 0.18;
controls.maxPolarAngle = 1.48;
controls.update();
const ambient = new THREE.AmbientLight(0x8492ad, 0.39);
scene.add(ambient);
const hemi = new THREE.HemisphereLight(0x7488ad, 0x261b12, 0.66);
scene.add(hemi);
const moonFill = new THREE.DirectionalLight(0x7386b5, 0.42);
moonFill.position.set(-22, 30, -14);
scene.add(moonFill);
const ground = new THREE.Mesh(
new THREE.PlaneGeometry(200, 200),
new THREE.MeshStandardMaterial({ color: 0x18201b, roughness: 1, metalness: 0 })
);
ground.rotation.x = -Math.PI / 2;
ground.position.y = -0.12;
ground.receiveShadow = true;
scene.add(ground);
const clearing = new THREE.Mesh(
new THREE.CircleGeometry(8.8, 64),
new THREE.MeshStandardMaterial({ color: 0x373329, roughness: 1, metalness: 0 })
);
clearing.rotation.x = -Math.PI / 2;
clearing.position.y = -0.092;
clearing.receiveShadow = true;
scene.add(clearing);
// A soft, custom radial glow used for the moon and the fire.
function makeGlowTexture() {
const canvas = document.createElement('canvas');
canvas.width = canvas.height = 128;
const ctx = canvas.getContext('2d');
const gradient = ctx.createRadialGradient(64, 64, 2, 64, 64, 64);
gradient.addColorStop(0, 'rgba(255,255,255,0.88)');
gradient.addColorStop(0.16, 'rgba(255,231,188,0.48)');
gradient.addColorStop(0.42, 'rgba(255,169,83,0.15)');
gradient.addColorStop(1, 'rgba(255,128,50,0)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 128, 128);
return new THREE.CanvasTexture(canvas);
}
const glowTexture = makeGlowTexture();
// Stars on a high, distant hemisphere.
let seed = 71239;
function random() {
seed = (seed * 1664525 + 1013904223) >>> 0;
return seed / 4294967296;
}
const starPositions = [];
const starColors = [];
for (let i = 0; i < 430; i++) {
const y = 0.045 + random() * 0.94;
const angle = random() * Math.PI * 2;
const horizontal = Math.sqrt(1 - y * y);
const r = 82 + random() * 9;
starPositions.push(Math.cos(angle) * horizontal * r, y * r, Math.sin(angle) * horizontal * r);
const tint = 0.72 + random() * 0.28;
starColors.push(tint * 0.78, tint * 0.88, tint);
}
const starGeometry = new THREE.BufferGeometry();
starGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starPositions, 3));
starGeometry.setAttribute('color', new THREE.Float32BufferAttribute(starColors, 3));
const stars = new THREE.Points(starGeometry, new THREE.PointsMaterial({
size: 1.35, sizeAttenuation: false, vertexColors: true,
transparent: true, opacity: 0.83, depthWrite: false, fog: false
}));
scene.add(stars);
// Moon and a few subtle low-poly craters.
const moonPosition = new THREE.Vector3(-45, 27, -25);
const moonGroup = new THREE.Group();
moonGroup.position.copy(moonPosition);
moonGroup.quaternion.setFromUnitVectors(
new THREE.Vector3(0, 0, 1),
camera.position.clone().sub(moonPosition).normalize()
);
scene.add(moonGroup);
const moon = new THREE.Mesh(
new THREE.SphereGeometry(2.05, 14, 10),
new THREE.MeshBasicMaterial({ color: 0xdce5e4, fog: false })
);
moonGroup.add(moon);
const craterMat = new THREE.MeshBasicMaterial({ color: 0xaebdc0, fog: false });
for (const [x, y, s] of [[-0.55, 0.37, 0.2], [0.45, 0.62, 0.13], [0.61, -0.3, 0.17], [-0.18, -0.55, 0.12]]) {
const crater = new THREE.Mesh(new THREE.SphereGeometry(s, 7, 5), craterMat);
crater.position.set(x, y, 1.98);
crater.scale.z = 0.23;
moonGroup.add(crater);
}
const moonHalo = new THREE.Sprite(new THREE.SpriteMaterial({
map: glowTexture, color: 0x9bb9e0, transparent: true, opacity: 0.42,
blending: THREE.AdditiveBlending, depthWrite: false, fog: false
}));
moonHalo.position.copy(moonPosition);
moonHalo.scale.set(14, 14, 1);
scene.add(moonHalo);
// Low-poly trees, scattered around (and well outside) the clearing.
const trunkGeometry = new THREE.CylinderGeometry(0.16, 0.25, 1, 7, 1);
const crownGeometry = new THREE.ConeGeometry(1, 1, 7, 1);
const trunkMats = [0x49382a, 0x594331, 0x40342a, 0x624a34].map(c =>
new THREE.MeshStandardMaterial({ color: c, roughness: 1, flatShading: true })
);
const leafMats = [0x142a27, 0x1b342e, 0x203b31, 0x273e30, 0x1c3029].map(c =>
new THREE.MeshStandardMaterial({ color: c, roughness: 1, flatShading: true })
);
for (let i = 0; i < 78; i++) {
const angle = random() * Math.PI * 2;
const radius = 13 + Math.sqrt(random()) * 30;
const x = Math.cos(angle) * radius;
const z = Math.sin(angle) * radius;
const height = 5.0 + random() * 4.5;
const trunkH = height * (0.34 + random() * 0.08);
const trunk = new THREE.Mesh(trunkGeometry, trunkMats[Math.floor(random() * trunkMats.length)]);
trunk.position.set(x, trunkH / 2, z);
trunk.scale.set(0.75 + random() * 0.35, trunkH, 0.75 + random() * 0.35);
trunk.castShadow = true;
trunk.receiveShadow = true;
scene.add(trunk);
const crownLayers = [
{ y: height * 0.38, r: height * 0.205, h: height * 0.43 },
{ y: height * 0.55, r: height * 0.165, h: height * 0.40 },
{ y: height * 0.72, r: height * 0.125, h: height * 0.34 }
];
for (const layer of crownLayers) {
const crown = new THREE.Mesh(crownGeometry, leafMats[Math.floor(random() * leafMats.length)]);
const scale = 0.85 + random() * 0.28;
crown.position.set(x, layer.y, z);
crown.scale.set(layer.r * scale, layer.h * scale, layer.r * scale);
crown.rotation.y = random() * Math.PI;
crown.castShadow = true;
crown.receiveShadow = true;
scene.add(crown);
}
}
// Scattered angular boulders around the edge of the clearing.
const rockGeometry = new THREE.DodecahedronGeometry(0.72, 0);
const rockMats = [0x55534b, 0x625f55, 0x494b47, 0x71685a].map(c =>
new THREE.MeshStandardMaterial({ color: c, roughness: 1, flatShading: true })
);
for (let i = 0; i < 15; i++) {
const a = random() * Math.PI * 2;
const r = 4.8 + random() * 4.4;
const rock = new THREE.Mesh(rockGeometry, rockMats[Math.floor(random() * rockMats.length)]);
rock.position.set(Math.cos(a) * r, 0.12 + random() * 0.15, Math.sin(a) * r);
rock.scale.set(0.45 + random() * 0.85, 0.36 + random() * 0.5, 0.42 + random() * 0.78);
rock.rotation.set(random() * 0.8, random() * Math.PI, random() * 0.8);
rock.castShadow = true;
rock.receiveShadow = true;
scene.add(rock);
}
// A broken ring of small stones around the fire.
for (let i = 0; i < 11; i++) {
const a = (i / 11) * Math.PI * 2 + 0.12;
const radius = 1.26 + (random() - 0.5) * 0.18;
const stone = new THREE.Mesh(rockGeometry, rockMats[(i + 1) % rockMats.length]);
stone.position.set(Math.cos(a) * radius, 0.15, Math.sin(a) * radius);
stone.scale.set(0.36 + random() * 0.18, 0.25 + random() * 0.17, 0.35 + random() * 0.19);
stone.rotation.set(random() * 0.6, random() * Math.PI, random() * 0.6);
stone.castShadow = true;
stone.receiveShadow = true;
scene.add(stone);
}
// Three rough-hewn logs crossed at the heart of the fire.
const barkMat = new THREE.MeshStandardMaterial({ color: 0x54351f, roughness: 1, flatShading: true });
const cutMat = new THREE.MeshStandardMaterial({ color: 0x96704a, roughness: 1, flatShading: true });
function addLog(x1, z1, x2, z2, y, radius) {
const start = new THREE.Vector3(x1, y, z1);
const end = new THREE.Vector3(x2, y, z2);
const axis = end.clone().sub(start);
const length = axis.length();
const quaternion = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), axis.clone().normalize());
const log = new THREE.Mesh(new THREE.CylinderGeometry(radius * 0.86, radius, length, 7, 1), barkMat);
log.position.copy(start).add(end).multiplyScalar(0.5);
log.quaternion.copy(quaternion);
log.castShadow = true;
log.receiveShadow = true;
scene.add(log);
const capGeometry = new THREE.CylinderGeometry(radius * 0.77, radius * 0.77, 0.035, 7, 1);
for (const point of [start, end]) {
const cap = new THREE.Mesh(capGeometry, cutMat);
cap.position.copy(point);
cap.quaternion.copy(quaternion);
cap.castShadow = true;
scene.add(cap);
}
}
addLog(-1.85, 0, 1.85, 0, 0.21, 0.28);
addLog(0, -1.7, 0, 1.7, 0.26, 0.27);
addLog(-1.45, -1.35, 1.45, 1.35, 0.32, 0.23);
// Faceted, gently waving flame tongues.
function flameGeometry(radius, height, segments = 7) {
const positions = [];
const indices = [];
const rings = [
{ y: 0, r: radius, x: 0, z: 0 },
{ y: height * 0.40, r: radius * 0.72, x: radius * 0.19, z: -radius * 0.1 },
{ y: height * 0.77, r: radius * 0.34, x: -radius * 0.09, z: radius * 0.13 }
];
for (let ri = 0; ri < rings.length; ri++) {
const ring = rings[ri];
for (let j = 0; j < segments; j++) {
const a = (j / segments) * Math.PI * 2 + ri * 0.19;
const wobble = 0.88 + 0.12 * Math.sin(j * 4.7 + ri);
positions.push(ring.x + Math.cos(a) * ring.r * wobble, ring.y, ring.z + Math.sin(a) * ring.r * wobble);
}
}
const tip = positions.length / 3;
positions.push(radius * 0.08, height, -radius * 0.12);
for (let ri = 0; ri < rings.length - 1; ri++) {
for (let j = 0; j < segments; j++) {
const a = ri * segments + j;
const b = ri * segments + (j + 1) % segments;
const c = (ri + 1) * segments + j;
const d = (ri + 1) * segments + (j + 1) % segments;
indices.push(a, c, b, b, c, d);
}
}
const base = (rings.length - 1) * segments;
for (let j = 0; j < segments; j++) indices.push(base + j, tip, base + (j + 1) % segments);
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.setIndex(indices);
geometry.computeVertexNormals();
return geometry;
}
const flameOrange = new THREE.MeshStandardMaterial({
color: 0xff7020, emissive: 0xff3908, emissiveIntensity: 1.65,
roughness: 0.8, flatShading: true, side: THREE.DoubleSide
});
const flameGold = new THREE.MeshStandardMaterial({
color: 0xffc04b, emissive: 0xff8a18, emissiveIntensity: 1.9,
roughness: 0.75, flatShading: true, side: THREE.DoubleSide
});
const flameCore = new THREE.MeshStandardMaterial({
color: 0xffe28a, emissive: 0xffb12d, emissiveIntensity: 2.1,
roughness: 0.7, flatShading: true, side: THREE.DoubleSide
});
const flameSpecs = [
{ x: 0, z: 0, r: 0.53, h: 2.25, material: flameOrange, phase: 0.2 },
{ x: -0.3, z: 0.12, r: 0.39, h: 1.75, material: flameOrange, phase: 1.7 },
{ x: 0.34, z: 0.05, r: 0.37, h: 1.86, material: flameOrange, phase: 3.2 },
{ x: 0.04, z: -0.32, r: 0.35, h: 1.62, material: flameOrange, phase: 4.5 },
{ x: -0.11, z: 0.02, r: 0.31, h: 1.58, material: flameGold, phase: 2.4 },
{ x: 0.22, z: -0.06, r: 0.24, h: 1.25, material: flameGold, phase: 5.1 },
{ x: -0.24, z: -0.1, r: 0.20, h: 1.08, material: flameCore, phase: 0.9 }
];
const flames = [];
for (const spec of flameSpecs) {
const mesh = new THREE.Mesh(flameGeometry(spec.r, spec.h), spec.material);
mesh.position.set(spec.x, 0.34, spec.z);
mesh.castShadow = true;
mesh.userData = spec;
scene.add(mesh);
flames.push(mesh);
}
const fireLight = new THREE.PointLight(0xff8a3c, 62, 24, 2);
fireLight.position.set(0, 1.45, 0);
fireLight.castShadow = true;
fireLight.shadow.mapSize.set(1024, 1024);
fireLight.shadow.camera.near = 0.35;
fireLight.shadow.camera.far = 25;
fireLight.shadow.bias = -0.00045;
fireLight.shadow.radius = 4;
scene.add(fireLight);
const fireGlow = new THREE.Sprite(new THREE.SpriteMaterial({
map: glowTexture, color: 0xff8a39, transparent: true, opacity: 0.34,
blending: THREE.AdditiveBlending, depthWrite: false, fog: false
}));
fireGlow.position.set(0, 1.05, 0);
fireGlow.scale.set(8.5, 8.5, 1);
scene.add(fireGlow);
// Rising ember particles with animated positions and warm vertex colors.
const particleCount = 68;
const particlePositions = new Float32Array(particleCount * 3);
const particleColors = new Float32Array(particleCount * 3);
const particles = [];
function resetParticle(p, initial = false) {
p.x = (random() - 0.5) * 0.7;
p.z = (random() - 0.5) * 0.7;
p.y = initial ? 0.7 + random() * 2.2 : 0.62;
p.vx = (random() - 0.5) * 0.36;
p.vz = (random() - 0.5) * 0.36;
p.vy = 0.65 + random() * 1.45;
p.duration = 0.85 + random() * 1.55;
p.life = initial ? random() * p.duration : 0;
}
for (let i = 0; i < particleCount; i++) {
const p = {};
resetParticle(p, true);
particles.push(p);
}
const particleGeometry = new THREE.BufferGeometry();
particleGeometry.setAttribute('position', new THREE.BufferAttribute(particlePositions, 3).setUsage(THREE.DynamicDrawUsage));
particleGeometry.setAttribute('color', new THREE.BufferAttribute(particleColors, 3).setUsage(THREE.DynamicDrawUsage));
const embers = new THREE.Points(particleGeometry, new THREE.PointsMaterial({
size: 0.115, sizeAttenuation: true, vertexColors: true,
transparent: true, opacity: 0.95, depthWrite: false
}));
scene.add(embers);
// A little ground texture: sparse clumps of stylized grass around the clearing.
const grassGeo = new THREE.ConeGeometry(0.095, 0.34, 4, 1);
const grassMats = [0x263329, 0x33402e, 0x3d402e].map(c =>
new THREE.MeshStandardMaterial({ color: c, roughness: 1, flatShading: true })
);
for (let i = 0; i < 52; i++) {
const a = random() * Math.PI * 2;
const r = 4.2 + random() * 5.5;
const x = Math.cos(a) * r, z = Math.sin(a) * r;
for (let j = 0; j < 3; j++) {
const blade = new THREE.Mesh(grassGeo, grassMats[Math.floor(random() * grassMats.length)]);
blade.position.set(x + (random() - 0.5) * 0.28, 0.13, z + (random() - 0.5) * 0.28);
blade.rotation.z = (random() - 0.5) * 0.38;
blade.rotation.y = random() * Math.PI;
blade.scale.setScalar(0.6 + random() * 0.75);
blade.castShadow = true;
scene.add(blade);
}
}
const clock = new THREE.Clock();
let elapsed = 0;
function animate() {
requestAnimationFrame(animate);
const dt = Math.min(clock.getDelta(), 0.04);
elapsed += dt;
flames.forEach((flame, i) => {
const s = flame.userData;
const wave = Math.sin(elapsed * (5.2 + i * 0.31) + s.phase);
const flutter = Math.sin(elapsed * 8.1 + s.phase * 1.7);
flame.scale.set(0.94 + wave * 0.08, 0.86 + wave * 0.14 + flutter * 0.05, 0.94 + flutter * 0.08);
flame.rotation.y = Math.sin(elapsed * 1.7 + s.phase) * 0.18;
flame.position.x = s.x + Math.sin(elapsed * 2.7 + s.phase) * 0.025;
flame.position.z = s.z + Math.cos(elapsed * 2.1 + s.phase) * 0.025;
});
const flicker = Math.sin(elapsed * 10.5) * 4 + Math.sin(elapsed * 17.3 + 1.4) * 2.6 + Math.sin(elapsed * 4.1) * 3.2;
fireLight.intensity = 57 + flicker;
fireLight.position.y = 1.42 + Math.sin(elapsed * 6.2) * 0.055;
fireGlow.material.opacity = 0.29 + 0.045 * Math.sin(elapsed * 7.1);
for (let i = 0; i < particleCount; i++) {
const p = particles[i];
p.life += dt;
if (p.life >= p.duration) resetParticle(p);
p.x += p.vx * dt;
p.z += p.vz * dt;
p.y += p.vy * dt;
const fade = Math.max(0, 1 - p.life / p.duration);
particlePositions[i * 3] = p.x;
particlePositions[i * 3 + 1] = p.y;
particlePositions[i * 3 + 2] = p.z;
particleColors[i * 3] = fade;
particleColors[i * 3 + 1] = fade * 0.48;
particleColors[i * 3 + 2] = fade * 0.11;
}
particleGeometry.attributes.position.needsUpdate = true;
particleGeometry.attributes.color.needsUpdate = true;
controls.update();
renderer.render(scene, camera);
}
animate();
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
});
</script>
</body>
</html>Reasoning trace (7,967 tokens)
**Building a low-poly scene** I’m working on creating an HTML import map for Three.js that looks polished and visually appealing. The goal isn’t to make a game, but a self-playing scene. I need an iframe that’s 1280x800, where I’ll build a low-poly landscape with a clearing and trees around it. The camera should target a campfire in the center. I’ll include animated flames and a tasteful UI overlay, ensuring a fitting atmosphere with fog, stars, and a moon.**Animating the scene details** I’m focusing on animating the flames by adjusting their scale and rotation. The flame shapes will consist of custom, irregular tapered rings. I’ll use ConeGeometry with six segments for dynamic scaling and color them with outer orange and inner golden hues. I also want to incorporate flickering point lights that cast shadows. For the trees, I’m visualizing 50 low-poly variations with a large ground plane. To add depth, I'll include rocks, logs, and stars in the sky, along with an emissive moon.**Configuring the scene view** I need to set up the camera looking from the positive z-axis toward the center. For the background, I won’t use a sky dome but will incorporate fog and distribute stars as points within a large sphere. These points might get affected by the fog, so I’ll use PointsMaterial with depthWrite set to false. I’ll create a moon as a sphere with an emissive halo, ensuring it's visible at a controlled distance relative to the camera.**Arranging a clearing** I'm thinking about how to arrange logs around a campfire. I want to position the logs in a circular pattern, maybe at angles like 45 and 135 degrees. They'll create a cylinder around the fire, which I'll visualize in a low-poly format. I’m also considering placing stones in a ring and adding trees at varied positions within a radius, ensuring enough density for a forest-like effect. I'll need random positioning and a consistent seed for randomness. I'm excited about how it will look!**Creating a foggy atmosphere** I’m working on the fog settings, aiming for an exponential density of .025. For the fog color, I’ll use a dark tone to create depth. I also want to set up ambient and hemispherical lighting, ensuring the campfire remains clear and bright. Using an emissive map for the flames sounds great! I might explore ways to generate a warm glow effect. Flickering flame animations will add realism, so I’ll customize cone geometries for individual flames. Let's see how it all comes together!**Exploring particle system options** I’m considering using little tetrahedron meshes for a particle system, which would be about 25 in movement. They’re kind of expensive, but I think they’ll work well. I want to focus on point geometry with dynamic attributes. Color variations could come from vertex coloring, with size adjustments as needed. I’m mulling over how to approach twinkling effects and visibility, possibly tweaking opacity based on point age. Keeping things lively and dynamic seems key!**Generating forest clearing** I'm considering creating zones with a radius from 10 to 42, using a camera to see depth and fog in the forest around a clearing. This clearing will have a size of around radius 9, and I aim to place at least 30 trees randomly. I'll generate these positions using a random distribution while ensuring no trees are too close to the center—specifically, keeping them at least 10.5 away. I’m thinking about having around 82 trees, with some positioned behind others.**Calculating moon vector** I’m examining the camera’s perspective towards the center and using the angular side for the moon. I calculate the dot product between the right vector of the camera, which is about (.79, 0, -.61), and the normalized moon vector of (-.65, .45, -.7). The result is approximately -.08, indicating a position that's near the center. It seems like a straightforward calculation, but I always want to ensure I’m interpreting these vectors accurately!**Optimizing star placement** I'm calculating the moon's position and confirming its vector alignment. The camera's direction shows good visibility above the center. Now, I've got to position stars within a world sphere radius of 90, avoiding ones below the horizon. I'll focus on placing stars in random positions above a certain height. Since the camera is at (14,11,18), I'm considering their relative positions while ensuring depth sorting accounts for terrain. I need to set proper parameters for material and color.**Adjusting rendering parameters** I need to adjust the perspective and point material size to 1.6, with perhaps uniform attenuation and ensure visibility. I should set depth testing to false to keep stars rendered above the trees. With depth testing true, I have to consider star positions relative to tree locations, as they can potentially occlude each other. I'll use a canvas texture for the halo sprite around the moon, ensuring it’s transparent with an additive blend. Also, I want to check the UI controls for proper functionality and ensure the overlay settings are correct.**Designing UI elements** I’m working on the UI layout, focusing on the bottom right for the main controls. I think I’ll center a subtle title at the bottom that says "A QUIET PLACE..." as an overlay. For the top left, I want a small square mark with the title and in the top right, I’ll include a moon indicator showing the time, like "MIDNIGHT • 02:17 AM." It needs to be simple and clean to fit the overall design aesthetic!**Designing a tree model** I’m thinking about the dimensions for the tree model: it should be around 6 units tall with a crown derived from a specific formula. I'm considering layers with varying radii and heights, ensuring the trunk is about 2.1 units. I want to place the tree on flat terrain and possibly add branch details, but I'm uncertain. Also, there's the moon’s appearance in the night sky, which I'll create using emissive material. I might position craters to align with the camera's view.**Creating moon craters** I’m thinking about designing the moon by making the sphere dark pale with tiny scales on the hemisphere facing the camera. The moon will be positioned far from the camera, oriented in that direction. I’ll set the crater coordinates based on the moon's center relative to the camera. Using small spherical disks for craters sounds good, with specific dimensions. I also want to ensure tree placements are random yet stable with a seed and create a cluttered background, while controlling the camera's polar angles.**Setting camera controls** I need to set the camera controls to prevent it from going below ground. I'll determine a target height around y=1.2, and set the max orbit angle relative to that. The current max polar angle is 1.49, which places the camera near the horizon slightly downward. To avoid ground-level issues, I'll adjust the minimum distance to 9 and ensure the target height is around 1.4. This way, the camera remains positioned safely above ground.**Designing the forest scene** I’m envisioning a dark forest floor with a circle created using `CircleGeometry(9.5, 64)` and a standard color of `0x454033`, placed at y=-0.095. The clearing will look better if I reduce the radius to 8.5 but skip the concentric radial surface for now. I’ll scatter darker dirt patches using Dodecahedrons for texture. Surrounding the circle, I'll add stones for the fire and logs, making sure they intersect around the center and adjust the geometry as needed.**Creating log details** I'm thinking about using two different lengths for the logs. To finish them off, I’ll add caps using `CylinderGeometry` with a radius of 0.24 and height of 0.025, oriented so their centers align with the endpoints of the logs. I want to ensure the quaternion aligns with the log's axis, maintaining the default geometry orientation. The ends of the logs should be darker since they’re cut wood, while the material for the caps will be lighter.