Xing4.0 29B A4B (free)
by XingChenReasoning1 console error
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>