GPT-6.1 Terra
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 47.9 s
- Cost
- $0.019
- Output tokens
- 3,740
- Spent thinking
- 140 tokens
- Speed
- 87.8 tok/s
- Console errors
- 0
- First token after
- 5.3 s
- First visible text after
- 5.3 s
- Prompt tokens
- 555
- Code size
- 10.3 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-6.1-terra- Reasoning sent
{"effort":"low"}- 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 · 230 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Midnight at the Clearing</title>
<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>
<style>
* { box-sizing: border-box; }
html, body { margin: 0; width: 100%; height: 100%; overflow: hidden; background: #080f18; }
canvas { display: block; }
.title { position: absolute; top: 35px; left: 42px; color: #f2e8d1; font-family: Georgia, serif; pointer-events: none; text-shadow: 0 2px 16px #000; }
.eyebrow { font: 10px/1.4 Arial, sans-serif; letter-spacing: .3em; text-transform: uppercase; color: #d6a966; margin-bottom: 9px; }
h1 { font-size: clamp(25px, 3vw, 39px); font-weight: normal; margin: 0; letter-spacing: .015em; }
.subtitle { font: 12px/1.5 Arial, sans-serif; color: #a4b1ac; margin-top: 8px; letter-spacing: .04em; }
.hint { position: absolute; bottom: 28px; left: 42px; color: #c5d0c9; font: 10px Arial, sans-serif; letter-spacing: .16em; text-transform: uppercase; pointer-events: none; opacity: .75; }
.hint span { display: inline-block; border: 1px solid #9aaca5; border-radius: 20px; padding: 8px 11px; margin-right: 9px; }
.credit { position: absolute; right: 38px; bottom: 34px; font: 10px Arial, sans-serif; color: #98aaa4; letter-spacing: .2em; text-transform: uppercase; pointer-events: none; }
@media(max-width: 600px) { .title { top: 24px; left: 24px; } .hint { left: 24px; bottom: 22px; font-size: 9px; } .credit { display: none; } }
</style>
</head>
<body>
<div class="title"><div class="eyebrow">A little wilderness</div><h1>Midnight at the clearing</h1><div class="subtitle">A quiet moment beneath the pines.</div></div>
<div class="hint"><span>↔ drag</span> to look around · scroll to zoom</div>
<div class="credit">Forest No. 01 / Nightfall</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(0x09141e);
scene.fog = new THREE.FogExp2(0x101d25, 0.018);
const camera = new THREE.PerspectiveCamera(42, innerWidth / innerHeight, 0.1, 160);
camera.position.set(13, 10, 19);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.25;
document.body.prepend(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 1.8, 0);
controls.enableDamping = true;
controls.maxPolarAngle = Math.PI / 2 - 0.055;
controls.minDistance = 7;
controls.maxDistance = 35;
controls.update();
let seed = 73451;
function rand() { seed = (seed * 1664525 + 1013904223) >>> 0; return seed / 4294967296; }
function range(a, b) { return a + rand() * (b - a); }
const mat = (color, extra = {}) => new THREE.MeshStandardMaterial({ color, flatShading: true, ...extra });
const groundMat = mat(0x273c37);
const ground = new THREE.Mesh(new THREE.CircleGeometry(85, 64), groundMat);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
scene.add(new THREE.HemisphereLight(0x96b3cb, 0x17221b, 1.05));
const moonlight = new THREE.DirectionalLight(0xaac5df, 1.25);
moonlight.position.set(-12, 24, -12);
scene.add(moonlight);
const fireLight = new THREE.PointLight(0xff812c, 75, 23, 2);
fireLight.position.set(0, 2.1, 0);
fireLight.castShadow = true;
fireLight.shadow.mapSize.set(1024, 1024);
fireLight.shadow.camera.near = 0.3;
fireLight.shadow.camera.far = 25;
fireLight.shadow.bias = -0.001;
scene.add(fireLight);
// Moon and distant stars.
const moon = new THREE.Mesh(new THREE.SphereGeometry(2.15, 20, 16), new THREE.MeshBasicMaterial({color: 0xe4e8d3}));
moon.position.set(-26, 34, -65);
scene.add(moon);
const starPositions = [];
for (let i = 0; i < 260; i++) {
const angle = range(0, Math.PI * 2), y = range(9, 75), radius = range(65, 90);
starPositions.push(Math.cos(angle) * radius, y, Math.sin(angle) * radius);
}
const starGeo = new THREE.BufferGeometry();
starGeo.setAttribute('position', new THREE.Float32BufferAttribute(starPositions, 3));
scene.add(new THREE.Points(starGeo, new THREE.PointsMaterial({color: 0xdbe7e2, size: 0.23, sizeAttenuation: true, transparent: true, opacity: 0.8, fog: false})));
// Pine trees, kept outside the clearing.
const bark = mat(0x49382d);
const pineMats = [mat(0x173c35), mat(0x1d493e), mat(0x234d40), mat(0x193b37)];
const trunkGeo = new THREE.CylinderGeometry(0.19, 0.31, 1, 6);
const crownGeo = new THREE.ConeGeometry(1, 1, 7);
function tree(x, z, scale) {
const group = new THREE.Group();
const trunk = new THREE.Mesh(trunkGeo, bark);
trunk.scale.set(scale, 2.8 * scale, scale);
trunk.position.y = 1.4 * scale;
trunk.castShadow = true;
group.add(trunk);
const foliage = pineMats[Math.floor(rand() * pineMats.length)];
for (let j = 0; j < 3; j++) {
const crown = new THREE.Mesh(crownGeo, foliage);
const w = (2.25 - j * 0.39) * scale;
const h = (3.6 - j * 0.3) * scale;
crown.scale.set(w, h, w);
crown.position.y = (3.0 + j * 1.45) * scale;
crown.rotation.y = j * 0.5 + rand() * 0.4;
crown.castShadow = true;
crown.receiveShadow = true;
group.add(crown);
}
group.position.set(x, 0, z);
scene.add(group);
}
for (let i = 0; i < 62; i++) {
const angle = i * 2.399 + range(-0.18, 0.18);
let radius = range(10.5, 31);
// Preserve a view into the clearing from the initial camera.
if (Math.sin(angle) > 0.45 && Math.cos(angle) > 0.1 && radius < 18) radius += 9;
tree(Math.cos(angle) * radius, Math.sin(angle) * radius, range(0.8, 1.65));
}
// Warm earthen patch beneath the fire.
const dirt = new THREE.Mesh(new THREE.CircleGeometry(5.1, 32), mat(0x554c3b));
dirt.rotation.x = -Math.PI / 2;
dirt.position.y = 0.015;
dirt.receiveShadow = true;
scene.add(dirt);
const rockMaterials = [mat(0x777971), mat(0x686e68), mat(0x898579)];
const rockGeo = new THREE.IcosahedronGeometry(1, 0);
function rock(x, z, sx, sy, sz) {
const m = new THREE.Mesh(rockGeo, rockMaterials[Math.floor(rand() * rockMaterials.length)]);
m.position.set(x, sy * 0.45, z);
m.scale.set(sx, sy, sz);
m.rotation.set(rand(), rand() * 3, rand());
m.castShadow = true; m.receiveShadow = true;
scene.add(m);
}
for (let i = 0; i < 12; i++) {
const a = i * Math.PI * 2 / 12;
rock(Math.cos(a) * 1.55, Math.sin(a) * 1.55, range(0.32, 0.48), range(0.23, 0.35), range(0.3, 0.45));
}
for (let i = 0; i < 17; i++) {
const a = range(0, 6.28), r = range(5.6, 13);
rock(Math.cos(a) * r, Math.sin(a) * r, range(0.3, 0.8), range(0.2, 0.55), range(0.3, 0.7));
}
// Firewood and seating logs.
const woodMat = mat(0x493024);
const cutMat = mat(0x9b7550);
function log(x, y, z, length, radius, angle, seating = false) {
const group = new THREE.Group();
const body = new THREE.Mesh(new THREE.CylinderGeometry(radius * 0.9, radius, length, 9), woodMat);
body.rotation.z = Math.PI / 2;
body.castShadow = true; body.receiveShadow = true;
group.add(body);
for (const side of [-1, 1]) {
const end = new THREE.Mesh(new THREE.CircleGeometry(radius * (side === 1 ? 0.9 : 1), 9), cutMat);
end.rotation.y = side * Math.PI / 2;
end.position.x = side * (length / 2 + 0.006);
group.add(end);
}
group.position.set(x, y, z);
group.rotation.y = angle;
scene.add(group);
}
for (let i = 0; i < 5; i++) log(0, 0.25 + i * 0.1, 0, 2.15, 0.19, i * Math.PI / 5);
log(-3.8, 0.48, 2.5, 3.3, 0.48, -0.5, true);
log(3.6, 0.44, -1.8, 3.1, 0.44, -0.7, true);
log(-2.8, 0.42, -3.8, 2.7, 0.42, 0.6, true);
// Layered, faceted flames.
const flameMats = [
new THREE.MeshBasicMaterial({color: 0xff5210}),
new THREE.MeshBasicMaterial({color: 0xffa51d}),
new THREE.MeshBasicMaterial({color: 0xffe374})
];
const flames = [];
for (let i = 0; i < 9; i++) {
const layer = i < 4 ? 0 : i < 7 ? 1 : 2;
const flame = new THREE.Mesh(new THREE.ConeGeometry(1, 1, 6), flameMats[layer]);
const a = i * 2.4;
const offset = layer === 0 ? 0.4 : 0.2;
flame.position.set(Math.cos(a) * offset, 1, Math.sin(a) * offset);
flame.userData = { x: flame.position.x, z: flame.position.z, phase: rand() * 6.28, h: layer === 0 ? range(1.7, 2.6) : layer === 1 ? range(1.3, 2) : range(0.8, 1.2), w: layer === 0 ? 0.48 : layer === 1 ? 0.37 : 0.25 };
scene.add(flame);
flames.push(flame);
}
// Ember particles.
const count = 100, particles = new Float32Array(count * 3), sparkData = [];
for (let i = 0; i < count; i++) sparkData.push({ phase: rand(), speed: range(0.12, 0.28), radius: range(0.1, 1.25), angle: range(0, 6.28) });
const sparkGeo = new THREE.BufferGeometry();
sparkGeo.setAttribute('position', new THREE.BufferAttribute(particles, 3));
const sparks = new THREE.Points(sparkGeo, new THREE.PointsMaterial({color: 0xffb44d, size: 0.085, transparent: true, opacity: 0.9, blending: THREE.AdditiveBlending, depthWrite: false}));
scene.add(sparks);
const clock = new THREE.Clock();
function animate() {
const t = clock.getElapsedTime();
fireLight.intensity = 65 + Math.sin(t * 9) * 7 + Math.sin(t * 15.7) * 5;
for (const f of flames) {
const d = f.userData;
const flicker = 1 + 0.18 * Math.sin(t * 8 + d.phase) + 0.08 * Math.sin(t * 14 + d.phase);
f.scale.set(d.w * (2 - flicker), d.h * flicker, d.w * (2 - flicker));
f.position.y = 0.37 + d.h * flicker * 0.5;
f.position.x = d.x + Math.sin(t * 3 + d.phase) * 0.08;
f.rotation.z = Math.sin(t * 5 + d.phase) * 0.1;
}
for (let i = 0; i < count; i++) {
const p = sparkData[i], v = (p.phase + t * p.speed) % 1;
particles[i * 3] = Math.cos(p.angle + t * 0.3) * p.radius * v + Math.sin(t * 2 + i) * 0.08;
particles[i * 3 + 1] = 0.6 + v * 6.2;
particles[i * 3 + 2] = Math.sin(p.angle + t * 0.3) * p.radius * v;
}
sparkGeo.attributes.position.needsUpdate = true;
controls.update();
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
addEventListener('resize', () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
});
</script>
</body>
</html>