### PARTIAL OUTPUT (time limit hit; the model was still generating) ### === thinking so far === Let me plan a self-contained HTML file with a canvas-based volcano simulation. Key components: 1. **Scene rendering**: Sky (dusk-ish gradient? — I need to be careful with gradients; but for a sky in a canvas simulation, a subtle atmospheric gradient is natural and functional, not a decorative UI gradient. Sky gradients are legitimate scene painting. I'll keep it restrained — maybe a dusk sky with stars). Ground with volcano cone and crater, side view. 2. **Pressure meter**: builds over time (rate influenced by "power" slider perhaps, or fixed). When maxed → eruption: lava burst particles launched from crater with varied velocities; pressure resets to 0 and builds again. Pressure meter drawn as a vertical gauge on the side, maybe styled like a seismograph gauge with ticks. 3. **Lava particles**: launched with velocity spread (upward cone). Gravity applies. Collision with terrain: land on slopes and slide downhill. Cooling: color from bright yellow → orange → red → dark gray → fades out. I'll represent color via temperature value per particle; map temperature to color. Also handle particles landing in the crater lake area? Keep simple: terrain is a heightfield (function of x). I'll build terrain as array of heights so particle collision is O(1) lookup. Terrain includes volcano cone with crater depression at top. 4. **Ash cloud**: smoke particles rising from crater, drifting with wind, growing and fading, gray puffs (circles with soft alpha). 5. **Sliders**: eruption power, wind strength+direction (one slider with center = 0), particle amount (spawn multiplier / cap). Three sliders, styled. 6. **HUD**: live particle count and FPS. Use a rAF-driven FPS smoothing. Performance: with ≥1000 particles, use typed arrays / object pools, simple circles via fillRect or small draws. For 1000+ particles drawing circles with arc() each frame is fine-ish but fillRect is faster. I could use additive compositing for lava glow. To keep it fast: use globalAlpha and fillRect for smoke, arcs for lava maybe. Actually a good trick: draw lava particles as 2x2 or 3x3 rects with color from temperature, using `ctx.fillRect`. For glow, draw with 'lighter' composite on an offscreen? Simpler: just draw particles, with a few larger translucent rects for glow on hot ones. Keep it under budget. Terrain: precompute heights array indexed by x (step 1px... maybe 2px steps for speed). Terrain drawn as filled polygon once per frame or cached to an offscreen canvas and redrawn (static terrain + cooling lava residue marks?). I could cache terrain into an offscreen canvas for performance. Also lava "settling" could paint onto the terrain cache (scorch/rock deposits) — nice touch: when a lava particle cools fully at rest, stamp a dark pixel onto the terrain offscreen canvas, making the slopes darken over time. That's a great signature detail: the volcano gradually builds up dark rock. I'll cap stamping to avoid overpainting (it just darkens, fine). Particle system design: - Lava pool: fixed-size arrays or object pool. Use structure-of-arrays with Float32Arrays: x, y, vx, vy, temp, life/state. Simpler: array of objects with pooling. 1000-3000 objects is fine in JS. I'll use object pool with a `used` count and swap-remove. Physics: - gravity g = ~300 px/s² scaled. Launch: vy = -(150..450) * powerFactor, vx = (rand) * spread + small. Particles leave crater at crater position. - Each frame: integrate with dt (clamped). Check y >= terrain height at x → collide. If collide: set y = terrain height, reflect/absorb horizontal velocity, convert vertical to sliding: vx reduced, add downhill acceleration along slope (gravity component along slope tangent: slope = (h[x+1]-h[x-1])/2). Slide until slow or off slope. Also small bounce chance. - Cooling: temp decays over time (faster when moving? slower when resting — realism: exposed surface cools; keep simple: temp -= rate*dt; resting particles cool a bit faster as they thin out). When temp below threshold and resting → either mark dead and stamp rock, or fade out. Fade alpha based on temp at cold end. Ash particles: separate pool. Spawn at crater each frame while "erupting" or always mild (volcano smoking constantly, heavier during eruption). Ash rises with buoyant vy negative, drifts with wind (wind value from slider), expands radius, fades out. Count maybe ~300 max. Drawn as translucent gray circles — I'll draw them to the main canvas with alpha. Eruption trigger: pressure gauge p in [0,1]; fill rate maybe 0.08/s base scaled slightly by power slider? The power slider affects eruption strength. When p>=1: erupt. Eruption duration ~ few seconds of continuous ejection (burst). During eruption, spawn N particles per frame scaled by particle-amount slider and power. Pressure resets to 0 at eruption start... Actually requirement: "when it maxes out, the volcano erupts, then pressure resets and builds again on its own." I'll have pressure drop rapidly during the eruption (venting) then build again. Implementation: during eruption phase (say 2.5–4s), pressure drains from 1 to 0; after eruption ends, rebuilds. Also maybe secondary small burps? Keep scope controlled. Crater glow: during high pressure, crater area glows / smoke increases; maybe rumbling shake (camera shake) — small screen shake on eruption is a nice signature moment. Add slight terrain glow at crater. Camera: fixed view, canvas width = window width (I'll size to viewport). Scene: sky, distant stars, maybe moon, mountains silhouette behind, volcano in foreground center-ish. Ground baseline below volcano slopes. Wind: slider from -1..1 (left strong wind .. right strong). Ash drifts by wind; also lava particles get slight wind influence while airborne? Lava bombs are heavy — small drift factor ok. Particle amount slider: 0.2x to 2x multiplier on spawn rate and/or cap. I'll make it a spawn-rate multiplier plus soft cap. To guarantee smooth at 1000 particles, default multiplier should be able to reach ~1500 during eruption. I'll set eruption spawn so it can build up to ~1200-1800 with default settings at 1x. Actually "at least 1000 active" — I should ensure the eruption generates enough. With multiplier up to 2x, plenty. Performance: swap-remove arrays; drawing: lava = fillRect with temp color; hot ones get a second bigger translucent rect (cheap glow). Smoke = arc circles with alpha (maybe 200-400 of them, ok). Terrain cached offscreen; deposits stamped to terrain cache occasionally. Color mapping for temperature: - temp 1: near white-yellow #fff3a0 / #ffd94a - 0.8: orange #ff9a2a - 0.5: red-orange #ff5a1e → #d93c14 - 0.25: dark red #8c2311 - 0.1: dark rock #4a342a → #3a2f2c Fade out below 0.1 with alpha. I'll implement a function tempColor(t) returning rgb string via precomputed lookup table (array of 64 colors) for speed — good. FPS counter: smoothed EMA of 1/dt. Display "PARTICLES: n | FPS: nn". Also pressure meter text maybe. UI design: I want it authored, not generic. Dark canvas fills screen; overlay panel top-left with title "VULCAN / STRATOVOLCANO OBSERVATORY"? Keep copy in English (user request English). A small control panel bottom-left or side with three sliders: POWER, WIND, OUTPUT. Sliders custom-styled (accent color amber). Pressure gauge drawn on canvas as vertical tube with mercury-like lava fill and tick marks, labeled "PRESSURE". HUD counter top-right in mono font. Fonts: avoid Inter/Roboto/system-ui. Use a monospace stack like "IBM Plex Mono" via Google Fonts CDN? External CDN allowed. I'll include Google Fonts link for a distinctive font — maybe "Space Grotesk" for display + "IBM Plex Mono" for readouts. Or go fully offline-safe: font-family fallback fine. CDN allowed, use it: IBM Plex Mono for HUD, and maybe "Unbounded" or "Bricolage Grotesque" for the title. Let's use "Bricolage Grotesque" isn't necessary; simpler: title in "Space Grotesk" bold, HUD in "IBM Plex Mono". Keep it restrained. Icons: minimal need. Maybe a small wind arrow? I can render a tiny SVG inline for the wind slider label or just text. Keep text labels, no emoji. Color scheme: night-dusk scene: deep indigo-black sky? Careful about "dark background with low contrast gray areas" — the scene is a painting, fine. UI accents: lava amber (#ff9a3c) and ash gray. Panel background: translucent dark with 1px border rgba(255,255,255,0.12). No gradients in UI; the sky may use a subtle vertical gradient (scene atmosphere — justified, it's a sky, and it's part of the art direction, not UI decoration). I'll keep the sky mostly flat dark with subtle gradient band near horizon — that's fine, it's painting a sky, natural not decorative. Terrain shape: - Base ground line at y = H*0.78 maybe. - Volcano cone centered at cx = W*0.5. Cone: height ~ H*0.45 above ground. Slope angle ~ 35–45°. Crater: at top, a depression (notch) width ~ W*0.09, depth small.terrain height function h(x) returns y (top surface). For x far from cone: ground level + small noise hills. Cone: rise linearly with slight concave curvature (steeper near top). Crater notch: inside crater, floor below rim by d. Build heights array with step 1 px for correctness; memory W floats fine. Terrain rendering: fill polygon from heights, dark rock color #2c2626 / brownish-gray. Add rim highlight (lighter edge stroke along surface). Also grass/rock texture noise? Slight noise via a few random darker speckles baked into cache. Also maybe snow? no. Add trees silhouettes on lower ground? Small pine triangles could be charming — a few dark pines on the foreground ground, subtle. Keep a couple. Sky: stars (twinkling subtle), a moon with soft halo (radial gradient — it's a glow of the moon; that's scene lighting, acceptable? A radial gradient halo around the moon is scene painting, fine). Also maybe aurora? no, keep focus. Distant mountains silhouette behind volcano (lighter haze color). Lava in crater: crater floor glowing (pulsing with pressure) — draw ellipse with temp color intensity based on pressure. Nice: crater lake of lava visible at top. Eruption behavior details: - On trigger: state = ERUPTING, duration = 2.0 + 1.5*rand s, shake amplitude. - During: spawnRate = (base 120/s * amountMul) * power-ish. Eject velocities: speed = (140..320) * powerMul upward-ish angles ±35°, some faster. Particles per frame with fractional accumulator. - Also continuous mild fumarole spray at high pressure: tiny sparks when pressure > 0.7? Optional; small venting particles at p>0.85 — adds anticipation. Maybe just smoke increase and crater glow + slight tremor shake scaling with p². Anticipation is the signature: gauge climbing with rumble intensity → eruption. I'll add tremor: camera shake amplitude = p² * 2px + eruption burst 6px decaying. Pressure build rate: 1/12 s full cycle ~12s build + ~4s erupt. Rate maybe (0.05 + power*0.02)? Keep constant 0.07/s → 14s. Fine. Ash spawn: constant 2-6/s idle; during eruption 40-60/s. Ash particles: vy initial -30..-80, buoyancy reduces; wind acceleration; radius grows 2→24; alpha fades; life ~4-7s. Color gray with slight warm tint early (smoke lit by glow). Render with circles, globalAlpha, color from dark gray to light gray based on age? Ash lit from below by lava early: start color rgb(120,100,90) → rgb(70,66,70). Simple: use age-based shade. Wind affects ash horizontal velocity with smoothing (wind gusted). Also add slight horizontal wind on airborne lava (0.15 factor). Particle slide on slope: ``` if (p.y >= h(x)) { p.y = h(x); if vy > 60 → bounce: vy = -vy*0.35 (some splash: spawn 1-2 small debris? skip), vx *= .8 else settle: vy=0; sliding mode: follow slope. } ``` Sliding: compute slope grad g = (h[x+4]-h[x-4])/8 (dy per dx). Downhill accel: gravity along surface = g_gravity * sin(theta) where tan theta = |g|. Simplify: ax = gSlide * sign(downhill) — downhill direction: surface height decreases toward downhill; particle accelerates toward larger y? Let's think in canvas coords: y grows downward. Terrain surface y = h. "Downhill" means toward lower elevation = larger h value. Slope of surface in canvas coords: dh/dx. If dh/dx > 0, surface descends (in screen, going right means lower elevation) — wait: h is the y-coordinate of surface. If h increases with x, surface goes down-screen with x, meaning rightward is downhill. Gravity along slope: accel a_x = G * (dh/dx) / sqrt(1+(dh/dx)^2)... Simplest: ax = G_slope * dhdx where dhdx normalized/clamped, plus friction opposing vx. I'll do: ``` slope = (h(x+3)-h(x-3))/6 vx += slope * slideAccel * dt (slideAccel ~ 220) vx *= friction (e.g. exp(-4*dt) or 1 - 3*dt) vy follows terrain: y = h(x) (stick), but if slope small and speed low, stop. ``` Sticking: while |vx| < 3 and |slope| < 0.05 → resting: cool faster, eventually fade & stamp. Also when resting and temp high, slight "lava flow" creep: hot particles continue creeping downhill slowly even at low speed (vx small persistent) — nice realism: lava flows. Implement: if temp > 0.5, friction lower, so they keep oozing downhill. Also particles that fall off the cone's sides at edges continue to ground and slide on ground slope (ground nearly flat, slope ~0 → they settle there). Lava pooling at base? They just cool out. Maybe tiny spread on flat ground via slope noise. Fine. Edge cases: x outside [0,W) → deactivate. Terrain at crater rim: particles launched up fall back into crater → land in crater, that's fine (they'll pool in crater — cool! crater fills with glowing lava briefly). But they'd never leave... resting particles fade after lifetime; ok. Also stamping deposits in crater makes crater fill visually — acceptable, but could look messy; stamp small dark dots only for cold ones — in crater the lava pool glow covers it. Fine. Also spawn origin: crater floor center (cx, craterY). Give per-particle upward velocities. Deposits stamping: when particle dies while resting on terrain (outside crater), stamp a 3x3 dark blob at terrain cache position with low alpha (0.15). Cap stamp count? alpha low accumulates fine. Also stamps shift terrain? Not needed. Terrain cache: offscreen canvas same size; draw sky+stars+moon+mountains+terrain once into it? Sky stars twinkle — draw stars dynamically over static sky (cheap: 120 stars as rects with alpha). Put static sky base (gradient) into cache; stars drawn each frame on top (they twinkle), then terrain cache (which includes mountains, terrain, trees). Then ash behind? Layering: 1. sky base (cache A: gradient + moon) 2. stars (dynamic) — actually stars should be behind mountains. Put stars in cache A too but twinkle lost... twinkle via re-drawing star brightness as overlay would incorrectly draw over terrain. Compromise: stars static in cache with varied brightness; skip twinkle or twinkle via drawing a few stars in upper sky region only where terrain never reaches (terrain max top y? mountains high...). Simpler: keep stars static with subtle variation baked; no twinkle. Add a slowly drifting thin cloud wisp near moon? skip. Keep stars static — fine. 3. terrain cache (mountains + ground + volcano + trees + deposits layer). 4. lava particles (some behind smoke? draw lava after terrain). 5. crater glow (on top of terrain, under smoke) — part of dynamic. 6. smoke/ash. 7. hot lava should glow through smoke? Draw smoke before lava? Ash rises above crater, lava arcs over it... draw order: terrain → lava → smoke? Smoke over lava looks right (eruption column). I'll draw lava first then smoke on top with alpha; hot lava glow shows through translucency. Actually better: draw smoke, then lava (lava on top so arcs readable), then maybe smoke behind column. Just: terrain → smoke(behind, alpha .5) → lava → smoke(front small portion)? Simplicity: terrain → lava → smoke. Smoke alpha ~0.5 so glow visible. Good enough. Hmm, ash rising from crater should partially occlude the cone — drawing smoke after terrain does that regardless of lava order. I'll do terrain → lava → smoke. Lava arcs will appear in front of smoke — slightly off physically but readable. Alternatively smoke → lava → done. Yes. Camera shake: apply ctx.translate(shakeX, shakeY) for scene layers (not HUD drawn in DOM — HUD is DOM, so fine). HUD DOM elements: - Top-left: title block: "VOLCANO OBSERVATORY" small caps + status line ("PRESSURE BUILDING" / "ERUPTION IN PROGRESS") updated from JS, maybe with a small pulsing dot (CSS animation) — colored amber when erupting. - Top-right: mono readout: PARTICLES 1 248 / FPS 60. - Bottom-left panel: sliders POWER, WIND, OUTPUT with value readouts. Custom range styling: track thin, thumb amber square/round. Panel: translucent dark, border 1px rgba(255,255,255,.14), backdrop-filter blur small. Also maybe bottom-right hint "adjust while it runs". Keep minimal. Pressure gauge: drawn on canvas near left edge as vertical thermometer with tick marks; fill color shifting yellow→red as it rises; label "PRESSURE" vertical. Also numeric percent? A thin outlined tube, ticks every 10%, fill from bottom. Position: x=30, from y=... top area under title? Put at left middle. Fine. Slider details: - POWER: 0.4–1.6, default 1.0 → affects ejection speed multiplier and spawn rate slightly. - WIND: -1..+1 default 0 → ash drift & slight lava drift; small wind gust noise (windActual = slider*0.9 + gust*sin(t)*0.1). - OUTPUT: 0.2–2 default 1 → spawn multiplier for both lava and ash. Particle budget: lava pool cap 4000; smoke cap 600. Spawn during eruption: rate = 260 * powerMul^0.5 * outputMul per second → with 3s eruption ≈ 780... need ≥1000 concurrently. Eruption 4s at 260/s = 1040 spawned, and lifetime of lava ~ 6-10s (cooling), so concurrent during eruption ≈ maybe 900-1300. Let's set rate 300/s default → concurrent ~1200+. With OUTPUT at 2 → 2000+, but cap pool 4000 and skip spawns beyond cap. Also idle: after eruption particles linger sliding, count stays high, good for FPS test. Cooling timeline: temp starts 1.0 (plus rand 0.9-1.2). Cooling rate while airborne: 0.35/s → after ~3s airborne temp ~0. On ground sliding: 0.25/s; resting: 0.45/s. Life ends when temp < 0.05 and alpha fade 1s. Total life ≈ 4-8s. Good. Also "glowing" launch: draw hot particles with additive-ish glow: I'll draw a 6x6 translucent rect behind (same color, alpha 0.25) for temp>0.55. And crater impact sparks: on landing with speed, spawn 1-2 tiny debris particles (reuse pool) — adds liveliness but increases count; keep modest (only if speed>200 and pool room). Actually could push counts up nicely toward 1000+. Include. FPS: rAF with dt clamp 1/30 max step... use dt = min(dt, 0.05). Physics substeps not needed; use semi-stable integration (x += vx*dt). With dt spikes particles may tunnel through terrain (fast vy 500 * 0.05 = 25px, terrain check catches since we check after move: if y>h snap). Use small collision sweep: if y > h(x) after move, snap — ok. FPS smoothing: fps = 0.9*fps + 0.1*(1/dtRaw). Display rounded. Resolution: canvas = devicePixelRatio-aware? For perf with thousands of particles, render at 1x DPR maybe (dpr = min(devicePixelRatio, 1.75)? Simpler: use dpr up to 2 with canvas scaled; fillRect cost scales with pixels count on GPU raster — canvas 2D fill ops are CPU-rasterized... Actually 2D canvas is GPU-accelerated in modern browsers. Fill 2000 small rects fine. I'll use dpr = min(devicePixelRatio||1, 2), but if perf worry, cap at 1.5. Use cap 2 with CSS image-rendering default; terrain cache also at dpr. Particles drawn in scene coords with ctx scaled by dpr — fillRect positions fractional; use int coords. OK. Resize: rebuild terrain on resize (debounced). Keep particles (clamp positions). Wind slider direction display: show value like "→ 0.6" arrow chars? Using text arrows like "◀▶" might count as emoji-ish glyph — I'll just show signed number and a small inline SVG arrow that flips? Simpler: readout "−0.35 / +0.60" with direction word LEFT/RIGHT. Fine. Deposits stamping onto terrain cache: need terrain cache ctx; stamp fillRect with rgba dark. Since cache is scaled by dpr, stamp with transform applied (use setTransform dpr). I'll create tctx with scale(dpr,dpr) and draw everything in scene coords. Crater lava pool glow: dynamic ellipse at crater floor, color mix based on pressure & eruption (pulse). Also inner crater walls glow slightly. Draw simple: ellipse with radial? Avoid overdraw; use two ellipses (outer alpha low). During eruption, bright. Let me define terrain geometry: ``` W, H (scene units = css px) groundY = H*0.72 coneBaseHalf = W*0.30? For narrow screens volcano should fit. coneHalf = min(W*0.34, 420) coneTopY = H*0.24 (apex around here) craterHalfW = coneHalf*0.13 (min 14) craterDepth = 22 ``` Height function: - Outside cone: groundY with gentle undulation: groundY + sin(x*0.003)*6 + sin(x*0.011+2)*4... plus raise far edges? Keep subtle. - Cone flank: elevation from groundY up to craterRimY: progress t = 1 - (|x-cx| - craterHalfW)/(coneHalf-craterHalfW) clamped; surface y = rimY + (groundY-rimY)*pow(1-t, 0.85)?? Let me param: for |d| in [craterHalfW, coneHalf]: s = (|d|-craterHalfW)/(coneHalf-craterHalfW) (0 at rim, 1 at base). y = rimY + (groundY - rimY) * s^0.8 (concave: steeper near top? s^0.8 <1 for s<1 means y closer to rimY → surface higher → slope steep near base? Let's think: at s=0 y=rimY(top). dy/ds = (groundY-rimY)*0.8*s^-0.2 → steep near top (s small) — that gives convex-ish... Actually for volcano, slopes slightly concave (steep near top). s^0.9: near s=0, small Δs → small Δy? derivative infinite at 0 for exponent<1 → steep near rim. Yes s^0.85 gives steep near rim, flattening toward base — classic stratovolcano profile. Good: y = rimY + (groundY-rimY)*s^0.85. Hmm wait that yields steep near rim, gentle near base: y changes fast when s small. Visually cone looks like Etna. Good. - Crater: |d| < craterHalfW: crater floor: rimY + craterDepth * smoothstep? floor y = rimY + craterDepth, with edges rising to rim: use y = rimY + craterDepth * (1 - (|d|/craterHalfW)^2)? At d=0 floor at rimY+craterDepth (deepest, lowest point on screen = lowest elevation). At |d|=craterHalfW, y=rimY. Use ellipse-ish: y = rimY + craterDepth*(1 - (d/hw)^1.7)? ok. - Rim slight outward lip bumps: add small noise on flanks: y += sin(d*0.05)*2 etc. Add ridges: y += Math.sin(d*0.02)*3 on flanks for texture (also makes sliding wobbly, fine). rimY = coneTopY. Also behind-mountain silhouettes: 2 layers of ridges behind via separate shapes drawn in terrain cache before main terrain, hazier colors. Crater center cx = W*0.5. Spawn point: (cx, rimY + craterDepth*0.5). Also line where cone meets ground: base at groundY; ground continues flat. Trees at ground far from cone: small triangles clusters at few positions (deterministic random with seeded function so resize consistent — use fixed positions relative fractions). Now UI panel styling: ``` body { margin:0; background:#0d0b12; overflow:hidden } canvas { display:block; position:fixed; inset:0 } .hud { position:fixed; ... font mono; color: #e8ddd0 } ``` Colors: text warm off-white #ece3d6; accent amber #ff9d3c; danger red-orange #ff5a3c. Panel bg rgba(20,16,20,0.55), backdrop blur 6px, border rgba(232,220,200,0.16), border-radius 10px? Slight radius ok. Title font "Space Grotesk" 700 uppercase letterspaced. HUD mono "IBM Plex Mono". Add small status dot: 8px circle with CSS animation pulse (opacity), color changes with state. That's functional status indicator, fine. Sliders custom: ``` input[type=range]{ -webkit-appearance:none; background:transparent; } track: 4px height, background rgba(255,255,255,.18), border-radius thumb: 14px square? round 14px, background #ff9d3c, border... ``` Wind slider thumb maybe shows arrow? keep same. Value readouts right-aligned mono. Slider labels row: label left, value right; slider below. Also small footer hint text: "Pressure rebuilds automatically after each eruption." Pressure gauge on canvas: vertical tube at left, x=28..44, y from 90 to H*0.55? Place bottom-left could clash with panel. Put gauge along left edge top-middle: y 90–260. Draw: outline stroke rgba white .25, ticks left side, fill rect from bottom with color lerp(#ffb347,#ff4d24) by p, plus a bright top edge line. Label "PRESSURE" rotated? Draw text vertically small mono — canvas font: use '11px IBM Plex Mono'. Label above gauge: "PRESSURE" horizontal above tube. Also maybe % value. Keep simple: tube + ticks + label + numeric. Also show status word under gauge? Status in DOM header. ok. Screen shake: shakeMag = p^2*2.2 + eruptShake (set 9 on eruption start, decays *= exp(-2.2*dt)). offset = (rand-.5)*2*mag. Apply translate for all scene drawing. Gauge/HUD drawn inside canvas? Gauge drawn on canvas — should shake too? Draw gauge without shake (after restore). Fine. Ash behavior detail: - spawn: x = cx + randn*10, y = rimY + craterDepth*0.4 - few. - vy = -(20 + rand*70); buoyancy: vy += (windless) slight -? They should rise then slow: apply vy *= damp? Ash: vy -= buoy*dt? Let's: initial vy negative; each frame vy += 26*dt (gravity-ish) until vy > -4 then rise stops, then drift. Wind: vx += wind*60*dt; also turbulence: vx += sin(t*2+seed)*10*dt, vy wiggle. - radius: r += (r0 + age*8)*dt... r grows from 3 to ~26 over life. alpha = (1-age/life)*0.5 * (fade in first .3s). - Color: shade = age/life: start rgb(138,116,104) (warm lit) → rgb(74,70,76). lerp. draw arc fill. - Cap 600; if full skip. Also small ground-hug ash? no. Lava particle spawn (during eruption): ``` angle: mostly upward: theta = PI/2 + (rand-.5)*1.5 (±43°) plus occasional wider 20% speed = (150 + rand*260) * powerMul * (0.8+rand*0.4) vx = cos(theta)*speed*0.9? define screen coords: vy = -sin? Let's: up = -y. vy = -(speed * (0.75+rand*0.25)) ... simpler: pick v speed s, direction angle a above horizon: vx = cos(a)*s*(rand side), vy = -sin(a)*s. a in [0.35, 1.35] rad mixed both sides: a = 0.35 + rand*1.0; side = rand<.5?... Actually symmetric: a = (Math.random()*0.9+0.25); vx = (Math.random()<.5?-1:1)*(Math.cos(a)*s*(0.8+Math.random()*.4)); vy = -Math.sin(a)*s... cos(a) for a up to 1.15 gives 0.42 min horizontal. Add pure verticals. Fine—varied velocities achieved. ``` powerMul = power slider (0.4-1.6). speeds scale with it. Gravity G = 340 px/s². Airborne: vx += wind*18*dt (slight), vy += G*dt. Check collision. Landing bounce: if vy > 140: vy = -vy*0.32* (rand .5-.9)? plus vx*=0.7, spawn debris if speed high & pool room: 2 debris with vx=±(40+rand*120), vy=-(60+rand*160), temp = parent temp*0.95, slightly smaller? no size var; fine. Else: y=h; state grounded: grounded handling each frame: ``` slope = (h(x+4)-h(x-4))/8 (clamp x) vx += slope*G*0.9*dt (downhill accel) friction: if temp>0.45: vx *= (1-1.2*dt) else vx *= (1-4.5*dt) vertical: y = h(x); vy=0 (but keep vy for re-bounce if launched off cliff — recompute each frame if y>h → airborne again; e.g., moving off crater rim edge into crater). ``` Handle x clamp near edges (x<2 or >W-2) → sample h at clamp. Resting & cold: if temp<0.12 && |vx|<4 && |slope|<0.08: enter dying: fade alpha over remaining: temp continues to decay; alpha = temp/0.12 maybe; when temp<=0.02 → stamp deposit (if not in crater? stamp anyway fine, small dark dot), kill. Also lava colors lookup: build LUT 48 entries from temp 0..1: ``` stops: 0.00: (58,48,46) 0.15: (94,70,62) 0.30: (140,66,50) 0.45: (196,64,38) 0.60: (232,92,32) 0.75: (255,138,42) 0.88: (255,190,70) 1.00: (255,232,130) ``` lerp between stops → LUT array of strings "rgb(r,g,b)". temp clamp. Alpha for drawing: hot: 1; cooling: 1 until temp<0.12 then alpha=temp/0.12*... plus dying fade. Compute a = temp<0.12 ? 0.6+0.4*(temp/0.12)? Simpler alpha = min(1, temp*6 + 0.25)? Let's alpha = temp<0.15 ? (0.15+temp/0.15*0.85) : 1 → fades near death. Hmm want cold dark rock visible then disappear: alpha ~ from 1 at .15 down to 0 at .02: a = clamp((temp-0.02)/0.13,0,1). ok. Particle size: base 2px rect + temp-based size s = 1.5 + temp*2.5 → hot bigger 4px, cold 1.5px. Also glow: if temp>0.55: fillRect alpha .22 size s*3 centered (use same fillStyle? need separate alpha — use ctx.globalAlpha per draw, fillStyle color with darker? Use same color with alpha via globalAlpha then reset). Batching: group draws by setting fillStyle per particle — 2000 fillStyle changes ok-ish. To optimize, could sort by bucket... keep simple; canvas handles it. Also flicker: hot particles color flicker via temp*(0.9+0.1*sin(t*13+seed)) — subtle. Skip for perf? cheap: temp += small noise at draw only. Do drawTemp = temp*(0.92+0.08*Math.sin(t*11+seed*7)). ok. Crater glow rendering: ``` glowP = pressure (0-1) + eruption pulse ellipse at crater floor: width = craterHalfW*2*0.8, height = craterDepth*0.9 color: LUT(temp = 0.5+0.5*p) with alpha 0.35+0.5*p plus tiny flicker sin. ``` Also during eruption, brighter + slight vertical squirt glow. Fine. Sky: gradient from #05040a top to #241a2c? Dusk with warm horizon? Volcano at dusk: top deep #070514, mid #1c1430, horizon #3a2436 warmish. Add subtle. Moon: pale #f0e6d0 circle at (W*0.78, H*0.16) r 34 with halo radial gradient alpha .18→0 radius 110. Stars: 140 random dots alpha .2–.9 sizes 1–2, only above terrain area. Mountain silhouettes behind: color #171225 & #221a33-ish; drawn as polylines with noise ridges. And a haze band near horizon: rgba warm .08 rect. ok. Ground foreground: main terrain color: rock #3b2f30? Volcano dark basalt: fill #332a2c; flank shading: darker on left side? Add subtle lighting: after filling, draw along-surface stroke highlight rgba(232,200,160,0.08) 2px on right-lit side... Simple: stroke path along surface with rgba(255,190,120,0.10), 2px. Also crater rim highlight dots. Trees: dark pine silhouettes color #0d1220 at few x positions on ground (skip near cone base). Draw triangles stacked. Deposits stamp: rgba(20,17,18,0.12) rects 4x4 at (x-2, y-2) — accumulates darkening. Cap: fine. Also stamp small bright? no. Now overall counts: lava pool 4096 objects preallocated {x,y,vx,vy,temp,seed,dead?}. Use array + count with swap-remove on death. Alive flag: use property 't' etc. Implementation: ``` const MAXL = 4096; const L = []; for(i=1: trigger eruption: eruptDur = 2.6+rand*1.8; eruptT=eruptDur; eruptShake=9; building=false; erupting=true. During eruption spawn: lavaRate = 320*output*sqrt(power) per second → accumulate: lavaAcc += rate*dt; while(lavaAcc>=1){spawn; lavaAcc--}. Also reduce spawn over eruption tail: rate *= smooth envelope (sin(pi*progress))? envelope = sin(PI * (1 - eruptT/eruptDur))? gives 0 at start... want strong at start, taper: env = 1 - progress^2? or env = (progress<0.7?1: (1-progress)/0.3). Use env = 1 - progress*0.75 maybe. Keep: rate scale = (1 - progress)*(1 - progress*0.5)... simple: env = Math.max(0.15, 1 - progress). Slight taper. Ash rate: idle 3*output; erupting: 55*output*(1-progress*0.6). ``` Total spawned default: 320/s * ~3.7s avg ≈ 1180; lifetimes 5-9s → concurrent peak ~1200-1500 including debris. Venting pre-eruption: when p>0.8, occasional tiny jet sparks: rate 12*(p-0.8)*... small sparks with low speed. Adds tension. Also tremor shake p^2*2.5. Wind gust: windCur = windSlider + sin(t*0.7)*0.12*|windSlider|? Add mild always: gust = (Math.sin(t*0.5)+Math.sin(t*1.3)*0.5) * 0.08 * (windSlider===0?0.5:1) → slight ambient sway even at 0 (ash still drifts a bit). ok. HUD update: every frame set textContent if changed (cache strings to avoid layout thrash — update at 4Hz or when value changes by threshold). I'll update DOM ~ every 150ms. FPS: track frames. Also "SIMULATION SMOOTH ≥1000 PARTICLES" — ensure OUTPUT slider at 1 yields >1000. Set eruption lava rate 340/s, duration min 2.6 → ~880 minimum plus debris and lingering; concurrency: particles die over 5-9s so during a 3s eruption concurrency builds: spawned 1020 while deaths from earlier cycles low... peak concurrency roughly spawnedRate*avgLife = 340*6.5 ≈ 2200? No: steady-state concurrency = rate × mean lifetime, but only during eruption plus tail. Given eruptions every ~15s and life ~6s, most particles from one eruption are alive simultaneously → concurrency ≈ totalSpawnedPerEruption ≈ 340*3.7 ≈ 1250 (minus early deaths). Yes ≥1000. With OUTPUT 2 → ~2500. Cap 4096 protects. Perf estimate: 2500 particles × (1-2 fillRect) + 400 smoke arcs — fine. Also collision function h(x): heights array step 1.0 px in scene coords; index = x|0 clamp. Heights Float32Array(W+2). Build on init/resize. Rebuild terrain cache too. Noise for terrain: deterministic — use simple hash function rand(seed). I'll write function n(i){ x=Math.sin(i*127.1)*43758.5; return frac }. Ground undulation: h = groundY + (n(i*0.5)... produce gentle: y = groundY + Math.sin(x*0.004+1.7)*7 + Math.sin(x*0.013+4)*3.5 + ridge for far edges? Keep. Cone flank noise: y += Math.sin(d*0.045)*2.2 + Math.sin(d*0.11+3)*1.2 (small bumps). Crater interior: floor y = rimY + craterDepth*(1-Math.pow(d/hw,1.6)) hmm at d=hw → rimY+0 ✓; at d=0 → rimY+craterDepth ✓. Also inner wall slightly rugged: + sin(d*0.3)*1.5. Between crater rim and flank start: at |d| exactly hw to hw+2, blend rim. Edge handling: left/right edges of screen ground continues. Cache drawing terrain: ``` tc.beginPath(); tc.moveTo(0,H); ... lineTo per x step 2 (visual fine), then to (W,H), close; fill color. Then surface highlight stroke: separate path along surface (step 2) stroke. Speckles: 300 random dots near surface below: darker. Rock strata? skip. ``` Draw mountains before ground fill (they're behind). Mountains drawn in same cache before foreground terrain: ridge from (0, y0) jagged via noise to (W, y0), down to H, fill haze color; two layers. Volcano glow at crater inside cache? no, dynamic. Trees: at fixed offsets: positions = fractions [0.08,0.13,0.86,0.91,0.18,0.79] choose 6-9 pines, base y = h(x). Draw simple: dark triangle(s) + trunk? Pine: 3 stacked triangles, height 14-22. Color #10131c-ish with slight variation. Also small observation hut? no. Now write code carefully. Also handle visibility: rAF only; dt clamp. DOM structure: ```

