class Drop
Drop is a self-contained ES6 class representing one raindrop: it knows how to initialize itself (reset), move itself (update), and render itself (draw). Wrapping behavior into a class like this is a core pattern for particle systems in p5.js - instead of managing dozens of loose variables, you manage an array of objects that each know how to take care of themselves.
🔬 The .25 in this.stopped?.25:1 slows a drop down to a quarter speed once it 'sticks'. What happens if you change .25 to 1 so stopped drops keep falling at full speed?
update(){if(!this.stopped&&this.y>this.stop){this.stopped=true;trails.push(new Trail(this.x,this.y,height));}this.y+=this.v*(this.stopped?.25:1);this.x+=sin(frameCount*.1+this.off)*.4;if(this.y>height+20)this.reset();}
🔬 The ellipse width is len*.3 while its height is the full len, giving drops a stretched teardrop look. What happens if you make the width equal to len as well, so drops become round?
draw(){noStroke();let len=this.s*(this.stopped?.7:1.5);fill(180,200,255,180);ellipse(this.x,this.y,len*.3,len);}
class Drop{
constructor(){this.reset();}
reset(){this.x=random(width);this.y=random(-height,0);this.v=random(3,8);this.s=random(6,12);this.off=random(TWO_PI);this.stop=random(height*.3,height*.9);this.stopped=false;}
update(){if(!this.stopped&&this.y>this.stop){this.stopped=true;trails.push(new Trail(this.x,this.y,height));}this.y+=this.v*(this.stopped?.25:1);this.x+=sin(frameCount*.1+this.off)*.4;if(this.y>height+20)this.reset();}
draw(){noStroke();let len=this.s*(this.stopped?.7:1.5);fill(180,200,255,180);ellipse(this.x,this.y,len*.3,len);}
}
Line-by-line explanation (15 lines)
🔧 Subcomponents:
if(!this.stopped&&this.y>this.stop){this.stopped=true;trails.push(new Trail(this.x,this.y,height));}
Marks the drop as stopped once it passes its random stop point, and creates a Trail object at that spot
if(this.y>height+20)this.reset();
Sends the drop back to a fresh random position above the canvas once it falls off the bottom
constructor(){this.reset();}- When a new Drop is created, it immediately calls reset() to set all of its starting properties in one place.
this.x=random(width);- Places the drop at a random horizontal position across the canvas width.
this.y=random(-height,0);- Starts the drop above the visible canvas (a negative y value), so it appears to fall in from off-screen.
this.v=random(3,8);- Gives the drop a random falling speed between 3 and 8 pixels per frame.
this.s=random(6,12);- Sets a random base size for the drop, used later to calculate its drawn length.
this.off=random(TWO_PI);- Picks a random starting phase for the sine wave so drops don't all sway in perfect unison.
this.stop=random(height*.3,height*.9);- Chooses a random vertical point (somewhere between 30% and 90% down the screen) where this drop will 'stick' and slow down.
this.stopped=false;- Resets the stopped flag so a recycled drop starts falling freely again.
if(!this.stopped&&this.y>this.stop){this.stopped=true;trails.push(new Trail(this.x,this.y,height));}- Checks if the drop has just reached its stop point for the first time; if so, it flags itself as stopped and adds a new Trail streak below it.
this.y+=this.v*(this.stopped?.25:1);- Moves the drop downward - at full speed before it stops, but at just 25% speed afterward, simulating it clinging to the glass.
this.x+=sin(frameCount*.1+this.off)*.4;- Nudges the drop left and right using a sine wave based on frameCount, creating a gentle swaying motion as it falls.
if(this.y>height+20)this.reset();- Once the drop is safely below the bottom edge, it resets to a new random position above the screen so the rain never runs out.
let len=this.s*(this.stopped?.7:1.5);- Calculates the drawn length of the drop - falling drops are stretched (1.5x) to look like motion streaks, stopped drops are shorter (0.7x).
fill(180,200,255,180);- Sets a semi-transparent pale blue color for the drop, matching the moonlit night sky theme.
ellipse(this.x,this.y,len*.3,len);- Draws the drop as a narrow, tall ellipse - thin width but long height - so it reads as a raindrop shape rather than a circle.