Muse Ember
by MetaReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 9.8 s
- Cost
- $0.0035
- Output tokens
- 6,786
- Spent thinking
- 638 tokens
- Speed
- 818 tok/s
- Console errors
- 0
- First token after
- 1.5 s
- First visible text after
- 2.1 s
- Prompt tokens
- 574
- Code size
- 18.6 KB
- Finish reason
- stop
- Served by
- Meta
- Model id
meta/muse-ember- Reasoning sent
{"effort":"low"}- Max output
- 943,718 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 · 405 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Campfire Clearing — Low-Poly Night Forest</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
html,body{width:100%;height:100%;overflow:hidden;background:#060b1a;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif}
#app{position:fixed;inset:0}
canvas{display:block}
.ui{position:fixed;top:0;left:0;right:0;padding:18px 20px;display:flex;justify-content:space-between;align-items:flex-start;pointer-events:none;z-index:10;color:#fff;gap:16px}
.card{pointer-events:auto;background:rgba(8,12,28,.58);backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,.12);border-radius:14px;padding:14px 16px;box-shadow:0 10px 30px rgba(0,0,0,.35)}
.title{font-size:15px;letter-spacing:.16em;text-transform:uppercase;font-weight:800}
.title span{color:#ffb347}
.sub{font-size:12.5px;line-height:1.5;color:#cbd5e1;margin-top:4px;max-width:360px}
.pill{display:inline-flex;align-items:center;gap:6px;font-size:11px;letter-spacing:.08em;text-transform:uppercase;background:rgba(255,150,50,.16);color:#ffcf9a;border:1px solid rgba(255,170,80,.3);padding:5px 9px;border-radius:999px;margin-top:9px}
.dot{width:7px;height:7px;border-radius:50%;background:#ff9d3c;box-shadow:0 0 10px #ff9d3c;animation:pulse 1.6s infinite}
@keyframes pulse{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(1.5);opacity:.6}}
.controls{text-align:right;font-size:12px;color:#dbeafe;line-height:1.7;min-width:170px}
.controls b{color:#fff;font-weight:700}
.kbd{display:inline-block;background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.18);border-bottom-width:2px;border-radius:6px;padding:1px 6px;font-size:11px;color:#fff}
.badge{position:fixed;left:50%;bottom:16px;transform:translateX(-50%);background:rgba(5,8,20,.62);backdrop-filter:blur(8px);border:1px solid rgba(255,255,255,.12);color:#e2e8f0;padding:8px 14px;border-radius:999px;font-size:12px;white-space:nowrap;z-index:10;pointer-events:none}
.badge i{color:#ffb347;font-style:normal}
@media(max-width:640px){.controls{display:none}.sub{max-width:260px}.title{font-size:13px} .badge{font-size:11px;bottom:12px;max-width:92vw;white-space:normal;text-align:center}}
</style>
</head>
<body>
<div id="app"></div>
<div class="ui">
<div class="card">
<div class="title">🔥 Campfire <span>Clearing</span></div>
<div class="sub">A low-poly forest at night — flickering flames, rising sparks, pine trees in the fog and a bright moon overhead.</div>
<div class="pill"><span class="dot"></span> Live • 34 trees • Real-time shadows</div>
</div>
<div class="card controls">
<div><b>Drag</b> to orbit • <b>Scroll</b> to zoom</div>
<div><b>Right-drag</b> to pan</div>
<div style="margin-top:6px;color:#94a3b8">Camera stays above the ground</div>
<div style="margin-top:6px"><span class="kbd">R</span> reset view <span class="kbd">Space</span> pause flicker</div>
</div>
</div>
<div class="badge">✦ Drag to explore the clearing — the fire lights the trees around it ✦</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';
const container = document.getElementById('app');
const renderer = new THREE.WebGLRenderer({ antialias:true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.1;
container.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x060b1a);
scene.fog = new THREE.FogExp2(0x0a1428, 0.028);
const camera = new THREE.PerspectiveCamera(52, window.innerWidth/window.innerHeight, 0.1, 300);
const START_POS = new THREE.Vector3(9.5, 5.2, 12.5);
const START_TARGET = new THREE.Vector3(0, 1.1, 0);
camera.position.copy(START_POS);
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.copy(START_TARGET);
controls.enableDamping = true;
controls.dampingFactor = 0.06;
controls.autoRotate = true;
controls.autoRotateSpeed = 0.35;
controls.minDistance = 4;
controls.maxDistance = 38;
controls.minPolarAngle = 0.15;
controls.maxPolarAngle = Math.PI * 0.495; // stays above ground
controls.enablePan = true;
controls.panSpeed = 0.6;
// pause auto-rotate on interaction, resume after idle
let idleTimer;
controls.addEventListener('start', ()=>{ controls.autoRotate=false; clearTimeout(idleTimer); });
controls.addEventListener('end', ()=>{ idleTimer=setTimeout(()=>controls.autoRotate=true, 4000); });
// ---------- Lights ----------
scene.add(new THREE.HemisphereLight(0x2a3a66, 0x0b1a0f, 0.55));
const moonLight = new THREE.DirectionalLight(0x8fb4ff, 0.55);
moonLight.position.set(-18, 28, -14);
moonLight.castShadow = true;
moonLight.shadow.mapSize.set(1024,1024);
moonLight.shadow.camera.left=-30; moonLight.shadow.camera.right=30;
moonLight.shadow.camera.top=30; moonLight.shadow.camera.bottom=-30;
moonLight.shadow.camera.far=80;
scene.add(moonLight);
const fireLight = new THREE.PointLight(0xff8c2f, 38, 28, 1.8);
fireLight.position.set(0, 1.6, 0);
fireLight.castShadow = true;
fireLight.shadow.mapSize.set(1024,1024);
fireLight.shadow.bias = -0.004;
scene.add(fireLight);
const fireLight2 = new THREE.PointLight(0xff6a00, 10, 10, 2);
fireLight2.position.set(0,0.5,0);
scene.add(fireLight2);
// ---------- Materials (flat shading low-poly) ----------
const mat = {
ground: new THREE.MeshStandardMaterial({ color:0x1e3d24, roughness:1, flatShading:true }),
ground2: new THREE.MeshStandardMaterial({ color:0x2a4a28, roughness:1, flatShading:true }),
dirt: new THREE.MeshStandardMaterial({ color:0x3b2b1e, roughness:1, flatShading:true }),
trunk: new THREE.MeshStandardMaterial({ color:0x4a2f1d, roughness:1, flatShading:true }),
pineDark: new THREE.MeshStandardMaterial({ color:0x14351e, roughness:1, flatShading:true }),
pine: new THREE.MeshStandardMaterial({ color:0x1b5a2e, roughness:1, flatShading:true }),
pineLight: new THREE.MeshStandardMaterial({ color:0x2a7a3e, roughness:1, flatShading:true }),
rock: new THREE.MeshStandardMaterial({ color:0x6b7280, roughness:1, flatShading:true }),
rockDark: new THREE.MeshStandardMaterial({ color:0x4b5563, roughness:1, flatShading:true }),
log: new THREE.MeshStandardMaterial({ color:0x5c3a22, roughness:1, flatShading:true }),
woodSeat: new THREE.MeshStandardMaterial({ color:0x6b4426, roughness:1, flatShading:true }),
flameOuter: new THREE.MeshStandardMaterial({ color:0xff6a00, emissive:0xff5500, emissiveIntensity:2.2, transparent:true, opacity:0.92, flatShading:true }),
flameMid: new THREE.MeshStandardMaterial({ color:0xffaa00, emissive:0xff9900, emissiveIntensity:2.6, transparent:true, opacity:0.95, flatShading:true }),
flameInner: new THREE.MeshStandardMaterial({ color:0xffe27a, emissive:0xffdd55, emissiveIntensity:3, transparent:true, opacity:0.98, flatShading:true }),
};
// ---------- Ground ----------
const groundGeo = new THREE.CircleGeometry(55, 48);
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.hypot(x,y);
const h = d<6 ? (Math.random()*0.08) : (Math.sin(x*0.35)*0.35 + Math.cos(y*0.3)*0.3 + Math.random()*0.25) * Math.min(1,(d-6)/10);
pos.setZ(i, h);
}
groundGeo.computeVertexNormals();
const ground = new THREE.Mesh(groundGeo, mat.ground);
ground.rotation.x = -Math.PI/2;
ground.receiveShadow = true;
scene.add(ground);
// dirt patch in clearing
const dirt = new THREE.Mesh(new THREE.CircleGeometry(4.2, 24), mat.dirt);
dirt.rotation.x=-Math.PI/2; dirt.position.y=0.02; dirt.receiveShadow=true;
scene.add(dirt);
const dirt2 = new THREE.Mesh(new THREE.CircleGeometry(2.6, 20), new THREE.MeshStandardMaterial({color:0x2b1f16, roughness:1, flatShading:true}));
dirt2.rotation.x=-Math.PI/2; dirt2.position.y=0.035; dirt2.receiveShadow=true;
scene.add(dirt2);
// path
for(let i=0;i<14;i++){
const p=new THREE.Mesh(new THREE.CircleGeometry(0.55+Math.random()*0.4,7), mat.ground2);
const t=i/13;
p.position.set(3.5 + t*16 + Math.sin(t*6)*1.2, 0.015, 2.5 + Math.cos(t*4)*1.5);
p.rotation.x=-Math.PI/2; p.receiveShadow=true; scene.add(p);
}
// ---------- Helpers ----------
function seededRand(seed){ let s=seed; return ()=>{ s=(s*16807)%2147483647; return (s-1)/2147483646; }; }
const rand = seededRand(42);
function makeTree(scale=1, variant=0){
const g=new THREE.Group();
const trunkH=1.2*scale;
const trunk=new THREE.Mesh(new THREE.CylinderGeometry(0.22*scale,0.32*scale,trunkH,6), mat.trunk);
trunk.position.y=trunkH/2; trunk.castShadow=true; g.add(trunk);
const mats=[mat.pineDark, mat.pine, mat.pineLight];
const pineMat=mats[variant % mats.length];
const layers=3;
for(let i=0;i<layers;i++){
const r=(1.5 - i*0.38)*scale;
const h=1.6*scale;
const cone=new THREE.Mesh(new THREE.ConeGeometry(r,h,7), pineMat);
cone.position.y=trunkH + 0.6*scale + i*0.75*scale;
cone.castShadow=true; cone.receiveShadow=true;
g.add(cone);
}
const top=new THREE.Mesh(new THREE.ConeGeometry(0.45*scale,0.9*scale,6), pineMat);
top.position.y=trunkH+2.6*scale; top.castShadow=true; g.add(top);
return g;
}
// ---------- Trees (>=30) ----------
const treePositions=[];
function placeTrees(){
// ring + scattered clearing edge
for(let i=0;i<34;i++){
let x,z,d;
let tries=0;
do{
const angle=rand()*Math.PI*2;
d=11 + rand()*28; // 11..39
x=Math.cos(angle)*d; z=Math.sin(angle)*d;
tries++;
}while(tries<20 && treePositions.some(p=>Math.hypot(p[0]-x,p[1]-z)<3.2));
treePositions.push([x,z]);
const s=0.8+rand()*1.4;
const tree=makeTree(s, Math.floor(rand()*3));
tree.position.set(x,0,z);
tree.rotation.y=rand()*Math.PI*2;
tree.rotation.z=(rand()-0.5)*0.08;
scene.add(tree);
}
// a few nearer framing trees (not on fire)
[[-8.5,-6.5,1.15],[9,-7,1.3],[-10,4,1.0],[7.5,8.5,0.9]].forEach(([x,z,s])=>{
const t=makeTree(s,1); t.position.set(x,0,z); t.rotation.y=rand()*3; scene.add(t);
});
}
placeTrees();
// bushes & grass tufts
for(let i=0;i<28;i++){
const a=rand()*Math.PI*2, d=5.5+rand()*18;
const x=Math.cos(a)*d, z=Math.sin(a)*d;
if(Math.random()<0.5){
const b=new THREE.Mesh(new THREE.IcosahedronGeometry(0.45+rand()*0.5,0), mat.pine);
b.position.set(x,0.3,z); b.scale.y=0.7; b.castShadow=true; b.receiveShadow=true; scene.add(b);
}else{
for(let k=0;k<3;k++){
const tuft=new THREE.Mesh(new THREE.ConeGeometry(0.14,0.55,5), mat.pineLight);
tuft.position.set(x+(rand()-0.5)*0.5,0.25,z+(rand()-0.5)*0.5); tuft.castShadow=true; scene.add(tuft);
}
}
}
// ---------- Rocks ----------
for(let i=0;i<12;i++){
const a=rand()*Math.PI*2, d=4.5+rand()*12;
const rock=new THREE.Mesh(new THREE.DodecahedronGeometry(0.28+rand()*0.55,0), rand()>0.5?mat.rock:mat.rockDark);
rock.position.set(Math.cos(a)*d, 0.18, Math.sin(a)*d);
rock.rotation.set(rand()*3,rand()*3,rand()*3);
rock.castShadow=true; rock.receiveShadow=true; scene.add(rock);
}
// fire ring stones
for(let i=0;i<10;i++){
const a=(i/10)*Math.PI*2;
const s=new THREE.Mesh(new THREE.DodecahedronGeometry(0.22+rand()*0.12,0), mat.rock);
s.position.set(Math.cos(a)*1.05,0.18,Math.sin(a)*1.05);
s.rotation.set(rand()*2,rand()*2,rand()*2); s.castShadow=true; s.receiveShadow=true; scene.add(s);
}
// ---------- Logs ----------
function makeLog(r=0.22,len=1.6){
const log=new THREE.Mesh(new THREE.CylinderGeometry(r,r*0.9,len,7), mat.log);
log.castShadow=true; log.receiveShadow=true;
const capMat=new THREE.MeshStandardMaterial({color:0xc9a06a, roughness:1, flatShading:true});
const cap=new THREE.Mesh(new THREE.CylinderGeometry(r*0.92,r*0.92,0.02,7), capMat);
cap.position.y=len/2+0.01; log.add(cap);
return log;
}
// teepee firewood
for(let i=0;i<7;i++){
const a=(i/7)*Math.PI*2;
const log=makeLog(0.14,1.1);
log.position.set(Math.cos(a)*0.35,0.22,Math.sin(a)*0.35);
log.rotation.z=Math.cos(a)*0.85;
log.rotation.x=Math.sin(a)*0.85;
scene.add(log);
}
// horizontal burning logs
for(let i=0;i<3;i++){
const log=makeLog(0.16,1.0);
log.rotation.z=Math.PI/2; log.rotation.y=i*1.1;
log.position.set((rand()-0.5)*0.3,0.18,(rand()-0.5)*0.3);
scene.add(log);
}
// seating logs around fire
for(let i=0;i<4;i++){
const a=(i/4)*Math.PI*2 + 0.6;
const seat=makeLog(0.28,1.9);
seat.rotation.z=Math.PI/2;
seat.rotation.y=-a+Math.PI/2;
seat.position.set(Math.cos(a)*3.1,0.28,Math.sin(a)*3.1);
scene.add(seat);
// log end caps visible - already added
}
// extra fallen logs
[[5,-4,0.8],[-6,6,-0.6],[12,6,0.3]].forEach(([x,z,r])=>{
const l=makeLog(0.24,2.2); l.rotation.z=Math.PI/2; l.rotation.y=r; l.position.set(x,0.24,z); scene.add(l);
});
// ---------- Flames ----------
const flameGroup=new THREE.Group(); scene.add(flameGroup);
function flameCone(r,h,matl,y){
const m=new THREE.Mesh(new THREE.ConeGeometry(r,h,7), matl);
m.position.y=y; m.castShadow=false; flameGroup.add(m); return m;
}
const flameOuter=flameCone(0.55,1.35,mat.flameOuter,0.95);
const flameMid=flameCone(0.38,1.05,mat.flameMid,0.9);
const flameInner=flameCone(0.22,0.7,mat.flameInner,0.8);
// embers glow base
const ember=new THREE.Mesh(new THREE.IcosahedronGeometry(0.42,0), new THREE.MeshStandardMaterial({color:0xff4400, emissive:0xff3300, emissiveIntensity:2.5, flatShading:true}));
ember.position.y=0.22; ember.scale.set(1,0.5,1); scene.add(ember);
// glow sprite (canvas)
function glowTexture(){
const c=document.createElement('canvas'); c.width=c.height=128;
const ctx=c.getContext('2d');
const g=ctx.createRadialGradient(64,64,0,64,64,64);
g.addColorStop(0,'rgba(255,180,80,0.9)'); g.addColorStop(0.4,'rgba(255,120,30,0.35)'); g.addColorStop(1,'rgba(255,80,0,0)');
ctx.fillStyle=g; ctx.fillRect(0,0,128,128);
return new THREE.CanvasTexture(c);
}
const glow=new THREE.Sprite(new THREE.SpriteMaterial({map:glowTexture(), transparent:true, depthWrite:false, blending:THREE.AdditiveBlending}));
glow.scale.set(3.2,3.2,1); glow.position.y=1.1; scene.add(glow);
// ---------- Sparks ----------
const SPARKS=140;
const sparkGeo=new THREE.BufferGeometry();
const sparkPos=new Float32Array(SPARKS*3);
const sparkVel=[];
const sparkLife=new Float32Array(SPARKS);
for(let i=0;i<SPARKS;i++){
sparkPos[i*3]= (Math.random()-0.5)*0.6;
sparkPos[i*3+1]= Math.random()*2.2;
sparkPos[i*3+2]= (Math.random()-0.5)*0.6;
sparkVel.push({x:(Math.random()-0.5)*0.45, y:0.9+Math.random()*1.6, z:(Math.random()-0.5)*0.45});
sparkLife[i]=Math.random();
}
sparkGeo.setAttribute('position', new THREE.BufferAttribute(sparkPos,3));
const sparkMat=new THREE.PointsMaterial({color:0xffcc66, size:0.09, transparent:true, opacity:0.95, blending:THREE.AdditiveBlending, depthWrite:false, sizeAttenuation:true});
const sparks=new THREE.Points(sparkGeo, sparkMat); scene.add(sparks);
// ---------- Stars & Moon ----------
function makeStars(){
const N=900; const g=new THREE.BufferGeometry(); const p=new Float32Array(N*3);
for(let i=0;i<N;i++){
const theta=Math.random()*Math.PI*2, phi=Math.random()*Math.PI*0.45;
const r=110;
p[i*3]=Math.cos(theta)*Math.cos(phi)*r;
p[i*3+1]=Math.sin(phi)*r+4;
p[i*3+2]=Math.sin(theta)*Math.cos(phi)*r;
}
g.setAttribute('position', new THREE.BufferAttribute(p,3));
const m=new THREE.PointsMaterial({color:0xffffff, size:0.55, sizeAttenuation:false, transparent:true, opacity:0.9, fog:false});
const stars=new THREE.Points(g,m); scene.add(stars); return stars;
}
const stars=makeStars();
// moon
const moon=new THREE.Mesh(new THREE.SphereGeometry(3.2,24,24), new THREE.MeshBasicMaterial({color:0xf5f3ce, fog:false}));
moon.position.set(-28,32,-38); scene.add(moon);
const moonGlow=new THREE.Sprite(new THREE.SpriteMaterial({map:glowTexture(), color:0xbdd7ff, transparent:true, opacity:0.5, depthWrite:false, blending:THREE.AdditiveBlending, fog:false}));
moonGlow.scale.set(16,16,1); moonGlow.position.copy(moon.position); scene.add(moonGlow);
// distant mountain silhouettes
for(let i=0;i<8;i++){
const a=(i/8)*Math.PI*2 + 0.4;
const m=new THREE.Mesh(new THREE.ConeGeometry(14+rand()*10, 10+rand()*8, 5), new THREE.MeshStandardMaterial({color:0x0d1f33, roughness:1, flatShading:true}));
m.position.set(Math.cos(a)*52, 2, Math.sin(a)*52); scene.add(m);
}
// ---------- Interaction extras ----------
let paused=false;
window.addEventListener('keydown', (e)=>{
if(e.code==='Space'){ paused=!paused; e.preventDefault(); }
if(e.key==='r'||e.key==='R'){ camera.position.copy(START_POS); controls.target.copy(START_TARGET); }
});
window.addEventListener('resize', ()=>{
camera.aspect=window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// ---------- Animation ----------
const clock=new THREE.Clock();
function animate(){
requestAnimationFrame(animate);
const t=clock.getElapsedTime();
const dt=Math.min(clock.getDelta(),0.05) || 0.016;
const flick = paused ? 1 : 1 + Math.sin(t*11)*0.08 + Math.sin(t*23+1.3)*0.12 + Math.sin(t*5)*0.06 + (Math.random()-0.5)*0.14;
fireLight.intensity = 38 * flick;
fireLight.position.x = Math.sin(t*7)*0.08;
fireLight.position.z = Math.cos(t*6)*0.08;
fireLight2.intensity = 10 * flick;
glow.material.opacity = 0.75 + Math.sin(t*9)*0.12;
glow.scale.setScalar(3.1 + Math.sin(t*8)*0.25);
ember.material.emissiveIntensity = 2.2 + Math.sin(t*6)*0.6;
if(!paused){
flameOuter.scale.set(1+Math.sin(t*12)*0.18, 1+Math.sin(t*15)*0.22, 1+Math.cos(t*10)*0.18);
flameOuter.rotation.y=t*1.2;
flameMid.scale.set(1+Math.cos(t*14)*0.2, 1+Math.sin(t*18)*0.25, 1+Math.sin(t*13)*0.2);
flameMid.rotation.y=-t*1.7;
flameInner.scale.set(1+Math.sin(t*20)*0.22, 1+Math.cos(t*17)*0.28, 1+Math.cos(t*19)*0.22);
flameGroup.position.y=Math.sin(t*11)*0.04;
flameGroup.rotation.y=Math.sin(t*2)*0.1;
// sparks
const arr=sparkGeo.attributes.position.array;
for(let i=0;i<SPARKS;i++){
sparkLife[i]+=dt*0.55;
if(sparkLife[i]>1){ sparkLife[i]=0; arr[i*3]=(Math.random()-0.5)*0.5; arr[i*3+1]=0.4; arr[i*3+2]=(Math.random()-0.5)*0.5; }
const v=sparkVel[i];
arr[i*3]+= (v.x + Math.sin(t*3+i)*0.12)*dt;
arr[i*3+1]+= v.y*dt*(1 - sparkLife[i]*0.4);
arr[i*3+2]+= (v.z + Math.cos(t*2.5+i)*0.12)*dt;
}
sparkGeo.attributes.position.needsUpdate=true;
sparkMat.opacity=0.9;
}
stars.rotation.y=t*0.005;
controls.update();
renderer.render(scene,camera);
}
animate();
</script>
</body>
</html>Reasoning trace (638 tokens)
Designing a single-file ThreeJS 0.