Models 44

DeepSeek V4.1 Flash 1017

by DeepSeekReasoningClean
Open in new window

Running live in a sandboxed frame. Click inside it before using the keyboard.

How it went

Total time
18.3 s
Cost
$0.0026
Output tokens
5,163
Spent thinking
0 tokens
Speed
287.6 tok/s
Console errors
0
First token after
366 ms
First visible text after
366 ms
Prompt tokens
573
Code size
14.6 KB
Finish reason
stop
Served by
Wafer
Model id
deepseek/deepseek-v4.1-flash-1017
Reasoning sent
{"effort":"none"}
Max output
64,000 tokens
Generated
Nov 11, 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 · 403 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Campfire in the Forest Clearing</title>
<style>
  html, body { margin: 0; padding: 0; overflow: hidden; background: #05070d; }
  canvas { display: block; }
  #hint {
    position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%);
    font-family: system-ui, -apple-system, sans-serif; font-size: 13px;
    color: #ffd9a0; background: rgba(20, 12, 6, 0.55); border: 1px solid rgba(255, 190, 120, 0.25);
    padding: 6px 14px; border-radius: 999px; letter-spacing: .04em;
    pointer-events: none; user-select: none; backdrop-filter: blur(3px);
    transition: opacity 1s ease;
  }
</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 id="hint">drag to look around · scroll to zoom</div>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

/* ---------- Renderer ---------- */
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.05;
document.body.appendChild(renderer.domElement);

/* ---------- Scene & Fog ---------- */
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x05070d);
scene.fog = new THREE.FogExp2(0x0a0e18, 0.024);

/* ---------- Camera ---------- */
const camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 500);
camera.position.set(9, 5.5, 12);

/* ---------- Controls ---------- */
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 1.2, 0);
controls.enableDamping = true;
controls.dampingFactor = 0.06;
controls.minDistance = 4;
controls.maxDistance = 34;
controls.maxPolarAngle = Math.PI * 0.495; // keeps camera above ground
controls.minPolarAngle = 0.15;
controls.enablePan = false;
controls.update();

/* ---------- Lights ---------- */
const ambient = new THREE.AmbientLight(0x223055, 0.35);
scene.add(ambient);

const moonLight = new THREE.DirectionalLight(0x8fa8d8, 0.55);
moonLight.position.set(-40, 60, -30);
moonLight.castShadow = true;
moonLight.shadow.mapSize.set(1024, 1024);
moonLight.shadow.camera.left = -40;
moonLight.shadow.camera.right = 40;
moonLight.shadow.camera.top = 40;
moonLight.shadow.camera.bottom = -40;
moonLight.shadow.camera.far = 160;
scene.add(moonLight);

const fireLight = new THREE.PointLight(0xff7a2a, 6, 30, 2);
fireLight.position.set(0, 1.6, 0);
fireLight.castShadow = true;
fireLight.shadow.mapSize.set(1024, 1024);
fireLight.shadow.bias = -0.002;
fireLight.shadow.camera.far = 30;
scene.add(fireLight);

/* ---------- Ground ---------- */
const groundGeo = new THREE.PlaneGeometry(160, 160, 40, 40);
groundGeo.rotateX(-Math.PI / 2);
const gp = groundGeo.attributes.position;
for (let i = 0; i < gp.count; i++) {
  const x = gp.getX(i), z = gp.getZ(i);
  const d = Math.sqrt(x * x + z * z);
  const bump = Math.sin(x * 0.35) * Math.cos(z * 0.32) * 0.22
             + Math.sin(x * 0.11 + z * 0.17) * 0.4;
  // flatten near the fire
  const f = Math.min(1, Math.max(0, (d - 3) / 6));
  gp.setY(i, bump * f + (d < 60 ? 0 : 0));
}
groundGeo.computeVertexNormals();
const groundMat = new THREE.MeshStandardMaterial({
  color: 0x1c2a1a, flatShading: true, roughness: 1, metalness: 0
});
const ground = new THREE.Mesh(groundGeo, groundMat);
ground.receiveShadow = true;
scene.add(ground);

// dirt patch under the fire
const dirtGeo = new THREE.CircleGeometry(3.2, 24);
dirtGeo.rotateX(-Math.PI / 2);
const dirt = new THREE.Mesh(dirtGeo, new THREE.MeshStandardMaterial({
  color: 0x2a1f14, roughness: 1, flatShading: true
}));
dirt.position.y = 0.02;
dirt.receiveShadow = true;
scene.add(dirt);

