Models 44

Xing4.0 29B A4B (free)

by XingChenReasoning1 console error
Open in new window

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

How it went

Total time
1 min 59 s
Cost
$0
Output tokens
6,021
Spent thinking
0 tokens
Speed
51.2 tok/s
Console errors
1
First token after
1.3 s
First visible text after
1.3 s
Prompt tokens
616
Code size
14.7 KB
Finish reason
stop
Served by
Phala
Model id
xingchen-agi/xing4.0-29b-a4b:free
Reasoning sent
{"effort":"none"}
Max output
32,000 tokens
Generated
Nov 9, 2026

Console errors

Caught in the first 3 seconds after load, in a sandboxed iframe like the one above.

Identifier 'makeRock' has already been declared

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 · 404 lines

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Campfire in the Forest - Low Poly 3D Scene</title>
<style>
  html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; background: #050510; font-family: sans-serif; }
  #app { width: 1280px; height: 800px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
  #loading { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #050510; color: #88ccff; display: flex; align-items: center; justify-content: center; font-size: 22px; z-index: 100; }
</style>
</head>
<body>
<div id="loading">Loading scene...</div>
<div id="app"></div>

<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>

<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

// ===== Setup =====
const container = document.getElementById('app');
const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x0a0a1a, 0.008);

const camera = new THREE.PerspectiveCamera(60, 1280 / 800, 0.1, 500);
camera.position.set(0, 9, 16);
camera.lookAt(0, 2, 0);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(1280, 800);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.1;
container.appendChild(renderer.domElement);

// ===== Controls (keep camera above ground) =====
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.08;
controls.minDistance = 4;
controls.maxDistance = 45;
controls.maxPolarAngle = Math.PI / 2 - 0.02; // stay above ground
controls.target.set(0, 3, 0);

// ===== Lighting (daylight → warm fire) =====
const ambientLight = new THREE.AmbientLight(0x1a1a3e, 0.35);
scene.add(ambientLight);

const moonLight = new THREE.DirectionalLight(0xbbddff, 0.3);
moonLight.position.set(-25, 30, -15);
moonLight.castShadow = true;
moonLight.shadow.mapSize.set(1024, 1024);
moonLight.shadow.camera.near = 1;
moonLight.shadow.camera.far = 80;
moonLight.shadow.camera.left = -20;
moonLight.shadow.camera.right = 20;
moonLight.shadow.camera.top = 20;
moonLight.shadow.camera.bottom = -20;
moonLight.shadow.bias = -0.001;
scene.add(moonLight);

// Fire warmth light (warm point light)
const fireLight = new THREE.PointLight(0xff7733, 0, 12, 2);
fireLight.position.set(0, 3, 0);
scene.add(fireLight);

// ===== Ground =====
const groundGeo = new THREE.PlaneGeometry(60, 60, 32, 32);
// Slight unevenness
const pos = groundGeo.attributes.position;
for (let i = 0; i < pos.count; i++) {
  const x = pos.getX(i), y = pos.getY(i);
  const d = Math.sqrt(x*x + y*y);
  pos.setZ(i, (Math.sin(x*0.1)*Math.cos(y*0.13)) * 0.3);
}
groundGeo.computeVertexNormals();
const groundMat = new THREE.MeshStandardMaterial({ color: 0x2a3a2a, roughness: 0.95, metalness: 0.0 });
const ground = new THREE.Mesh(groundGeo, groundMat);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);

// ===== Helper: create a low-poly rock =====
function makeRock(size) {
  const g = new THREE.IcosahedronGeometry(size, 0);
  // Flatter bottom
  const p = g.attributes.position;
  for (let i = 0; i < p.count; i++) {
    p.setX(i, p.getX(i));
  }
  return new THREE.Mesh(g, new THREE.MeshStandardMaterial({ color: 0x5a5a5a, roughness: 0.8 }));
}
function makeRock(radius, flatScale, color) {
  const g = new THREE.IcosahedronGeometry(radius, 0);
  const p = g.attributes.position;
  for (let i = 0; i < p.count; i++) {
    p.setX(i, p.getX(i));
    p.setY(i, p.getY(i) * flatScale);
  }
  g.computeVertexNormals();
  return new THREE.Mesh(g, new THREE.MeshStandardMaterial({ color: color || 0x6a6a6a, roughness: 0.85 }));
}

