class PrecipParticle
This class is the core particle used for both rain and snow, sharing one constructor and update loop but branching on this.type - a common pattern for keeping similar objects in one class instead of duplicating code.
🔬 This is the gravity that pulls rain down 4x faster than snow. What happens if you make the snow gravity (0.06) negative, so flakes rise instead of fall?
const gravity = this.type === "rain" ? 0.25 : 0.06;
this.vel.y += gravity;
this.vel.x += globalWind.x * 0.03;
class PrecipParticle {
constructor(x, y, type) {
this.type = type; // 'rain' or 'snow'
this.pos = createVector(x, y);
if (type === "rain") {
const baseVy = random(4, 7);
this.vel = createVector(globalWind.x * 0.4, baseVy);
this.size = random(6, 10);
this.mass = 1.0;
} else {
const baseVy = random(1, 3);
this.vel = createVector(globalWind.x * 0.3, baseVy);
this.size = random(3, 6);
this.mass = 0.7;
this.phase = random(TWO_PI); // side-to-side drift
}
this.dead = false;
}
update() {
if (this.dead) return;
// Gravity + wind influence
const gravity = this.type === "rain" ? 0.25 : 0.06;
this.vel.y += gravity;
this.vel.x += globalWind.x * 0.03;
if (this.type === "snow") {
// soft side drift using sin
this.pos.x += sin(this.phase + this.pos.y * 0.03) * 0.3;
}
this.pos.add(this.vel);
// Ground collision / accumulation
const groundY = getGroundSurfaceY(this.pos.x);
if (this.pos.y + this.size * 0.5 >= groundY) {
this.landAtGround();
}
}
landAtGround() {
const idx = groundIndexForX(this.pos.x);
if (idx >= 0 && idx < GROUND_SEGMENTS) {
if (this.type === "rain") {
waterHeights[idx] += this.mass * 1.2;
} else {
snowHeights[idx] += this.mass * 1.5;
}
}
this.dead = true;
}
isDead() {
return (
this.dead ||
this.pos.y > height + 50 ||
this.pos.x < -50 ||
this.pos.x > width + 50
);
}
draw() {
if (this.type === "rain") {
const len = this.size;
stroke(200, 230, 255, 230);
strokeWeight(2);
line(
this.pos.x,
this.pos.y - len * 0.5,
this.pos.x,
this.pos.y + len * 0.5
);
} else {
noStroke();
fill(245, 250, 255, 235);
ellipse(this.pos.x, this.pos.y, this.size, this.size);
}
}
}
Line-by-line explanation (7 lines)
🔧 Subcomponents:
if (type === "rain") { ... } else { ... }
Gives rain drops faster fall speed and larger size than snowflakes, and gives snow an extra drift phase
if (this.pos.y + this.size * 0.5 >= groundY) { this.landAtGround(); }
Detects when a particle reaches the ground curve and converts it into accumulated height
this.pos = createVector(x, y);- Stores the particle's position as a p5.Vector so x/y can be moved together with .add()
this.vel = createVector(globalWind.x * 0.4, baseVy);- Rain starts already nudged sideways by 40% of the current wind, plus a random downward speed
this.phase = random(TWO_PI); // side-to-side drift- Snowflakes get a random starting angle so their sine-wave drift doesn't all sync up
const gravity = this.type === "rain" ? 0.25 : 0.06;- Rain accelerates downward much faster than the lighter, floatier snow
this.pos.x += sin(this.phase + this.pos.y * 0.03) * 0.3;- Adds a gentle side-to-side wobble to snow based on its height and personal phase offset
const groundY = getGroundSurfaceY(this.pos.x);- Looks up the current top of the water+snow pile beneath this particle's x position
waterHeights[idx] += this.mass * 1.2;- When a raindrop lands, it raises the ground's water height array at that column by an amount based on its mass