Let's design a solid self-contained implementation. Key elements: - Canvas full-screen, side view. Ground line, volcano cone as a polygon with a crater (V-shaped notch at the summit). Volcano shape: symmetric cone with a crater (V-shaped notch) at the top. - Pressure meter (bar) builds up over time; when full → eruption burst: emits N particles with velocities scaled by "eruption power"; pressure then resets and rebuilds. - Lava particles: gravity, collide with terrain height field, settle/slide downhill (friction along slope). Cooling: color interpolation from yellow → orange → red → dark gray, then fade out and disappear. - Ash/smoke: rises, drifts with wind, expands, fades. - Sliders: power, wind (strength + direction), particle amount. Wind slider is negative to positive? Direction is a separate control? "Strength and direction of the wind" — one slider -100..100 for strength+direction, or two controls; the requirement says three sliders: eruption power, wind strength and direction (maybe two sliders?), particle amount. Hmm, "three sliders ... eruption power, wind strength and direction, particle amount". Ambiguous — make wind a slider -100..100 (negative = left, positive = right) to cover strength+direction, keeping a total of three sliders. Or direction as a separate slider. Simpler: wind slider -100..100. - Particle count: cap with a maximum pool. Need to be able to keep 1000+ active. Use arrays. Terrain: height field function groundY(x) — volcano triangle plus flat ground. Cone defined as: center cx, base at ground level gy, peak at crater. Crater: plateau with a dip. Piecewise: slope rises from the left base to the left rim of the crater, crater dip, right rim descends to the right base, beyond that flat ground. Slope collision handling: particle position vs height at height(x). If below, snap to surface, velocity: decompose into slope tangent, apply friction, cancel normal component. Slope computed from height(x+1)-height(x). On steep slopes, slide downhill. Eventually stop or fade. Cooling: particles have life; color keyed to temperature = remaining heat. When settled, cool faster. Rendering: draw sky gradient, maybe also draw a glow of the crater while pressure is high. Draw volcano filled as a dark polygon. Particles as circles with glow (shadowBlur may be slow with 1000 particles; use radial? Too slow). Performance: drawing filled arcs of circles for 1000 particles with globalCompositeOperation 'lighter' for glow is fine. Avoid shadowBlur. Precompute terrain height array per x pixel (store in array) to speed collision detection. Slope from array. FPS: compute smoothed fps. Pressure: builds up at some rate; triggers eruption burst when full: spawn burstCount = amount * power particles, maybe multiple pulses over ~1 second. Then pressure resets to 0. Also allow small "trickle": when pressure is high, glow at the crater. Settled lava: particles permanently reduce terrain? Could darken the terrain and add glow — too complex. Instead, settled particles slide a bit then become "rock" and fade out after a while. Simpler: settled lava stays for a while (temperature low → darkens), fades out after ~6 seconds, disappears to keep counts manageable. But user wants 1000+ active — cap at max ~6000, spawn heavily. Wind slider affects ash drift, and also slightly affects airborne lava. Particle amount slider: scales the spawn count of eruption and ash. UI: sliders in a fixed panel at the top. Counter top-left: particle count and FPS. Pressure meter on the right side or top. Code sketch: let canvas, ctx; resize handler; terrain heights array H[] length W. buildTerrain(): for x: base ground at H0 = h*0.85. Volcano centered at cx=w*0.5? Put the volcano at 45% of the width. Cone: leftBaseX..peakLeft..crater..peakRight..rightBase. Use: halfW = w*0.32; peakX=w*0.45? Simple: cx=w*0.5, craterHalf=40 (scales), peakY = groundY - coneH, crater depth ~18. Height function: for x in the range cx-halfBase .. cx-craterHalf: interpolate from ground to peak... Actually compute: - g = groundBase (y = canvas.height*0.78) - volcano height vh = canvas.height*0.4 - craterW = w*0.05 - baseW = w*0.30 Slope: |dx| > craterW/2? For |x-cx| in [craterW/2, baseW]: t = (|x-cx|-crater/2)/(baseW - crater/2) to map y, slopeY = peakY + t*(g-peakY)?? That's a slope from peak to base... y goes from peakY to g: y = peakY + t*(g - peakY). Inside craterW: dip to crater depth: dip at center: craterY = peakY + craterDepth*(1-(|x| /(crater/2))^2)? Dip at center: y = peakY + depth*(1 - (d/ (crater/2))^2)? Actually, crater floor is above the peak: y = peakY + craterFloorOffset... Crater = depression: at edges (rim) y=peakY, at center lower, so y increases: y = peakY + craterDepth*(1 - (d/(craterHalf))^2)... At d=craterHalf, y=peakY (rim) ✓, at center d=0, peakY+depth (bottom of crater) ✓. Save H array per x (float). Clamp. Physics loop dt (clamp at 0.033). For each lava particle: If !settled: vy += gravity*dt; vx += wind*dt*0.15; vy += wind*dt*0.05. Update position. Check surface: sy = terrainY(x). If y >= sy: settle: y = sy + small value; compute slope s = (H[x+1]-H[x-1]); velocity decomposition: tangential direction... Simplify: compute slope; vx = slope * speedFactor? Slide model: keep speed along surface: along = vx + vy*slope? Hmm. Simpler: compute slope; gAlong = gravity * sin(theta) where sin theta = slope/sqrt(1+slope²). Direction downhill: -sign(slope). v_along += gAlong*downDir*dt*dt? Units: acceleration a = gravity*slope/(1+slope²)... Apply: dir = slope>0? -1:1 (x increases as y goes down, so downhill is the opposite sign of slope... if y increases going downhill (screen coords), downhill x direction = -sign(slope)). vAlong += -gravity*slope*? Let's just do this: vAlong += -gravity*slope/(1+slope*slope)*dt; friction: vAlong *= 0.98. And vx = vAlong, vy = vAlong*slope. Good enough. If slope is small, vAlong→0, set "resting" state. When resting, friction higher. Temperature: temp starts at 1, decreases: temp -= coolRate*dt (faster when settled? Slower when in the air? Actually hot when launched). life = temp. Color: interpolate from bright yellow (high) to dark rock (low), alpha fades near the end. Remove when temp<=0 or off-screen. On settle, add a small bounce? Convert: if hit at high speed, small chance to splash? Keep simple: stick and slide. Ash particles: spawn from crater at pressure or continuously during eruption. Rise vy negative, vx = wind*..., expand radius, fade alpha. Remove when alpha<=0. Pressure: rate pressure += dt * 0.15 (scaled). If >=1: erupting: eruption burst over 0.8 seconds spawning ~ amount*power * multiple waves in multiple frames. Use an eruption state with remaining time. Crater glow: draw radial gradient at the crater during high pressure or during eruption. Draw order: sky gradient, maybe sun, volcano polygon via H (fill path along H, filled dark brown), lava glow inside crater when pressure is high, ash, particles with lighter compositing, UI meter. Counter: active = lava.length + ash.length. Performance: fine. Let's write it. Details: H array recomputed on resize; particles use W (canvas width) at time of launch. Wind slider: -100..100, wind = value/100 * 60 px/s² for ash, *20 for lava. Power slider: 0.2..2 multiplier on velocity and burst size. Amount slider: 0.3..2 for particle count per eruption; base burst maybe amount*1800? Need 1000+: at power 1, amount 1: burst ~1200? "Must stay smooth with at least 1000 active" — allow amount slider max ~4000 in one eruption. Cap total lava around 8000; remove the oldest when over. Spawn: position at crater center, vx spread, vy upward power*(450..900)*power, vx spread ±350*power. Ash: continuous smoke from crater at rate proportional to pressure (pre-eruption smoke) and heavy during eruption. FPS counter: count frames per second, update text once per second. Write it all out cleanly. Meter bar on the left vertical: shows pressure, plus label. Panel with sliders: HTML inputs with labels showing values. Colors: lava color via temp: t>0.66: yellowish white; interpolate between stops: define function lavaColor(temp): stops: temp 1: rgb(255,255,180) bright; 0.7: rgb(255,160,40); 0.45: rgb(220,70,10); 0.25: rgb(90,25,5) dark red; then fade to rgb(40,15,10) fading. Use lerp between stops. Alpha: 1 until temp<0.15 then fade? Actually when it becomes rock, don't fade — stay as rock and remove when temp<=0: cool to rock color with min temp -0.? Give two properties: heat (1→0 over lifetime) and age. Simpler: when heat reaches 0.12 (rock), then start fading out? Give settled lava a longer life, in-air shorter life. Keep it: heat decreases faster when settled. Remove when heat<=0. Color: heat>0.5 hot glow color; heat<0.3 darkens to rock gray. Alpha = min(1, heat/0.3)? So it fades at the end. Good. Smaller radius when settled? No, glow radius larger when hot. Crater glow: when pressure>0.5: draw a pulsing radial gradient at crater center, intensity based on pressure. Ash color gray, rising vy -60..-140, expand radius, alpha max 0.5. Eruption trigger: eruptionTimer set; during eruption, spawn waves: every ~0.07 seconds spawn a batch. Pressure resets to 0, rebuild rate maybe slower. Pre-eruption: ash rate scales with pressure (rumbling smoke). Write HTML with styling: panel at top, semi-transparent. Edge cases: particles off-screen (x<0 or >w, y>h) removed. Cap arrays. Code: const S={}; init on load; use requestAnimationFrame loop with dt. Let's write the final HTML. Roughly 250 lines. Ensure self-contained. Terrain draw: ctx.beginPath moveTo(0,H[0]) ... lineTo every ~2 px, close along the bottom edge. Dark gradient fill. Maybe add snow/ash tinting. Sliders panel fixed top-left; counter top-right; pressure meter maybe bottom-left horizontal. Fine: vertical meter at bottom-right. Add crater lava pool glow during eruption: bright fill at crater center. Write the code carefully. Wind direction: value/100 * 80 for lava acceleration, *60 with noise for ash. Also add a bit of initial turbulence to the ash. Counter updates FPS: fps = frames count over one second. OK, writing.