// ===== Helper: low-poly tree (trunk + sphere foliage) =====
function makeTree(height, radius, trunkColor) {
  const group = new THREE.Group();
  // Trunk
  const trunkH = height * 0.7;
  const trunkGeo = new THREE.CylinderGeometry(radius*0.25, radius*0.4, trunkH, 6);
  const trunkMat = new THREE.MeshStandardMaterial({ color: trunkColor, roughness: 0.9 });
  const trunk = new THREE.Mesh(trunkGeo, trunkMat);
  trunk.position.y = trunkH / 2;
  trunk.castShadow = true;
  group.add(trunk);
  // Foliage: a few cones/spheres stacked
  const foliageMat = new THREE.MeshStandardMaterial({ color: 0x1f3d1f, roughness: 0.85 });
  const fGeo = new THREE.ConeGeometry(radius*1.1, height*0.5, 5);
  const f1 = new THREE.Mesh(fGeo, foliageMat);
  f1.position.y = trunkH + height*0.15;
  f1.castShadow = true;
  group.add(f1);
  const fGeo2 = new THREE.ConeGeometry(radius*0.9, height*0.4, 5);
  const f2 = new THREE.Mesh(fGeo2, foliageMat);
  f2.position.y = trunkH + height*0.5;
  f2.castShadow = true;
  group.add(f2);
  const fGeo3 = new THREE.SphereGeometry(radius*0.7, 6, 4);
  const f3 = new THREE.Mesh(fGeo3, foliageMat);
  f3.position.y = trunkH + height*0.62;
  f3.castShadow = true;
  group.add(f3);
  return group;
}

// ===== Place rocks around fire =====
const rocks = [];
for (let i = 0; i < 8; i++) {
  const angle = (i / 8) * Math.PI * 2 + Math.random()*0.3;
  const dist = 1.8 + Math.random()*1.2;
  const r = makeRock(0.4 + Math.random()*0.5, 0.6);
  r.position.set(Math.cos(angle)*dist, 0.05, Math.sin(angle)*dist);
  r.rotation.y = Math.random()*Math.PI*2;
  scene.add(r);
  rocks.push(r);
}
// Large log as a base
const logGeo = new THREE.CylinderGeometry(0.12, 0.16, 3.0, 6);
const logMat = new THREE.MeshStandardMaterial({ color: 0x4a2818, roughness: 0.85 });
const logs = [
  new THREE.Mesh(logGeo, logMat).translateY(1.5),
  new THREE.Mesh(logGeo, logMat).rotateZ(Math.PI/2).translateY(1.5),
];
logs[0].position.set(0.15, 0, 0.05);
logs[1].position.set(-0.15, 0.1, -0.05);
logs.forEach(l => l.castShadow = true, l.receiveShadow = true);
scene.add(logs[0], logs[1]);

// ===== Campfire Fire Animation (model) =====
const fireGroup = new THREE.Group();
fireGroup.position.set(0, 0, 0);
scene.add(fireGroup);

// Logs around fire pit
const firePitLogs = [];
const pitLogsGeo = new THREE.CylinderGeometry(0.15, 0.2, 0.1, 8);
const pitLogMat = new THREE.MeshStandardMaterial({ color: 0x3a1e10, roughness: 0.9 });
for (let i = 0; i < 4; i++) {
  const a = (i / 4) * Math.PI * 2;
  const log = new THREE.Mesh(pitLogsGeo, pitLogMat);
  log.position.set(Math.cos(a)*0.35, 0.05, Math.sin(a)*0.35);
  log.rotation.z = -Math.PI/2;
  log.castShadow = true;
  fireGroup.add(log);
  firePitLogs.push(log);
}

// Fire model — a series of cones with vertex displacement for flame shape
const flameCount = 5;
const flames = [];
for (let i = 0; i < flameCount; i++) {
  const h = 1.2 + Math.random()*0.8;
  const geo = new THREE.ConeGeometry(0.25 + Math.random()*0.1, h, 7, 1);
  // Displace vertices for organic flame look
  const p = geo.attributes.position;
  for (let v = 0; v < p.count; v++) {
    const y = p.getY(v);
    const t = y / h; // 0 bottom, 1 tip
    const wave = Math.sin(t * Math.PI * 2 + i) * 0.15 * (1-t);
    const sway = Math.sin(t * Math.PI * 6 + i*1.7) * 0.06 * (1-t);
    p.setX(v, p.getX(v) + wave * (Math.random()-0.5) * 0.3);
    p.setZ(v, p.getZ(v) + wave * (Math.random()-0.5) * 0.3);
    p.setX(v, p.getX(v) + sway);
    p.setZ(v, p.getZ(v) + sway);
  }
  geo.computeVertexNormals();
  const mat = new THREE.MeshBasicMaterial({ color: new THREE.Color().setHSL(0.06 + Math.random()*0.03, 1, 0.45 + Math.random()*0.15) });
  const flame = new THREE.Mesh(geo, mat);
  flame.position.set((Math.random()-0.5)*0.1, 0.05, (Math.random()-0.5)*0.1);
  flame.userData.phase = Math.random() * Math.PI * 2;
  fireGroup.add(flame);
  flames.push(flame);
}

