Create a single HTML file that includes a canvas-based volcano simulation. Required features: pressure meter, eruption cycle, lava particles with gravity, sliding on the terrain slope, color change via cooling, ash cloud, 3 sliders (power, wind, particle amount), counters for particle count and FPS, capable of handling 1000+ particles.
Terrain: heightmap array across the full canvas width. Volcano cone shape with a crater. Define a function groundHeight(x). Volcano: roughly triangular mountain, with a crater dip near the center.
Terrain heightmap: h[x] for x 0..W. Cone: apex at cx, crater is flat between cx-craterW..cx+craterW. Slopes descend down to the ground level.
Lava particles: launched from the crater, with position and velocity, with gravity. Landing detection: y >= terrain(x). Then slide: while there's a slope, move along the terrain: vy=0, accelerate in the slope direction via the gradient. Cooling: heat value decreases, color goes yellow→orange→red→dark. Disappears when heat drops below the threshold and it comes to rest, or fade-out after landing.
Maintain performance: use simple arrays, don't use shadows per particle (shadows are slow). Glow effect via additive composite 'lighter' and pre-rendered radial gradient sprites. Pre-render 2–3 sprites of different colors? Simpler: pre-render a single white-to-yellow gradient sprite and tint with globalAlpha... canvas tinting isn't easy. Pre-render multiple sprites across the heat gradient (e.g., 16-color sprites). Then drawImage with scaling — fast.
Ash: gray smoke sprites, alpha based on lifetime.
Eruption cycle: pressure 0→100 increases; when full, eruption state for a fixed duration emitting particles at rate * particleSlider; then reset pressure to a low value.
Sliders: power (launch speed), wind (ash drift and slight effect on lava, -1 to 1), particles per eruption.
FPS counter: update smoothed FPS roughly every 0.5 seconds. Particle count: total active count.
Terrain collision and sliding: when a particle lands (y >= groundY(x)), set state to "on ground": compute gradient from heightmap; if |gradient| > small threshold, move x in the downhill direction, set y = ground; otherwise settle (state 3), keep cooling, then fade. Lava slows down while sliding. Use terrain smoothing.
Terrain construction: W=960, H=540. Ground baseline at y = H-60. Volcano: baseWidth, peakY. height[x] = ... function terrainY(x):
- Outside the mountain base: groundY.
- Mountain: linear from base to crater rim, with crater dip in the middle.
h = ground - (mountainHeight * coneFactor). coneFactor = 1 - |x-cx|/halfBase clamped. Crater dip: within craterRadius of cx, lower the terrain to form a bowl: terrainY += craterDepth * (1 - |x-cx|/craterRadius)? Careful: crater interior lower than rim. Launch from crater center just slightly above terrain.
Compute terrainY[x] = groundY - coneH * max(0, 1 - |x-cx|/halfBase) + crater dip (inside craterRadius: dipAmount). Actually the crater dip lowers the summit. Make it smooth.
After that, smoothing pass over heightmap.
Drawing: sky gradient, maybe a sun, ground fill below the terrain polygon, distant stars at night? Keep the sky gradient dark, lava glow.
Particle drawing: composite 'lighter', size roughly heat-dependent.
Cap: array with alive flag; reuse pool to avoid GC. Max particles probably 4000.
Ash drift: vx += wind*...; y decreases.
Code should be compact and readable. Alright, let's write it.
Sprite generation:
function makeGlowSprite(color){ offscreen canvas 32x32, radial gradient from center color opaque → transparent }
Lava palette heat stages (0..1): colors: white-yellow #fff8c0 → #ffd24a → #ff9a2a → #ff5a10 → #c22a08 → #6a1404 → #2a0a04 dark. Build LAVA_STAGES = 12 sprites.
Ash sprites: gray radial.
Update loop with dt.
Pressure: builds at rate; during eruption spawn = dt * spawnRate. Eruption lasts while pressure>0? Simpler: pressure builds 0→100; upon reaching 100 set erupt=true; during eruption rapidly spawns particles and pressure drops to 0; then builds again. Power slider affects speed.
Particle amount slider: spawn rate multiplier, e.g., 20..400/sec... need to keep 1000+ active: with rate 300/s and lifetime ~4-6s, active count ~1500. Good, plus ash too.
Lava lifetime: while airborne heat decreases slowly, while grounded cooling is faster. Remove when heat<=0 with alpha fade.
Sliding: on ground, slope = (terrain[x+2]-terrain[x-2])/4; if |slope|>0.06, vx = slope * slideSpeed (proportional to gradient and heat, cooled lava slows). x += vx*dt; y = terrain(x)-1. Heat decreases. Otherwise settled: vx→0, cooling rate increases, stops when heat<0 → dead.
Glow on canvas: while erupting, draw an inner glow at the crater.
HUD: update FPS and particle count DOM counter as text every 0.25 seconds.
Let's write the file.
Canvas size: fix canvas at 960x540, scale via CSS for responsiveness.
Wind effect on lava: light vx += wind*20*dt; ash: stronger drift.
Edge handling: kill particles if x<0 or x>W and grounded, or if they've gone off-screen.
Writing the code:
const W=960,H=540; terrain array of size W+1.
buildTerrain():
ground=H-50; cx=W*0.5; halfBase=W*0.46; coneH=250; craterR=42; craterDip=34;
for x: d=abs(x-cx); m = coneH*max(0,1-d/halfBase); apply easing maybe. y=ground-m; if d=100 → erupting, eruptionTime=0.
Erupting: spawn; pressure = max(0, 100 - eruptionTime*…)? Pressure display drops; after pressure reaches 0, eruption lasts min 2.5s? Simpler: eruption strength = 100 pressure decays at 40/s → 2.5s eruption, spawn rate proportional to pressure. Then transition to build.
spawnRate = 40 + amount*3 particles/s (amount slider 10..400), scaled by pressure/100 during eruption. Ash rate = amount*0.3.
Lava particle fields: x,y,vx,vy,heat (1→0),cool rate,size,state(0 airborne,1 ground). drawImage(spriteIndex, x-s/2,y-s/2,s,s).
heat stages: sprite index = floor((1-heat)*(N-1))? heat 1 → hottest. spriteIdx = clamp(floor((1-heat)*N)).
Cool rate: airborne 0.05/s? Lifetime about 10s? Need to disappear. Airborne 0.08, on slope 0.12, settled 0.25. On disappearance, alpha fade = min(1, heat*3).
Draw alpha only meaningful for non-lighter; use globalAlpha together with 'lighter' — that's fine, works.
Settling on ground: when particle has low slope and low velocity, just draw it there; disappears with cooling — good, no permanent accumulation. Maybe leave faint solidified marks? Not required.
Crater glow when pressure high: radial gradient overlay with alpha = pressure/100.
Writing HTML.