GPT-6.1 Terra
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 55.6 s
- Cost
- $0.025
- Output tokens
- 4,877
- Spent thinking
- 0 tokens
- Speed
- 89.8 tok/s
- Console errors
- 0
- First token after
- 1.2 s
- First visible text after
- 1.2 s
- Prompt tokens
- 555
- Code size
- 14.0 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-6.1-terra- Reasoning sent
{"effort":"none"}- 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 · 352 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Emberwood — A Night in 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 { width: 100%; height: 100%; margin: 0; overflow: hidden; }
body { background: #080f1c; color: #f2e9d9; font-family: Georgia, "Times New Roman", serif; }
canvas { display: block; width: 100%; height: 100%; }
.overlay { position: fixed; inset: 0; pointer-events: none; }
.top {
position: absolute; top: 34px; left: 42px;
text-shadow: 0 2px 14px rgba(0,0,0,.75);
}
.eyebrow, .hint, .corner {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
text-transform: uppercase; letter-spacing: .22em;
}
.eyebrow { color: #d8a16d; font-size: 10px; margin-bottom: 9px; }
h1 { font-weight: normal; font-size: clamp(25px, 3.2vw, 42px); letter-spacing: .02em; margin: 0; }
.sub { color: #b7c1bd; font-size: 13px; margin: 8px 0 0; font-style: italic; }
.hint {
position: absolute; bottom: 31px; left: 50%; transform: translateX(-50%);
color: #d0c7b7; font-size: 9px; white-space: nowrap;
background: rgba(8, 15, 22, .45); border: 1px solid rgba(225, 202, 166, .18);
border-radius: 30px; padding: 11px 17px; backdrop-filter: blur(5px);
}
.hint span { color: #e7aa70; }
.corner { position: absolute; right: 36px; bottom: 36px; color: rgba(192, 203, 195, .55); font-size: 9px; }
@media (max-width: 600px) {
.top { top: 24px; left: 24px; }
.corner { display: none; }
.hint { bottom: 22px; font-size: 8px; padding: 10px 13px; }
}
</style>
</head>
<body>
<div class="overlay">
<div class="top">
<div class="eyebrow">A little place to wander</div>
<h1>Emberwood</h1>
<p class="sub">A quiet night in the clearing.</p>
</div>
<div class="hint"><span>✧</span> Drag to explore · Scroll to zoom</div>
<div class="corner">After dark / No. 01</div>
</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(0x0a1422);
scene.fog = new THREE.FogExp2(0x111d29, 0.022);
const camera = new THREE.PerspectiveCamera(43, innerWidth / innerHeight, 0.1, 180);
camera.position.set(14, 10, 19);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.setSize(innerWidth, innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.15;
document.body.prepend(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 1.65, 0);
controls.enableDamping = true;
controls.dampingFactor = 0.055;
controls.minDistance = 8;
controls.maxDistance = 35;
controls.minPolarAngle = 0.28;
controls.maxPolarAngle = Math.PI / 2 - 0.07;
controls.enablePan = false;
controls.autoRotate = true;
controls.autoRotateSpeed = 0.22;
controls.addEventListener('start', () => { controls.autoRotate = false; });
let seed = 23891;
function random() {
seed = (seed * 1664525 + 1013904223) >>> 0;
return seed / 4294967296;
}
const rand = (a, b) => a + random() * (b - a);
const choose = arr => arr[Math.floor(random() * arr.length)];
const ambient = new THREE.AmbientLight(0x8ba3b8, 0.52);
scene.add(ambient);
const moonLight = new THREE.DirectionalLight(0x9bb9d4, 1.35);
moonLight.position.set(-12, 25, -15);
scene.add(moonLight);
const fireLight = new THREE.PointLight(0xff7329, 65, 18, 1.7);
fireLight.position.set(0, 1.35, 0);
fireLight.castShadow = true;
fireLight.shadow.mapSize.set(1024, 1024);
fireLight.shadow.bias = -0.001;
fireLight.shadow.normalBias = 0.035;
fireLight.shadow.camera.near = 0.2;
fireLight.shadow.camera.far = 18;
scene.add(fireLight);
const fireFill = new THREE.PointLight(0xffad55, 13, 7, 2);
fireFill.position.set(0, 2.5, 0);
scene.add(fireFill);
const groundMat = new THREE.MeshStandardMaterial({ color: 0x283b34, flatShading: true, roughness: 1 });
const groundGeo = new THREE.PlaneGeometry(160, 160, 70, 70);
const gp = groundGeo.attributes.position;
for (let i = 0; i < gp.count; i++) {
const x = gp.getX(i), y = gp.getY(i);
const d = Math.sqrt(x*x + y*y);
gp.setZ(i, d < 5 ? 0 : (Math.sin(x * .22) * Math.cos(y * .18) * .23 + rand(-.13, .13)) * Math.min(1, (d - 5) / 5));
}
groundGeo.computeVertexNormals();
const ground = new THREE.Mesh(groundGeo, groundMat);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
const patch = new THREE.Mesh(
new THREE.CircleGeometry(5.1, 15),
new THREE.MeshStandardMaterial({ color: 0x665443, roughness: 1, flatShading: true })
);
patch.rotation.x = -Math.PI / 2;
patch.position.y = 0.025;
patch.receiveShadow = true;
scene.add(patch);
const patch2 = new THREE.Mesh(
new THREE.CircleGeometry(3.2, 14),
new THREE.MeshStandardMaterial({ color: 0x594638, roughness: 1, flatShading: true })
);
patch2.rotation.x = -Math.PI / 2;
patch2.position.y = 0.035;
patch2.receiveShadow = true;
scene.add(patch2);
const trunkMats = [0x3f302c, 0x4c3831, 0x514034].map(color => new THREE.MeshStandardMaterial({ color, flatShading: true, roughness: 1 }));
const leafMats = [0x1b3834, 0x213f37, 0x25483e, 0x304b3d, 0x203a35].map(color => new THREE.MeshStandardMaterial({ color, flatShading: true, roughness: 1 }));
function tree(x, z, scale) {
const group = new THREE.Group();
const trunk = new THREE.Mesh(new THREE.CylinderGeometry(.18, .31, 3.5, 6), choose(trunkMats));
trunk.position.y = 1.75;
trunk.castShadow = true;
trunk.receiveShadow = true;
group.add(trunk);
const leaf = choose(leafMats);
const tiers = [
{ y: 3.45, r: 1.78, h: 3.45 },
{ y: 4.8, r: 1.48, h: 3.2 },
{ y: 6.08, r: 1.12, h: 2.9 }
];
for (const tier of tiers) {
const crown = new THREE.Mesh(new THREE.ConeGeometry(tier.r, tier.h, 7), leaf);
crown.position.y = tier.y;
crown.rotation.y = rand(0, 2);
crown.castShadow = true;
crown.receiveShadow = true;
group.add(crown);
}
group.position.set(x, 0, z);
group.scale.setScalar(scale);
scene.add(group);
}
for (let i = 0; i < 64; i++) {
const angle = (i / 64) * Math.PI * 2 + rand(-.1, .1);
const radius = i < 26 ? rand(8.4, 13.5) : rand(14, 33);
tree(Math.cos(angle) * radius, Math.sin(angle) * radius, rand(.75, 1.35));
}
const stoneMats = [0x77766a, 0x8a8170, 0x696e66, 0x9a8873].map(color => new THREE.MeshStandardMaterial({ color, flatShading: true, roughness: 1 }));
const stoneGeo = new THREE.IcosahedronGeometry(1, 0);
function rock(x, z, sx, sy, sz) {
const mesh = new THREE.Mesh(stoneGeo, choose(stoneMats));
mesh.position.set(x, sy * .45 + .05, z);
mesh.scale.set(sx, sy, sz);
mesh.rotation.set(rand(-.3, .3), rand(0, 6), rand(-.3, .3));
mesh.castShadow = true;
mesh.receiveShadow = true;
scene.add(mesh);
}
for (let i = 0; i < 12; i++) {
const a = i / 12 * Math.PI * 2;
rock(Math.cos(a) * 1.48, Math.sin(a) * 1.48, rand(.34, .48), rand(.22, .37), rand(.29, .42));
}
[[4.6, 1.4], [-4.2, 1.7], [3.7, -3.2], [-4.7, -2.4], [6, -1], [-2.8, 4.8]].forEach(([x,z]) => rock(x, z, rand(.35,.75), rand(.25,.5), rand(.35,.7)));
const barkMat = new THREE.MeshStandardMaterial({ color: 0x573725, flatShading: true, roughness: 1 });
const endMat = new THREE.MeshStandardMaterial({ color: 0xa17c55, flatShading: true, roughness: 1 });
function log(x, z, length, radius, angle, y = radius + .08) {
const group = new THREE.Group();
const body = new THREE.Mesh(new THREE.CylinderGeometry(radius, radius * 1.08, length, 7, 1), barkMat);
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 * .97, 7), endMat);
end.position.x = side * (length / 2 + .006);
end.rotation.y = side * Math.PI / 2;
group.add(end);
const ring = new THREE.Mesh(new THREE.RingGeometry(radius * .43, radius * .47, 7), barkMat);
ring.position.x = side * (length / 2 + .009);
ring.rotation.y = side * Math.PI / 2;
group.add(ring);
}
group.position.set(x, y, z);
group.rotation.y = angle;
scene.add(group);
}
log(-3.05, 2.65, 2.5, .35, -.38);
log(3.2, -1.75, 2.5, .34, -.65);
log(-2.55, -3.25, 2.1, .28, .6);
for (let i = 0; i < 4; i++) {
log(0, 0, 2.05, .17, i * Math.PI / 4, .23 + (i % 2) * .12);
}
const emberMat = new THREE.MeshBasicMaterial({ color: 0xa82b0d });
for (let i = 0; i < 13; i++) {
const ember = new THREE.Mesh(new THREE.IcosahedronGeometry(rand(.08, .16), 0), emberMat);
const a = rand(0, Math.PI * 2), r = rand(0, .8);
ember.position.set(Math.cos(a) * r, rand(.12, .3), Math.sin(a) * r);
scene.add(ember);
}
function flameGeometry() {
const geo = new THREE.ConeGeometry(1, 1, 7, 3);
const pos = geo.attributes.position;
for (let i = 0; i < pos.count; i++) {
const y = pos.getY(i);
pos.setX(i, pos.getX(i) + (y + .5) * (y + .5) * .18);
}
geo.computeVertexNormals();
return geo;
}
const flameGeo = flameGeometry();
const flameMaterials = [
new THREE.MeshBasicMaterial({ color: 0xe94d12 }),
new THREE.MeshBasicMaterial({ color: 0xff8a20 }),
new THREE.MeshBasicMaterial({ color: 0xffd269 }),
new THREE.MeshBasicMaterial({ color: 0xffedaa })
];
const flames = [];
function addFlame(x, z, height, width, mat, phase) {
const mesh = new THREE.Mesh(flameGeo, mat);
mesh.position.set(x, height / 2 + .25, z);
mesh.scale.set(width, height, width);
mesh.rotation.y = rand(0, 6);
scene.add(mesh);
flames.push({ mesh, x, z, height, width, phase });
}
addFlame(0, 0, 2.25, .78, flameMaterials[0], 0);
addFlame(-.38, .15, 1.65, .48, flameMaterials[0], 1.3);
addFlame(.4, -.1, 1.55, .46, flameMaterials[0], 2.5);
addFlame(.1, .18, 1.65, .53, flameMaterials[1], .6);
addFlame(-.25, -.12, 1.27, .36, flameMaterials[1], 2.2);
addFlame(.12, .31, 1.06, .35, flameMaterials[2], 1.8);
addFlame(-.08, .37, .65, .24, flameMaterials[3], 3.2);
const sparkCount = 95;
const sparkPositions = new Float32Array(sparkCount * 3);
const sparkData = [];
for (let i = 0; i < sparkCount; i++) {
sparkData.push({ offset: rand(0, 1), speed: rand(.12, .26), angle: rand(0, 6.28), radius: rand(.05, .65), drift: rand(.2, 1.1) });
}
const sparkGeo = new THREE.BufferGeometry();
sparkGeo.setAttribute('position', new THREE.BufferAttribute(sparkPositions, 3));
const sparkMat = new THREE.PointsMaterial({ color: 0xffae50, size: .075, transparent: true, opacity: .95, sizeAttenuation: true, depthWrite: false, blending: THREE.AdditiveBlending });
const sparks = new THREE.Points(sparkGeo, sparkMat);
scene.add(sparks);
const starsGeo = new THREE.BufferGeometry();
const starPos = [];
for (let i = 0; i < 380; i++) {
const a = rand(0, Math.PI * 2);
const y = rand(.17, .99);
const r = Math.sqrt(1 - y*y);
starPos.push(Math.cos(a) * r * 95, y * 95, Math.sin(a) * r * 95);
}
starsGeo.setAttribute('position', new THREE.Float32BufferAttribute(starPos, 3));
const stars = new THREE.Points(starsGeo, new THREE.PointsMaterial({ color: 0xc8d9db, size: .22, sizeAttenuation: true, transparent: true, opacity: .85, fog: false }));
scene.add(stars);
const moon = new THREE.Mesh(new THREE.SphereGeometry(2.1, 24, 16), new THREE.MeshBasicMaterial({ color: 0xf4e8c9, fog: false }));
moon.position.set(-35, 38, -60);
scene.add(moon);
const moonHalo = new THREE.Sprite(new THREE.SpriteMaterial({ color: 0xe8dfc7, transparent: true, opacity: .11, fog: false, depthWrite: false }));
const haloCanvas = document.createElement('canvas');
haloCanvas.width = haloCanvas.height = 128;
const ctx = haloCanvas.getContext('2d');
const grad = ctx.createRadialGradient(64, 64, 4, 64, 64, 64);
grad.addColorStop(0, 'rgba(255,255,255,0.65)');
grad.addColorStop(.25, 'rgba(255,255,255,0.22)');
grad.addColorStop(1, 'rgba(255,255,255,0)');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, 128, 128);
moonHalo.material.map = new THREE.CanvasTexture(haloCanvas);
moonHalo.scale.set(16, 16, 1);
moonHalo.position.copy(moon.position);
scene.add(moonHalo);
const grassMat = new THREE.MeshStandardMaterial({ color: 0x405747, flatShading: true, side: THREE.DoubleSide });
const grassGeo = new THREE.ConeGeometry(.09, .42, 3);
for (let i = 0; i < 180; i++) {
const a = rand(0, Math.PI * 2), r = rand(5.2, 19);
const grass = new THREE.Mesh(grassGeo, grassMat);
grass.position.set(Math.cos(a) * r, .16, Math.sin(a) * r);
grass.rotation.z = rand(-.25, .25);
grass.scale.setScalar(rand(.6, 1.4));
scene.add(grass);
}
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const t = clock.getElapsedTime();
fireLight.intensity = 60 + Math.sin(t * 8.1) * 5 + Math.sin(t * 13.7) * 3;
fireLight.position.x = Math.sin(t * 3.4) * .12;
fireFill.intensity = 11 + Math.sin(t * 6.2) * 2;
for (const f of flames) {
const wave = Math.sin(t * 6 + f.phase);
const h = f.height * (1 + .1 * wave + .04 * Math.sin(t * 11 + f.phase));
f.mesh.scale.set(f.width * (1 + .08 * Math.sin(t * 5 + f.phase)), h, f.width);
f.mesh.position.y = h / 2 + .22;
f.mesh.position.x = f.x + Math.sin(t * 3 + f.phase) * .07;
f.mesh.rotation.z = Math.sin(t * 4 + f.phase) * .09;
}
for (let i = 0; i < sparkCount; i++) {
const s = sparkData[i];
const p = (s.offset + t * s.speed) % 1;
sparkPositions[i * 3] = Math.cos(s.angle + t * .5) * (s.radius + p * s.drift) + Math.sin(t + s.angle) * p * .35;
sparkPositions[i * 3 + 1] = .45 + p * 5.3;
sparkPositions[i * 3 + 2] = Math.sin(s.angle + t * .45) * (s.radius + p * s.drift);
}
sparkGeo.attributes.position.needsUpdate = true;
controls.update();
renderer.render(scene, camera);
}
animate();
window.addEventListener('resize', () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
});
</script>
</body>
</html>