GPT-6 Luna
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 48.1 s
- Cost
- $0.0022
- Output tokens
- 4,278
- Spent thinking
- 202 tokens
- Speed
- 114.5 tok/s
- Console errors
- 0
- First token after
- 10.7 s
- First visible text after
- 12.3 s
- Prompt tokens
- 555
- Code size
- 12.5 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-6-luna- Reasoning sent
{"effort":"low"}- 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 · 273 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Night Campfire</title>
<style>
* { box-sizing: border-box; }
html, body { margin: 0; width: 100%; height: 100%; overflow: hidden; background: #07111b; }
body { font-family: Georgia, "Times New Roman", serif; color: #f5e8cf; }
canvas { display: block; width: 100%; height: 100%; }
.vignette { position: fixed; inset: 0; pointer-events: none; background: radial-gradient(ellipse at center, transparent 38%, rgba(1,5,10,.22) 100%); }
.title {
position: fixed; top: 28px; left: 34px; z-index: 2;
text-shadow: 0 2px 16px #000; pointer-events: none;
}
.eyebrow { color: #e39b54; font: 10px/1.4 Arial, sans-serif; letter-spacing: .28em; text-transform: uppercase; }
h1 { margin: 7px 0 0; font-size: clamp(22px, 3vw, 34px); font-weight: 400; letter-spacing: .045em; }
.hint {
position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);
color: rgba(248,232,207,.68); font: 11px Arial, sans-serif; letter-spacing: .12em;
text-transform: uppercase; text-shadow: 0 1px 8px #000; white-space: nowrap;
pointer-events: none;
}
@media (max-width: 560px) {
.title { top: 20px; left: 20px; }
.hint { bottom: 18px; font-size: 9px; letter-spacing: .08em; }
}
</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="title"><div class="eyebrow">Wilderness · After dark</div><h1>The quiet of the woods</h1></div>
<div class="hint">Drag to look around · Scroll to explore</div>
<div class="vignette"></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(0x08131d);
scene.fog = new THREE.FogExp2(0x08131d, 0.026);
const camera = new THREE.PerspectiveCamera(48, innerWidth / innerHeight, 0.1, 130);
camera.position.set(13, 11, 20);
const renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: 'high-performance' });
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 1.7));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.15;
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 1.2, 0);
controls.enableDamping = true;
controls.dampingFactor = 0.055;
controls.minDistance = 9;
controls.maxDistance = 35;
controls.minPolarAngle = 0.18;
controls.maxPolarAngle = Math.PI / 2 - 0.035;
controls.maxAzimuthAngle = Infinity;
scene.add(new THREE.HemisphereLight(0x839cb8, 0x182018, 0.62));
const moonLight = new THREE.DirectionalLight(0x9bb4d1, 1.05);
moonLight.position.set(-12, 19, -8);
moonLight.castShadow = true;
moonLight.shadow.mapSize.set(1024, 1024);
moonLight.shadow.camera.left = -24; moonLight.shadow.camera.right = 24;
moonLight.shadow.camera.top = 24; moonLight.shadow.camera.bottom = -24;
moonLight.shadow.bias = -0.0005;
scene.add(moonLight);
const ground = new THREE.Mesh(
new THREE.PlaneGeometry(150, 150),
new THREE.MeshStandardMaterial({ color: 0x17251f, roughness: 1 })
);
ground.rotation.x = -Math.PI / 2;
ground.position.y = -0.08;
ground.receiveShadow = true;
scene.add(ground);
// A softer, open patch of earth beneath the fire.
const clearing = new THREE.Mesh(
new THREE.CircleGeometry(8.7, 48),
new THREE.MeshStandardMaterial({ color: 0x302c22, roughness: 1, polygonOffset: true, polygonOffsetFactor: -1 })
);
clearing.rotation.x = -Math.PI / 2;
clearing.position.y = -0.045;
clearing.receiveShadow = true;
scene.add(clearing);
// Scattered flat stones and little patches of moss give the earth some shape.
let seed = 4817;
function rand() { seed = (seed * 16807) % 2147483647; return (seed - 1) / 2147483646; }
const rockMat = new THREE.MeshStandardMaterial({ color: 0x4b5048, roughness: 1, flatShading: true });
const mossMat = new THREE.MeshStandardMaterial({ color: 0x273b2b, roughness: 1, flatShading: true });
for (let i = 0; i < 42; i++) {
const a = rand() * Math.PI * 2, r = 5.2 + rand() * 13;
const rock = new THREE.Mesh(new THREE.DodecahedronGeometry(0.22 + rand() * 0.45, 0), rockMat);
rock.position.set(Math.cos(a) * r, 0.04 + rand() * 0.12, Math.sin(a) * r);
rock.scale.set(1.25, 0.58 + rand() * 0.45, 0.9);
rock.rotation.set(rand(), rand() * 6, rand());
rock.castShadow = true; rock.receiveShadow = true; scene.add(rock);
}
for (let i = 0; i < 22; i++) {
const a = rand() * Math.PI * 2, r = 5.8 + rand() * 8;
const tuft = new THREE.Mesh(new THREE.DodecahedronGeometry(.45 + rand() * .5, 0), mossMat);
tuft.position.set(Math.cos(a)*r, .02, Math.sin(a)*r);
tuft.scale.set(1.5, .22, .85); tuft.rotation.y = rand()*6.28; scene.add(tuft);
}
// Forest ring: deliberately leave a clear space around the campfire.
const trunkMat = new THREE.MeshStandardMaterial({ color: 0x38291f, roughness: 1, flatShading: true });
const pineMats = [
new THREE.MeshStandardMaterial({ color: 0x173128, roughness: 1, flatShading: true }),
new THREE.MeshStandardMaterial({ color: 0x1d3a30, roughness: 1, flatShading: true }),
new THREE.MeshStandardMaterial({ color: 0x223b32, roughness: 1, flatShading: true }),
new THREE.MeshStandardMaterial({ color: 0x182c29, roughness: 1, flatShading: true })
];
for (let i = 0; i < 58; i++) {
const a = rand() * Math.PI * 2;
const r = 10.2 + Math.sqrt(rand()) * 25;
const x = Math.cos(a) * r, z = Math.sin(a) * r;
const h = 4.1 + rand() * 5.6, trunkH = h * (0.28 + rand() * .1);
const group = new THREE.Group();
group.position.set(x, 0, z);
const trunk = new THREE.Mesh(new THREE.CylinderGeometry(.18 + h*.015, .3 + h*.02, trunkH, 6), trunkMat);
trunk.position.y = trunkH/2; trunk.castShadow = true; trunk.receiveShadow = true; group.add(trunk);
const layers = 3;
for (let j=0; j<layers; j++) {
const layerH = h * (0.55 - j*.04);
const radius = h * (0.26 - j*.045);
const cone = new THREE.Mesh(new THREE.ConeGeometry(radius, layerH, 6, 1), pineMats[Math.floor(rand()*pineMats.length)]);
cone.position.y = trunkH * .55 + h*.16 + j*h*.21;
cone.rotation.y = rand()*Math.PI;
cone.castShadow = true; cone.receiveShadow = true; group.add(cone);
}
group.rotation.y = rand()*Math.PI*2;
scene.add(group);
}
// Two crossed, charred logs at the heart of the clearing.
const logMat = new THREE.MeshStandardMaterial({ color: 0x493225, roughness: 1, flatShading: true });
const charMat = new THREE.MeshStandardMaterial({ color: 0x211d19, roughness: 1, flatShading: true });
function makeLog(x, z, length, angle) {
const log = new THREE.Mesh(new THREE.CylinderGeometry(.24, .29, length, 7), logMat);
log.rotation.z = Math.PI/2;
log.rotation.y = angle;
log.position.set(x, .3, z);
log.castShadow = true; log.receiveShadow = true; scene.add(log);
for (const s of [-1,1]) {
const end = new THREE.Mesh(new THREE.CylinderGeometry(.19,.22,.025,7), charMat);
end.rotation.z = Math.PI/2; end.rotation.y = angle;
end.position.set(x + Math.cos(angle)*length*.5*s, .3, z - Math.sin(angle)*length*.5*s);
end.castShadow = true; scene.add(end);
}
}
makeLog(0, 0, 3.3, .48);
makeLog(0, 0, 3.0, -.48);
// A loose ring of angular stones around the embers.
for (let i=0; i<10; i++) {
const a = i/10*Math.PI*2 + .12;
const stone = new THREE.Mesh(new THREE.DodecahedronGeometry(.38 + (i%3)*.06, 0), rockMat);
stone.position.set(Math.cos(a)*1.72, .24, Math.sin(a)*1.72);
stone.scale.set(1.25,.72,1); stone.rotation.set(rand(),rand()*4,rand());
stone.castShadow = true; stone.receiveShadow = true; scene.add(stone);
}
// Flame shapes are faceted low-poly cones with animated, gently wavering tips.
const flameGroup = new THREE.Group();
flameGroup.position.y = .28;
scene.add(flameGroup);
const orangeMat = new THREE.MeshBasicMaterial({ color: 0xff5a17, transparent: true, opacity: .92, depthWrite: false });
const goldMat = new THREE.MeshBasicMaterial({ color: 0xffb52b, transparent: true, opacity: .96, depthWrite: false });
const coreMat = new THREE.MeshBasicMaterial({ color: 0xffe178, transparent: true, opacity: .92, depthWrite: false });
const flames = [];
function addFlame(radius, height, material, x, z, phase) {
const mesh = new THREE.Mesh(new THREE.ConeGeometry(radius, height, 6, 1), material);
mesh.position.set(x, height*.48, z);
mesh.rotation.z = (rand()-.5)*.24;
mesh.rotation.x = (rand()-.5)*.2;
flameGroup.add(mesh);
flames.push({mesh, radius, height, phase});
}
addFlame(.67, 2.15, orangeMat, 0, 0, 0);
addFlame(.48, 1.72, goldMat, -.25, .08, 1.7);
addFlame(.43, 1.55, goldMat, .28, -.1, 3.1);
addFlame(.29, 1.22, coreMat, .02, -.12, 4.8);
addFlame(.28, 1.34, orangeMat, -.45, -.05, 2.3);
const fireLight = new THREE.PointLight(0xff7926, 7.2, 19, 1.65);
fireLight.position.set(0, 2.1, 0);
fireLight.castShadow = true;
fireLight.shadow.mapSize.set(512,512);
fireLight.shadow.camera.near = .5; fireLight.shadow.camera.far = 22;
fireLight.shadow.bias = -0.002;
scene.add(fireLight);
// Moon and a scattering of crisp, distant stars.
const moon = new THREE.Mesh(new THREE.SphereGeometry(1.05, 12, 10), new THREE.MeshBasicMaterial({ color: 0xdce6eb }));
moon.position.set(-19, 18, -34); scene.add(moon);
const moonGlow = new THREE.Mesh(new THREE.SphereGeometry(1.65, 12, 10), new THREE.MeshBasicMaterial({ color: 0x98b8d0, transparent: true, opacity: .08, depthWrite: false }));
moonGlow.position.copy(moon.position); scene.add(moonGlow);
const starPositions = [];
for (let i=0; i<500; i++) {
const u = rand()*2-1, phi = rand()*Math.PI*2;
const rr = 70 + rand()*22;
starPositions.push(Math.sqrt(1-u*u)*Math.cos(phi)*rr, 22+u*35, Math.sqrt(1-u*u)*Math.sin(phi)*rr);
}
const starGeo = new THREE.BufferGeometry();
starGeo.setAttribute('position', new THREE.Float32BufferAttribute(starPositions,3));
const stars = new THREE.Points(starGeo, new THREE.PointsMaterial({ color: 0xc9d9e6, size: .16, sizeAttenuation: true, transparent: true, opacity: .82 }));
scene.add(stars);
// Tiny glowing embers drift upward, fade, then respawn near the fire.
const particleCount = 90;
const particleData = Array.from({length: particleCount}, () => ({
x:(rand()-.5)*.9, z:(rand()-.5)*.9, y:rand()*3.7,
vy:.45+rand()*1.2, drift:(rand()-.5)*.45, life:rand()
}));
const sparkPositions = new Float32Array(particleCount*3);
const sparkColors = new Float32Array(particleCount*3);
const sparkGeo = new THREE.BufferGeometry();
sparkGeo.setAttribute('position', new THREE.BufferAttribute(sparkPositions,3));
sparkGeo.setAttribute('color', new THREE.BufferAttribute(sparkColors,3));
const sparkMat = new THREE.PointsMaterial({ size: .095, vertexColors: true, transparent: true, opacity: .95, depthWrite: false, sizeAttenuation: true });
const sparks = new THREE.Points(sparkGeo, sparkMat);
scene.add(sparks);
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const t = clock.getElapsedTime();
for (const f of flames) {
const wobble = Math.sin(t*5.2+f.phase)*.11 + Math.sin(t*8.4+f.phase*2)*.055;
f.mesh.scale.set(1 + wobble*.5, .84 + Math.sin(t*6.3+f.phase)*.2, 1 + wobble*.35);
f.mesh.rotation.z = Math.sin(t*3.6+f.phase)*.1;
f.mesh.position.x = (f.phase===0 ? 0 : f.mesh.position.x) + Math.sin(t*2.8+f.phase)*.0015;
}
fireLight.intensity = 6.8 + Math.sin(t*11)*.75 + Math.sin(t*17.2)*.5;
fireLight.position.x = Math.sin(t*2.4)*.12;
for (let i=0; i<particleCount; i++) {
const p = particleData[i];
p.y += p.vy*.016; p.x += p.drift*.016; p.z += Math.sin(t*2+i)*.0018;
if (p.y > 4.7 || p.life > 1) {
p.x=(rand()-.5)*.72; p.z=(rand()-.5)*.72; p.y=.5+rand()*.35;
p.vy=.45+rand()*1.15; p.drift=(rand()-.5)*.45; p.life=0;
}
p.life += .006;
sparkPositions[i*3]=p.x; sparkPositions[i*3+1]=p.y; sparkPositions[i*3+2]=p.z;
const fade = Math.max(.15,1-p.life);
sparkColors[i*3]=1; sparkColors[i*3+1]=.28+.45*fade; sparkColors[i*3+2]=.04+.08*fade;
}
sparkGeo.attributes.position.needsUpdate = true;
sparkGeo.attributes.color.needsUpdate = true;
controls.update();
renderer.render(scene,camera);
}
animate();
addEventListener('resize', () => {
camera.aspect = innerWidth/innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth,innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio,1.7));
});
</script>
</body>
</html>Reasoning trace (202 tokens)
**Coding visual implementation** I need to produce code with an accurate import map and create a polished visual. This includes three modules, with shadows, trees placed randomly in an annulus, and a clearing radius of about 8. The camera will feature animated flames using a faceted mesh, and I’ll incorporate particles for sparks. I'll design a sky background with fog, stars, and a moon sphere. The ground will have tree trunks, logs, rocks, and a subtly circular clearing, ensuring proper lighting and controls. I’ll also include UI labels and manage shadow casting, especially from point lights.