// Base glow disc under flames
const glowGeo = new THREE.CircleGeometry(0.4, 16);
const glowMat = new THREE.MeshBasicMaterial({ color: 0xff8833, transparent: true, opacity: 0.35, blending: THREE.AdditiveBlending });
const glow = new THREE.Mesh(glowGeo, glowMat);
glow.rotation.x = -Math.PI/2;
glow.position.y = 0.01;
fireGroup.add(glow);

// ===== Sparks Particle System =====
const sparkCount = 80;
const sparkGeo = new THREE.BufferGeometry();
const sparkPositions = new Float32Array(sparkCount * 3);
const sparkVelocities = [];
const sparkLifetimes = [];
const sparkColors = [];
for (let i = 0; i < sparkCount; i++) {
  sparkPositions[i*3]   = (Math.random()-0.5)*0.3;
  sparkPositions[i*3+1] = 0.1 + Math.random()*1.0;
  sparkPositions[i*3+2] = (Math.random()-0.5)*0.3;
  sparkVelocities.push({ x:(Math.random()-0.5)*0.04, y:0.02+Math.random()*0.05, z:(Math.random()-0.5)*0.04 });
  sparkLifetimes.push(Math.random());
  sparkColors.push(1.0, 0.8 + Math.random()*0.2, 0.3);
}
sparkGeo.setAttribute('position', new THREE.BufferAttribute(sparkPositions, 3));
sparkGeo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(sparkColors), 3));
const sparkMat = new THREE.PointsMaterial({ size: 0.12, vertexColors: true, transparent: true, opacity: 0.9, blending: THREE.AdditiveBlending, depthWrite: false });
const sparks = new THREE.Points(sparkGeo, sparkMat);
scene.add(sparks);

// ===== Trees (at least 30) =====
function randomTreePosition(excludeRoughly) {
  for (let attempt = 0; attempt < 30; attempt++) {
    const angle = Math.random() * Math.PI * 2;
    const dist = 8 + Math.random() * 28;
    const x = Math.cos(angle) * dist;
    const z = Math.sin(angle) * dist;
    // avoid too close to fire center
    if (Math.sqrt(x*x + z*z) < 6.5) continue;
    // avoid on water/clearing edges? just keep random
    return { x, z };
  }
  return null;
}
let treeCount = 0;
while (treeCount < 36) {
  const pos = randomTreePosition();
  if (!pos) break;
  const height = 2.5 + Math.random() * 2.5;
  const radius = 0.5 + Math.random() * 0.5;
  const tree = makeTree(height, radius, 0x2a4a2a);
  // slight scale variation
  const s = 0.8 + Math.random() * 0.5;
  tree.scale.setScalar(s);
  // bias tree slightly away from exact center but not too far
  const angleFromCenter = Math.atan2(pos.z, pos.x);
  const arcDist = (Math.PI * 0.6) * Math.random();
  const tx = Math.cos(angleFromCenter + Math.PI/2) * arcDist * Math.sign(pos.x||1) * 0 + Math.cos(angleFromCenter) * Math.abs(pos.dist || 18);
  // simpler: just place at computed distance, slight y offset
  const d = Math.sqrt(pos.x*pos.x + pos.z*pos.z);
  const ax = Math.cos(angleFromCenter + Math.PI*0.3 + Math.random()*0.2) * d;
  const az = Math.sin(angleFromCenter + Math.PI*0.3 + Math.random()*0.2) * d;
  tree.position.set(ax, 0, az);
  tree.rotation.y = Math.random() * Math.PI * 2;
  scene.add(tree);
  treeCount++;
}

// ===== Stars & Moon =====
// Stars as points
const starCount = 600;
const starGeo = new THREE.BufferGeometry();
const starPos = new Float32Array(starCount * 3);
for (let i = 0; i < starCount; i++) {
  const theta = Math.random() * Math.PI * 2;
  const phi = Math.acos(2 * Math.random() - 1);
  const r = 80 + Math.random() * 40;
  starPos[i*3]   = r * Math.sin(phi) * Math.cos(theta);
  starPos[i*3+1] = r * Math.cos(phi);
  starPos[i*3+2] = r * Math.sin(phi) * Math.sin(theta);
}
starGeo.setAttribute('position', new THREE.BufferAttribute(starPos, 3));
const starMat = new THREE.PointsMaterial({ color: 0xffffff, size: 0.5, transparent: true, opacity: 0.9 });
const stars = new THREE.Points(starGeo, starMat);
scene.add(stars);

