Models 44

GPT-6 Luna

by OpenAIReasoningClean
Open in new window

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 &nbsp;·&nbsp; 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.