MONTE FUOCO

PRESSURE BUILDING

0... layout: two rows "PARTICLES 0" / "FPS 60"
``` Gauge drawn on canvas left side; panel bottom-left might collide with gauge if gauge at left... Place gauge at right-top? DOM stats top-right. Gauge: left side vertical centered (y from 120 to 340) — panel bottom-left width ~260 starting y from bottom. On small screens overlap? Set gauge top-left below header (header height ~70). Gauge x=44, y 96..288. Panel bottom-left: from bottom 20 + height ~ 170 → top edge ~ H-190. If H small (500), gauge bottom 288 vs panel top 310 — ok. On tiny heights may collide; acceptable, also panel could go bottom-right? Wind slider... Keep bottom-left; move gauge to left; fine. Actually place panel bottom-right and stats top-right, gauge left — cleaner: brand top-left, stats top-right, gauge left-middle, controls bottom-right. Yes. Slider ranges: - POWER: min .4 max 1.6 step .01 value 1 → label POWER, readout "1.00×" - WIND: min -1 max 1 step .01 value 0 → readout "0.00 CALM" / LEFT/RIGHT - OUTPUT: min .2 max 2 step .01 value 1 → readout "1.00×" Also POWER affects pressure build rate slightly? "eruption power" only ejecta. Keep. Status text states: "PRESSURE BUILDING · 43%"? Status line shows phase: building with % ; erupting "ERUPTING". Also maybe "VENTING" when p>0.85: "SEISMIC ACTIVITY RISING". I'll show: building: "PRESSURE BUILDING — 47%"; p>0.85: "PRESSURE CRITICAL — 96%"; erupting: "ERUPTION IN PROGRESS". Dot color: building amber pulse slow; critical red fast; erupting red solid pulse. CSS classes on header. Write JS ~450 lines. Let me now write it. Terrain build code: ```js function buildTerrain(){ W = innerWidth; H = innerHeight; groundY = H*0.74; cx = W*0.5; coneH = Math.min(H*0.42, H*0.42); rimY = groundY - coneH; // top coneHalf = Math.min(W*0.32, 400); craterHW = Math.max(12, coneHalf*0.11); craterD = 26; hs = new Float32Array(W+2); for(x=0..W): d=Math.abs(x-cx); let y; if(d < craterHW){ y = rimY + craterD*(1-Math.pow(d/craterHW,1.6)) + Math.sin(d*0.4)*1.4; } else if(d < coneHalf){ s = (d-craterHW)/(coneHalf-craterHW); y = rimY + (groundY-rimY)*Math.pow(s,0.85); y += Math.sin(d*0.05)*2.4 + Math.sin(d*0.017+3)*3; } else { y = groundY + Math.sin(x*0.004+1.7)*7 + Math.sin(x*0.012+4.2)*3.5 + Math.sin(x*0.031)*1.6; // slight rise far from volcano: // y -= Math.max(0,(Math.abs(x-cx)-coneHalf))/W*30? skip } hs[x]=y; ``` Note crater rim transition: at d=craterHW both give rimY ✓ (flank s=0 → rimY plus noise ±2.4.. ok). Crater interior near rim edges: pow curve steep near edges? 1-(d/hw)^1.6: at d slightly < hw, (d/hw)^1.6 ≈ 1-1.6ε → y ≈ rimY+craterD*1.6ε → derivative infinite-ish steep near rim walls, flat-ish middle reversed? Actually 1-(u)^1.6 has derivative -1.6u^0.6 → at u→0 slope 0 → floor flat middle, steep walls. Good cone-shaped crater. Spawn point y: craterFloorY = rimY + craterD*0.55. Eject velocities must clear rim: rim height above floor ~ craterD*0.55 ≈ 14px plus need to exit upward: fine with speeds ≥150. Heights include screen W+1 index; clamp sample. Terrain cache: create off = document.createElement('canvas') sized W*dpr,H*dpr; tctx scale. Draw sky into separate static cache? Combine: single static cache SCENE including sky gradient, moon+halo, stars, mountains, terrain, trees, surface highlight. Stars static — accept. Redraw cache each frame via drawImage (1 call). Dynamic on top. Deposits stamped directly into cache via its ctx (need tctx reference with dpr transform; stamping uses fillRect at scene coords since transform set — set once via tctx.setTransform(dpr,0,0,dpr,0,0)). Cache creation on resize; also on first run. Order per frame: ``` ctx.save(); translate(shx,shy) (int); ctx.drawImage(sceneCache,0,0,W,H); // since cache is dpr-sized, drawImage with dWidth=W? drawImage(canvas,0,0,W,H) scales the dpr-sized bitmap down? Source canvas is W*dpr pixels; specifying dw=W, dh=H draws it scaled to W,H in current transform (dpr) → final pixels W*dpr — correct 1:1 mapping. ✓ (with ctx transform dpr). craterGlow lava particles draw smoke draw ctx.restore(); gauge (no shake) ``` Crater glow: draw ellipse: ctx.ellipse(cx, floorY+3, craterHW*0.85, 7, 0,0,2PI) fill with hot color alpha varying; plus narrower brighter inner. Also glow up the plume? skip. Smoke behind crater glow? Smoke drawn after → covers glow slightly, fine. Lava drawing loop: ``` for(i0.55){ ctx.globalAlpha=a*0.25; ctx.fillStyle=col; fillRect(x-s*1.6, y-s*1.6, s*3.2, s*3.2) glow } ctx.globalAlpha=a; fillRect(x-s*0.5, y-s*0.5, s, s) ``` Setting fillStyle each iteration ~2500 times; ok. Micro-optimization: precompute LUT as strings. Update loop lava: ``` G=340; windCur computed once per frame. for i in 0..nL: p=L[i] x=p.x+ (p.vx+ (p.grounded?0:windDrift))*dt ... Grounded tracking: use flag p.g? store p.grounded bool. if(!p.g){ p.vy += G*dt; p.vx += windCur*16*dt; p.x += p.vx*dt; p.y += p.vy*dt; if(p.y >= hAt(p.x)){ const impact = p.vy; p.y = hAt(p.x); if(impact > 170 && Math.random()<0.7){ p.vy = -impact*0.30*(0.5+Math.random()*0.5); p.vx*=0.75; // bounce if(impact>230 && nL0.45 ? 1-1.3*dt : 1-4.5*dt; p.vx*=fr<0?0:fr; p.x += p.vx*dt; const hh = hAt(p.x); p.y = hh; // stick // rolling off: no vy needed since y follows terrain; if terrain drops (cliff), y jump handled by following h (teleport down) — visual pop but ok. Also crater rim: sliding off rim into crater: y snaps down each frame — looks like falling. Acceptable. // stopping: if(Math.abs(p.vx)<3 && Math.abs(sl)<0.06 && p.t<0.4) p.vx=0; } cooling: rate = p.g ? (moving? 0.22 : 0.5) : 0.32 — determine moving by |vx|>6. p.t -= rate*dt*(0.8+p.seed*0.4)? vary per particle: store p.cd cooling divisor. Simplify: p.t -= rate*dt; per-particle variance via seed used in flicker only. Fine. if(p.t<=0.02){ if(p.g) stamp(p.x,p.y); kill } if(p.x<0||p.x>W||p.y>H+40) kill ``` Grounded particles on flat ground with slope ~ 0.02: accel small; friction kills vx. Resting cold fades & stamps. slopeAt(x): (hs[i+4]-hs[i-4])/8 clamp indices. Note hs in screen y; downhill = larger hs. If sl>0 (surface descends rightward), accel positive → moves right/downhill ✓. Bounce debris spawn: keep temp = p.t*0.9, vx random ±(30..140), vy = -(40..180), grounded=0, position at impact. Also airborne particles leaving screen sides → kill (also terrain exists full width; particles on flanks stay inside). Ash update: ``` s.age += dt; if(age>life) kill s.vx += (windCur*46 + Math.sin(time*1.9+s.seed*6)*14)*dt s.vy += 30*dt (buoy loss) then if s.vy > -2 clamp to -2? Let ash slowly decelerate: also small upward lift early: while age<0.6: s.vy -= 40*dt? Simplify: s.vy += (26 - s.age*8)*dt clamp min -60 max -2... Let me: s.vy += 24*dt; if(s.vy > 6) s.vy = 6; initial vy -25..-80 decays; can end drifting down slightly (falling ash) with wind drift. ok also turbulence vy wiggle. s.x += s.vx*dt; s.y += s.vy*dt; s.r += (4+s.age*7)*dt? r grows: r = s.r0 + age*9 capped 26. store r0. alpha = fade: a = s.age<0.4? s.age/0.4 : 1-((s.age-... simple a = (1-s.age/s.life); also clamp color: c = lerp warm→gray by u=age/life: r=138+ (74-138)*u etc. draw: globalAlpha a*0.45; arc. ``` Also ash shouldn't go below terrain visibly? They drift; if y > h(x)+r, let them settle: y=h, vy=0, keep drifting with wind sliding? Ash falling onto slopes then blowing — extra complexity; just let them fade before landing typically (life 3.5-6s, rise slows...). If below terrain, clamp y = h(x) and vx = wind*30, vy=0. Simple clamp fine, they fade out resting. ok. Kill ash if x out of range. Spawning ash during eruption from crater + also along plume? Spawn at (cx±10, floorY-4) with strong vy for first 1.5s of eruption (column), then crater mouth. fine: vy0 = -(60+rand*90) during eruption, -(20+rand*40) idle. Also hot ember glow inside smoke early? skip. Pressure gauge drawing: ``` const gx=46, gt=H*0.30? define: gy0=118, gy1=118+180=298? Use gaugeTop=112, gaugeH=190. tube w=22 (x 40..62) ticks each 20%: lines left side len 6, labels? tiny numbers every 40%? draw "100" at top? Keep: 11 ticks. fill: hgt = gaugeH*p; color: lerp between (255,190,80) and (255,60,40) by p: col = `rgb(255, ${190-130*p|0}, ${80-40*p|0})` plus flicker when critical. fillRect(gx+3, gy0+gaugeH-hgt, 16, hgt) outline stroke rgba(232,220,200,0.35) rect(gx,gy0,22,gaugeH) rounded? plain rect with r=4 via roundRect maybe (support ok modern; fallback fine—use roundRect, guarded? I'll just use rect + slight radius via roundRect if exists else rect). Simplify: strokeRect. label "PRESSURE" above; percentage text under: `${Math.round(p*100)}%` in mono 11px fillText. Also danger zone marker line at 85%: small red dash + "VENT" text? Add small triangle marker. Keep: dashed red line at 85%. ``` Draw text font: '10px "IBM Plex Mono", monospace' etc. FPS/counts DOM update: every 0.15s set. Status: also show wind as label near? The wind slider readout shows direction. Eruption trigger also ejects initial burst of big rocks? Keep uniform. Venting sparks at p>0.82: rate v = (p-0.82)*90 per second, spawn small: vy=-(120+rand*160)*0.6, vx ±(20..70), these are lava pool particles too (they'll cool fast). Adds pre-eruption drama. Also these are counted as particles → pre-eruption count maybe 30-80. fine. Time: time += dt. Shake: ``` tremor = p*p*2.4; if erupting eruptShake decays: eruptShake *= Math.exp(-1.8*dt) with initial 8. mag = tremor + eruptShake (eruptShake also re-pulsed on eruption start). shx = (Math.random()*2-1)*mag etc (round to 0.5? keep float). ``` Resize handler: debounce 150ms; rebuild everything; clamp particle positions to new W? Just let them be; hAt clamps index → weird but transient. Also clear deposits (cache rebuild wipes — fine). Canvas sizing: c.width = W*dpr etc; ctx.setTransform(dpr,...). Edge: DPR cap: const dpr = Math.min(2, window.devicePixelRatio||1). For perf maybe cap 1.75? use min(2,..) fine; if user has 2 dpr and 2000 particles could strain; I'll cap at 1.5 for safety? Hmm crispness vs perf. Use min(2, dpr) but if particles high... static. Choose dpr cap 2; typical desktop dpr 1. OK. Google Fonts: link Space Grotesk (500,700) + IBM Plex Mono (400,600). Title text "MONTE FUOCO — LIVE ERUPTION MODEL"? Title: "VULCANO #7 — STRATOVOLCANO" hmm. Brand: "MONTE FUOCO" big, subtitle "stratovolcano · live simulation". Status line separate. Panel content: ```
1.00×
...

Pressure rebuilds automatically after every eruption. Move the sliders anytime — even mid-eruption.

``` Slider styling for webkit + moz. Dot pulse CSS: ``` .dot{width:9px;height:9px;border-radius:50%;background:#ff9d3c; animation: pulse 1.6s infinite} .crit .dot{background:#ff5235; animation-duration:.5s} .erupt .dot{background:#ffcf5c...} ``` Set class on body or header. Also I need status readout of particle count: DOM ids pcount, fpsv. Let me also add keyboard? no. Small extra signature: deposits accumulate — slopes visibly darken after each eruption; mention in note: "Debris settles and darkens the slopes — old eruptions leave their mark." Nice narrative. Also maybe lava pooling in crater re-glow: crater glow already dynamic. Write final code now. Careful with details. Slope-based accel for grounded uses G along slope approx: a = G * sl / sqrt(1+sl²)? Use G*sl*0.85 as planned — for sl smallish fine. On steep flank sl ≈ dy/dx: flank steep near rim: dy/dx = (groundY-rimY)*0.85*s^-0.15/(coneHalf...) compute: coneH ≈ 0.42H (~300 for H=720... H maybe 800 → groundY 592, rimY 328, Δ=264). s^0.85 near s=0.05: derivative .85*s^-0.15*Δ/(coneHalf-craterHW): s=0.05 → s^-0.15≈1.37 → slope ≈ .85*1.37*264/360 ≈ 0.85 → ~40°. Good. Base slope at s=1: .85*264/360 ≈ 0.62 → 32°. Hmm that means slope doesn't flatten at base much (concave mild). Fine, stratovolcano slopes ~30-40°. Downhill accel on 33° slope: G*sinθ ≈ 340*0.55*0.85 ≈ 160 px/s² → particles ooze downhill reaching friction equilibrium: friction 1-1.3*dt → terminal v ≈ 160/1.3 ≈ 120 px/s for hot — they visibly flow down slope. Hot cooling 0.22/s → stays >0.45 for ~2.5s → flows ~ while sliding 200px maybe. Then friction 4.5 stops them. Reasonable. Also grounded hot particles on flat ground: sl~0 → decelerate quickly. Particles resting in crater: sl small inside crater floor (flat-ish center, steep walls). They settle at floor. Good. Landing bounce threshold vy>170; typical landing vy from launch vy0=-250..-450 + travel: reimpact vy ~ 300-450 → bounce once with 0.3 → 100 → second contact grounded. Good, adds arcs. Debris spawn: on high impact: 2 debris, vy=-(50+rand*140), vx=(rand-0.5)*220, t=p.t*(0.85+rand*0.15). Now stamping: stamp(x,y): stc.fillStyle='rgba(16,13,14,0.10)'; fillRect(x-2,y-2,4,4). Plus occasionally slightly larger. ok. Crater glow draws on top of terrain each frame: ``` const gl = 0.25 + p*0.75 (plus erupting boost) + flicker 0.05*sin(t*9) color from LUT at temp 0.55+0.4*p ctx.globalAlpha = clamp(gl,0,1) ellipse(cx, craterFloorY, craterHW*0.9, 6+p*3) inner: alpha*0.8 ellipse(cx, craterFloorY, craterHW*0.55, 4+p*2) brighter (temp .8) Also spatter glow on rim walls during eruption? skip. ``` Wait: the ellipse drawn over crater interior region — crater interior terrain color dark; glow ellipse should look like molten pool. Yes. Also add glow "spill" over rim during eruption start? skip. Check crater floor position: craterFloorY = rimY + craterD*0.55. Terrain at center: rimY+craterD → floor deeper than pool ellipse center; pool ellipse center at floorY+? Draw pool at y = rimY+craterD-6 (near floor surface). Since crater depth 26, ellipse ry ~8 — inside crater walls ✓. Set poolY = rimY + craterD - 7. Spawn lava from poolY-4. Now smoke spawn y: poolY - 10. Also during eruption, also eject smoke higher column with bigger r0. Let me write terrain cache details: ``` function buildScene(){ sc = document.createElement('canvas'); sc.width=W*dpr; sc.height=H*dpr; tsc = sc.getContext('2d'); tsc.setTransform(dpr,0,0,dpr,0,0); // sky const g = tsc.createLinearGradient(0,0,0,H); g.addColorStop(0,'#060512'); g.addColorStop(0.45,'#160f26'); g.addColorStop(0.8,'#2a1b2c'); g.addColorStop(1,'#3c2430'); fillRect. // stars for(i<160): x=n1(i)*W, y=n1(i+7)*H*0.6 (above terrain top?), sizes, alpha by n. ensure stars behind mountains fine. // moon mx=W*0.76, my=H*0.17, r=30: halo radial gradient rgba(232,220,190) .16→0 radius 100; body #f2e7d2 with slight crater dots? two darker circles alpha .08. Also crescent shading: overlay dark circle offset for phase? keep simple: shading arc: darker circle offset x+10 alpha .18 color sky-ish. // horizon haze band: rect near groundY-40..groundY rgba(140,80,90,0.05)? skip or subtle. // mountains back layers: layer(color '#150f23', baseY=groundY-90, amp 60): polyline via noise: for x step 8: y = baseY - (noise ridge) e.g. Math.sin(x*0.004+2)*28 + Math.sin(x*0.011)*18 + n... produce ridge: y = baseY - (0.5+0.5*Math.sin(x*0.003+1))*70 - Math.sin(x*0.017)*14. down to H fill. layer2 '#1d1530' baseY=groundY-40 gentler. Wait volcano foreground will cover center; mountains behind visible on sides. good. // terrain path surface step 2 → fill '#312829'? Let me pick volcano rock: '#33272a'. Fill from surface down. surface stroke: rgba(210,160,120,0.10) lw2 — subtle rim light. maybe two strokes: one warm on top. speckles: 400 dots: random x, y = h(x)+2+n*30, color rgba(0,0,0,0.18) rects 2x2. plus some lighter rgba(190,140,110,0.05). // crater interior shading: darker fill inside crater walls: draw path along crater region fill rgba(0,0,0,0.35)? Compute polygon of crater interior (x from cx-hw..cx+hw surface down 30px) fill dark. ok: iterate. // trees: positions fixed fractions: .07,.12,.17,.84,.88,.93 plus .22? avoid near cone (coneHalf up to 400 → cone base spans cx±400; on 1000px wide screen edges only). For narrow screens coneHalf=0.32W*? At W=600: coneHalf=min(192,400)=192 → spans 300±192=108..492 — trees at fractions .07/.12 (x 42-72) outside 108 ✓, .17→102 borderline. Use fractions [.06,.11,.16,.84,.89,.94] safe for W≥500? .16*600=96 <108 ok. For W=400: coneHalf=128 → spans 72..328 → trees at .06*400=24 ok, .11=44 ok, .16=64 ok; .84=336 ok. ✓ Draw pine: at (tx, ty=h(x)): height hgt=16+n*10; draw 3 triangles narrowing upward, trunk? just triangles: color '#0e1220' with alpha .9. plus small snow? no. Also grass tufts? skip. } ``` Also maybe distant volcano glow reflection? no. One more: subtle reflection of eruption glow on ground/atmosphere dynamic: during eruption, draw translucent warm overlay near cone: e.g., radial gradient centered at crater with alpha tied to pressure? That's a dynamic lighting touch — "light spill": draw a radial gradient (rgba(255,140,50) a=0.10*p) radius 200 at crater each frame before particles. This is dynamic scene lighting, justified. Also light the smoke lower part warm — approximate: smoke color already lerps toward warm when young. ok. Foreground vs background cone: single volcano. ok. Let me also ensure requirement "land on the slopes, and slide or settle downhill" ✓; "glowing lava particles varied velocities" ✓; "ash cloud rises & drifts with wind" ✓; sliders ✓ live; counter ✓. Now write the full file. Also include meta viewport, title. Code time. I'll aim for clean structure with comments in English (user language English). Let me write: ```html Monte Fuoco — Live Eruption Model