/* ---------- Fire pit ring of stones ---------- */
const stoneMat = new THREE.MeshStandardMaterial({ color: 0x55585c, flatShading: true, roughness: 0.95 });
const ringCount = 11;
const fireRing = new THREE.Group();
for (let i = 0; i < ringCount; i++) {
  const a = (i / ringCount) * Math.PI * 2;
  const s = new THREE.DodecahedronGeometry(0.28 + Math.random() * 0.12, 0);
  const m = new THREE.Mesh(s, stoneMat);
  m.position.set(Math.cos(a) * 1.15, 0.18, Math.sin(a) * 1.15);
  m.rotation.set(Math.random() * 3, Math.random() * 3, Math.random() * 3);
  m.scale.y = 0.75;
  m.castShadow = true; m.receiveShadow = true;
  fireRing.add(m);
}
scene.add(fireRing);

/* ---------- Logs around the fire ---------- */
const logMat = new THREE.MeshStandardMaterial({ color: 0x3a2414, flatShading: true, roughness: 1 });
function makeLog(len, rad) {
  const g = new THREE.CylinderGeometry(rad, rad * 1.05, len, 6);
  g.rotateZ(Math.PI / 2);
  const m = new THREE.Mesh(g, logMat);
  m.castShadow = true; m.receiveShadow = true;
  return m;
}
for (let i = 0; i < 5; i++) {
  const a = (i / 5) * Math.PI * 2 + 0.3;
  const log = makeLog(2.6, 0.18);
  log.position.set(Math.cos(a) * 0.55, 0.35 + i * 0.09, Math.sin(a) * 0.55);
  log.rotation.y = a;
  log.rotation.z = 0.22;
  scene.add(log);
}
// a couple of bigger logs out in the clearing
for (let i = 0; i < 5; i++) {
  const a = Math.random() * Math.PI * 2;
  const r = 3.5 + Math.random() * 4;
  const log = makeLog(1.8 + Math.random() * 1.2, 0.22 + Math.random() * 0.08);
  log.position.set(Math.cos(a) * r, 0.22, Math.sin(a) * r);
  log.rotation.y = Math.random() * Math.PI;
  log.rotation.z = Math.PI / 2;
  scene.add(log);
}

/* ---------- Rocks around clearing ---------- */
const rockMat = new THREE.MeshStandardMaterial({ color: 0x4b4e55, flatShading: true, roughness: 1 });
for (let i = 0; i < 22; i++) {
  const a = Math.random() * Math.PI * 2;
  const r = 2.6 + Math.random() * 12;
  const s = 0.3 + Math.random() * 0.8;
  const geo = new THREE.DodecahedronGeometry(s, 0);
  const m = new THREE.Mesh(geo, rockMat);
  m.position.set(Math.cos(a) * r, s * 0.45, Math.sin(a) * r);
  m.rotation.set(Math.random() * 3, Math.random() * 3, Math.random() * 3);
  m.scale.set(1, 0.7 + Math.random() * 0.4, 1);
  m.castShadow = true; m.receiveShadow = true;
  scene.add(m);
}

/* ---------- Low-poly trees ---------- */
const trunkMat = new THREE.MeshStandardMaterial({ color: 0x2c1c10, flatShading: true, roughness: 1 });
const leafColors = [0x14351c, 0x1a4022, 0x0f2a17, 0x1d4a26];
const leafMats = leafColors.map(c => new THREE.MeshStandardMaterial({ color: c, flatShading: true, roughness: 1 }));

function makeTree(x, z, scale) {
  const tree = new THREE.Group();
  const h = 3.5 + Math.random() * 3.5;
  const trunkH = h * 0.45;
  const trunk = new THREE.Mesh(new THREE.CylinderGeometry(0.16, 0.26, trunkH, 5), trunkMat);
  trunk.position.y = trunkH / 2;
  trunk.castShadow = true; trunk.receiveShadow = true;
  tree.add(trunk);

  const layers = 3;
  for (let l = 0; l < layers; l++) {
    const r = (1.35 - l * 0.32) * (0.85 + Math.random() * 0.3);
    const lh = h * (0.34 - l * 0.05);
    const cone = new THREE.Mesh(new THREE.ConeGeometry(r, lh, 6), leafMats[(l + Math.floor(Math.random() * 4)) % 4]);
    cone.position.y = trunkH + l * lh * 0.55 + lh / 2;
    cone.rotation.y = Math.random() * Math.PI;
    cone.castShadow = true; cone.receiveShadow = true;
    tree.add(cone);
  }
  tree.position.set(x, 0, z);
  tree.scale.setScalar(scale);
  return tree;
}

