Passion Is Downstream
About This Sketch
A visual meditation on how passion emerges from the accumulation of skill and effort, rather than being a prerequisite for beginning. Small acts of commitment fall like raindrops, each landing and expanding into competence, which eventually generates the radiating ripples of genuine passion.
This sketch accompanies the essay "Passion Is Downstream," which argues against the common advice to "follow your passion" and instead suggests that passion develops as a natural consequence of building competence, gaining autonomy, and seeing impact from your work.
Algorithm
This sketch visualizes the core thesis of "Passion Is Downstream"βthat passion emerges from competence rather than preceding it.
The animation uses a water metaphor: small drops (representing initial commitment) fall from the top of the canvas and land at the bottom. Each drop that lands creates a pool that gradually expands (representing growing competence and skill development). Once a pool reaches its full size, it begins generating ripples that radiate outward (representing passion that emerges from mastery).
The visual progression embodies the post's argument: you don't start with the ripples (passion)βyou start with the commitment (drops), which builds competence (expanding pools), which then generates passion (radiating ripples). The causal flow moves from top to bottom, making "passion is downstream" literally visible.
Pseudocode
SETUP:
Initialize empty arrays for drops and pools
Create 400x300 canvas
DROP CLASS:
Start at random x position at top of canvas
Fall downward at random speed
When reaching bottom:
Mark as landed
Create a new pool at landing position
Remove self from active drops
POOL CLASS:
Start with radius of 0
Gradually expand toward maximum radius (competence building)
When reaching full size:
Begin spawning ripples periodically
Each ripple expands outward and fades (passion radiating)
DRAW (every frame):
Fade background slightly for trail effect
Spawn new drops occasionally
Update all drops (move downward, check for landing)
Update all pools (expand, manage ripples)
Display drops, pools, and ripples
Show labels: "commitment β" and "β passion"
Source Code
let sketch = function(p) {
let drops = [];
let pools = [];
let time = 0;
class Drop {
constructor() {
// Random starting position at top
this.x = p.random(50, 350);
this.y = -10;
this.speed = p.random(2, 4);
this.hasLanded = false;
this.landedTime = 0;
}
update() {
if (!this.hasLanded) {
this.y += this.speed;
// Check if reached bottom
if (this.y >= 280) {
this.hasLanded = true;
this.landedTime = time;
// Create a pool where it landed
pools.push(new Pool(this.x, 280));
}
}
}
display(colors) {
if (!this.hasLanded) {
// Small falling drop (before commitment/competence)
p.noStroke();
p.fill(...colors.accent2, 180);
p.ellipse(this.x, this.y, 4, 8);
}
}
}
class Pool {
constructor(x, y) {
this.x = x;
this.y = y;
this.radius = 0;
this.maxRadius = p.random(25, 45);
this.growth = p.random(0.3, 0.6);
this.brightness = 255;
this.ripples = [];
}
update() {
// Pool grows over time (competence building)
if (this.radius < this.maxRadius) {
this.radius += this.growth;
} else {
// Once at max size, create ripples (passion emerging)
if (p.frameCount % 30 === 0) {
this.ripples.push({ r: 0, alpha: 255 });
}
}
// Update ripples
for (let i = this.ripples.length - 1; i >= 0; i--) {
this.ripples[i].r += 1.5;
this.ripples[i].alpha -= 3;
if (this.ripples[i].alpha <= 0) {
this.ripples.splice(i, 1);
}
}
}
display(colors) {
// Draw ripples (passion radiating outward)
p.noFill();
for (let ripple of this.ripples) {
p.stroke(...colors.accent1, ripple.alpha * 0.6);
p.strokeWeight(2);
p.circle(this.x, this.y, ripple.r * 2);
}
// Draw main pool (competence)
p.noStroke();
let poolAlpha = p.map(this.radius, 0, this.maxRadius, 50, 180);
// Gradient effect - brighter at edges
for (let r = this.radius; r > 0; r -= 3) {
let alpha = p.map(r, 0, this.radius, poolAlpha, poolAlpha * 0.3);
p.fill(...colors.accent2, alpha);
p.circle(this.x, this.y, r * 2);
}
// Bright center when fully formed
if (this.radius >= this.maxRadius) {
p.fill(...colors.accent1, 200);
p.circle(this.x, this.y, 8);
}
}
}
p.setup = function() {
p.createCanvas(400, 300);
p.colorMode(p.RGB);
};
p.draw = function() {
const colors = getThemeColors();
// Slow fade background
p.fill(...colors.bg, 40);
p.noStroke();
p.rect(0, 0, p.width, p.height);
time++;
// Spawn new drops occasionally
if (p.frameCount % 40 === 0 && drops.length < 5) {
drops.push(new Drop());
}
// Update and display drops (commitment/effort)
for (let i = drops.length - 1; i >= 0; i--) {
drops[i].update();
drops[i].display(colors);
// Remove landed drops
if (drops[i].hasLanded && time - drops[i].landedTime > 10) {
drops.splice(i, 1);
}
}
// Update and display pools (competence growing into passion)
for (let pool of pools) {
pool.update();
pool.display(colors);
}
// Labels
p.fill(...colors.accent3, 150);
p.textSize(11);
p.textAlign(p.LEFT);
p.text('commitment β', 10, 20);
p.textAlign(p.RIGHT);
p.text('β passion', 390, 285);
// Visual metaphor: Small drops (initial commitment) fall and land,
// creating expanding pools (growing competence) that eventually
// generate ripples (passion radiating outward from mastery).
// Passion is downstream - it emerges from the accumulated work.
};
};