// Moon
const moonGeo = new THREE.SphereGeometry(1.8, 24, 20);
const moonMat = new THREE.MeshStandardMaterial({ color: 0xeef0ff, roughness: 0.9, emissive: 0x111133, emissiveIntensity: 0.15 });
const moon = new THREE.Mesh(moonGeo, moonMat);
moon.position.set(-30, 25, -25);
moon.rotation.y = 0.3;
scene.add(moon);

// ===== Interaction Overlay =====
const overlay = document.createElement('div');
overlay.id = 'startOverlay';
overlay.textContent = 'Click or press any key to start the demo';
overlay.style.cssText = 'position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(5,5,16,0.75);color:#aaddff;font-size:20px;z-index:50;cursor:pointer;transition:opacity .5s;font-family:sans-serif;';
document.body.appendChild(overlay);

let started = false;
let overlayHidden = false;
function hideOverlay() {
  if (started) return;
  if (!overlayHidden) {
    overlayHidden = true;
    overlay.style.opacity = '0';
    setTimeout(() => overlay.remove(), 500);
  }
}

function startDemo() {
  if (started) return;
  started = true;
  hideOverlay();
}

window.addEventListener('keydown', () => { if (!started) startDemo(); }, { once: true });
overlay.addEventListener('click', startDemo);
window.addEventListener('pointerdown', () => { if (!started) startDemo(); }, { once: true });

// ===== Animate =====
const clock = new THREE.Clock();
let frame = 0;
function animate() {
  requestAnimationFrame(animate);
  const dt = Math.min(clock.getDelta(), 0.05);
  frame++;

  // Fire light flicker
  fireLight.intensity = 1.5 + Math.sin(frame * 0.15) * 0.3 + Math.sin(frame * 0.31) * 0.2 + Math.random() * 0.2;
  fireLight.position.y = 0.05 + Math.sin(frame * 0.2) * 0.05;

  // Flames animation
  flames.forEach((flame, i) => {
    const t = flame.userData.phase;
    flame.scale.y = 1 + Math.sin(frame * 0.1 + t) * 0.12;
    flame.scale.x = 1 + Math.sin(frame * 0.13 + t*1.3) * 0.08;
    flame.position.y = 0.05 + Math.sin(frame * 0.15 + t) * 0.05;
    flame.rotation.z = Math.sin(frame * 0.08 + t) * 0.08;
  });

  // Glow flicker
  glow.material.opacity = 0.3 + Math.sin(frame * 0.2) * 0.1 + Math.random()*0.05;

  // Sparks update
  const sp = sparkGeo.attributes.position.array;
  for (let i = 0; i < sparkCount; i++) {
    spLifetimes[i] += dt * 0.8;
    if (spLifetimes[i] > 2) {
      // respawn
      spLifetime[i] = 0;
      sp[i*3]   = (Math.random()-0.5)*0.3;
      sp[i*3+1] = 0.1 + Math.random()*0.5;
      sp[i*3+2] = (Math.random()-0.5)*0.3;
      sparkVelocities[i] = { x:(Math.random()-0.5)*0.04, y:0.02+Math.random()*0.05, z:(Math.random()-0.5)*0.04 };
      sparkColors[i*3]=1.0; sparkColors[i*3+1]=0.8; sparkColors[i*3+2]=0.3;
    } else {
      sp[i*3]   += sparkVelocities[i].x;
      sp[i*3+1] += sparkVelocities[i].y;
      sp[i*3+2] += sparkVelocities[i].z;
      sparkVelocities[i].y += 0.0006; // gravity slow
      // fade color
      const fade = Math.max(0, 1 - spLifetimes[i]/2);
      sparkColors[i*3]   = 1.0 * fade;
      sparkColors[i*3+1] = 0.8 * fade;
      sparkColors[i*3+2] = 0.3 * fade;
    }
  }
  sparkGeo.attributes.position.needsUpdate = true;
  sparkGeo.attributes.color.needsUpdate = true;

  // Slow rotate stars
  stars.rotation.y += dt * 0.005;

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

animate();

// ===== Resize =====
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>