const treeCount = 42;
let placed = 0;
let guard = 0;
while (placed < treeCount && guard < 2000) {
  guard++;
  const a = Math.random() * Math.PI * 2;
  const r = 6 + Math.random() * 30;
  const x = Math.cos(a) * r, z = Math.sin(a) * r;
  // no trees in the clearing
  const d = Math.sqrt(x * x + z * z);
  if (d < 6) continue;
  const s = 0.8 + Math.random() * 0.8;
  scene.add(makeTree(x, z, s));
  placed++;
}

/* ---------- Stars ---------- */
function makeStars() {
  const N = 1800;
  const pos = new Float32Array(N * 3);
  const col = new Float32Array(N * 3);
  const c = new THREE.Color();
  for (let i = 0; i < N; i++) {
    const theta = Math.random() * Math.PI * 2;
    const phi = Math.acos(Math.random() * 0.95); // upper hemisphere
    const R = 200;
    pos[i*3]   = R * Math.sin(phi) * Math.cos(theta);
    pos[i*3+1] = R * Math.cos(phi) + 5;
    pos[i*3+2] = R * Math.sin(phi) * Math.sin(theta);
    const t = Math.random();
    c.setHSL(0.55 + Math.random() * 0.12, 0.15 + Math.random() * 0.25, 0.7 + Math.random() * 0.3);
    col[i*3] = c.r; col[i*3+1] = c.g; col[i*3+2] = c.b;
    void t;
  }
  const g = new THREE.BufferGeometry();
  g.setAttribute('position', new THREE.BufferAttribute(pos, 3));
  g.setAttribute('color', new THREE.BufferAttribute(col, 3));
  const m = new THREE.PointsMaterial({
    size: 1.1, sizeAttenuation: true, vertexColors: true,
    transparent: true, opacity: 0.95, depthWrite: false,
    fog: false, blending: THREE.AdditiveBlending
  });
  return new THREE.Points(g, m);
}
const stars = makeStars();
scene.add(stars);

/* ---------- Moon ---------- */
const moonGroup = new THREE.Group();
const moonGeo = new THREE.IcosahedronGeometry(6, 1);
const moonMat = new THREE.MeshBasicMaterial({ color: 0xf2f4e0, fog: false });
const moon = new THREE.Mesh(moonGeo, moonMat);
moonGroup.add(moon);
// soft halo
const haloMat = new THREE.MeshBasicMaterial({ color: 0x9fb6e0, transparent: true, opacity: 0.12, fog: false, blending: THREE.AdditiveBlending, depthWrite: false });
const halo = new THREE.Mesh(new THREE.IcosahedronGeometry(11, 1), haloMat);
moonGroup.add(halo);
moonGroup.position.set(-70, 80, -110);
scene.add(moonGroup);

/* ---------- Flames ---------- */
const flameGroup = new THREE.Group();
scene.add(flameGroup);
const flames = [];
const flameColors = [0xffcf5a, 0xff9a2e, 0xff5c1a, 0xffe9a8];
for (let i = 0; i < 7; i++) {
  const geo = new THREE.ConeGeometry(0.32 + Math.random() * 0.15, 1.1 + Math.random() * 0.7, 5, 1);
  geo.translate(0, 0.55, 0);
  const mat = new THREE.MeshBasicMaterial({
    color: flameColors[i % flameColors.length],
    transparent: true, opacity: 0.85, depthWrite: false,
    blending: THREE.AdditiveBlending
  });
  const m = new THREE.Mesh(geo, mat);
  const a = Math.random() * Math.PI * 2;
  const r = Math.random() * 0.35;
  m.position.set(Math.cos(a) * r, 0.35, Math.sin(a) * r);
  m.userData = {
    baseY: 0.35, phase: Math.random() * Math.PI * 2,
    speed: 4 + Math.random() * 4,
    amp: 0.15 + Math.random() * 0.2,
    scaleBase: 0.85 + Math.random() * 0.4
  };
  flameGroup.add(m);
  flames.push(m);
}

// glowing embers base
const emberGeo = new THREE.SphereGeometry(0.5, 8, 6, 0, Math.PI * 2, 0, Math.PI / 2);
emberGeo.rotateX(Math.PI);
const emberMat = new THREE.MeshBasicMaterial({ color: 0xff6a1a, transparent: true, opacity: 0.7, blending: THREE.AdditiveBlending, depthWrite: false });
const embers = new THREE.Mesh(emberGeo, emberMat);
embers.position.y = 0.1;
embers.scale.y = 0.5;
scene.add(embers);

/* ---------- Sparks ---------- */
const SPARK_COUNT = 160;
const sparkPos = new Float32Array(SPARK_COUNT * 3);
const sparkVel = new Float32Array(SPARK_COUNT * 3);
const sparkLife = new Float32Array(SPARK_COUNT);
const sparkMaxLife = new Float32Array(SPARK_COUNT);

function spawnSpark(i) {
  const a = Math.random() * Math.PI * 2;
  const r = Math.random() * 0.4;
  sparkPos[i*3]   = Math.cos(a) * r;
  sparkPos[i*3+1] = 0.6 + Math.random() * 0.3;
  sparkPos[i*3+2] = Math.sin(a) * r;
  sparkVel[i*3]   = (Math.random() - 0.5) * 0.6;
  sparkVel[i*3+1] = 1.6 + Math.random() * 2.2;
  sparkVel[i*3+2] = (Math.random() - 0.5) * 0.6;
  sparkLife[i]    = 0;
  sparkMaxLife[i] = 1.2 + Math.random() * 1.6;
}
for (let i = 0; i < SPARK_COUNT; i++) { spawnSpark(i); sparkLife[i] = Math.random() * sparkMaxLife[i]; }

const sparkGeo = new THREE.BufferGeometry();
sparkGeo.setAttribute('position', new THREE.BufferAttribute(sparkPos, 3));
const sparkColors = new Float32Array(SPARK_COUNT * 3);
for (let i = 0; i < SPARK_COUNT; i++) {
  sparkColors[i*3] = 1.0; sparkColors[i*3+1] = 0.6; sparkColors[i*3+2] = 0.2;
}
sparkGeo.setAttribute('color', new THREE.BufferAttribute(sparkColors, 3));
const sparkMat = new THREE.PointsMaterial({
  size: 0.11, sizeAttenuation: true, vertexColors: true,
  transparent: true, opacity: 0.95, depthWrite: false,
  blending: THREE.AdditiveBlending, fog: true
});
const sparks = new THREE.Points(sparkGeo, sparkMat);
scene.add(sparks);

/* ---------- Animate ---------- */
const clock = new THREE.Clock();
const hint = document.getElementById('hint');
let hintFaded = false;

function animate() {
  const t = clock.getElapsedTime();
  const dt = Math.min(clock.getDelta(), 0.05);

  // flames
  for (const f of flames) {
    const u = f.userData;
    const flick = Math.sin(t * u.speed + u.phase) * 0.5 + 0.5;
    const flick2 = Math.sin(t * (u.speed * 1.7) + u.phase * 2.3) * 0.5 + 0.5;
    f.position.y = u.baseY + flick * u.amp;
    f.scale.set(
      u.scaleBase * (0.85 + flick * 0.3),
      u.scaleBase * (0.8 + flick2 * 0.6),
      u.scaleBase * (0.85 + flick * 0.3)
    );
    f.rotation.y = t * 0.8 + u.phase;
    f.material.opacity = 0.55 + flick * 0.4;
  }
  embers.material.opacity = 0.5 + Math.sin(t * 9) * 0.15;
  flameGroup.rotation.y = t * 0.2;

  // fire light flicker
  const n = Math.sin(t * 11.3) * 0.35 + Math.sin(t * 23.7) * 0.2 + Math.sin(t * 4.1) * 0.3;
  fireLight.intensity = 6 + n * 3.5;
  fireLight.position.x = Math.sin(t * 9) * 0.15;
  fireLight.position.z = Math.cos(t * 7.3) * 0.15;
  fireLight.position.y = 1.5 + Math.sin(t * 5) * 0.15;

  // sparks
  for (let i = 0; i < SPARK_COUNT; i++) {
    sparkLife[i] += dt;
    if (sparkLife[i] > sparkMaxLife[i]) { spawnSpark(i); continue; }
    sparkPos[i*3]   += sparkVel[i*3] * dt;
    sparkPos[i*3+1] += sparkVel[i*3+1] * dt;
    sparkPos[i*3+2] += sparkVel[i*3+2] * dt;
    sparkVel[i*3]   += Math.sin(t * 2 + i) * 0.4 * dt;
    sparkVel[i*3+2] += Math.cos(t * 1.7 + i) * 0.4 * dt;
    sparkVel[i*3+1] -= 0.6 * dt;
  }
  sparkGeo.attributes.position.needsUpdate = true;
  // fade sparks by life using a second attribute trick via size
  sparkMat.opacity = 0.9;

  // stars slow rotation
  stars.rotation.y = t * 0.005;

  controls.update();
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}
animate();

/* fade hint after a few seconds */
setTimeout(() => { if (!hintFaded) { hint.style.opacity = '0'; hintFaded = true; } }, 5000);

/* ---------- Resize ---------- */
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});
</script>
</body>
</html>