The Advice Problem
About This Sketch
Particles rise from below. Most fade before reaching the top. The few survivors glow at the summit and broadcast downward rings — their experience radiating out as advice. A visualization of survivorship bias: the advice landscape is shaped entirely by the voices of those who made it, while the silent majority of paths that ended early leave no trace.
Algorithm
Many particles spawn at the bottom of the canvas and rise upward. Each particle is assigned a random fate: about 14% survive all the way to the top; the rest fade out at a random height along their ascent.
Survivors reaching the top become pulsing "advisor" nodes. Each advisor periodically emits downward-expanding elliptical rings, representing their advice broadcasting outward. The rings fade as they descend.
The canvas fills with a field of rising particles — most dimming and vanishing mid-journey — while a handful of glowing survivors accumulate at the top, broadcasting rings into the space below.
This sketch accompanies the blog post "The Advice Problem" and visualizes survivorship bias: you only hear from the ones who made it.
Pseudocode
SETUP:
Initialize canvas (400x300)
Pre-populate particles at random heights with random survival fates
DRAW (every frame):
Get current theme colors
Soft-clear background (trail effect)
Spawn new particle every 16 frames (if under limit):
Assign random x position
Assign survives=true (14% chance) or survives=false
If non-surviving: assign fadeStartY (random height where it dims)
For each particle:
Move upward by speed
If non-surviving and below fadeStartY: reduce alpha
If alpha reaches 0: remove
If surviving particle reaches top: promote to advisor
Draw particle with color lerped from muted (bottom) to warm (top)
For each advisor:
Float gently with sine oscillation
Every 52 frames: emit new downward ring
Update rings: move down, expand radius, fade alpha
Draw rings as thin ellipses
Draw glowing dot with pulsing radius
Trim advisors list to max 9
Draw caption: "you hear from the ones who made it"
Source Code
let sketch = function(p) {
// Many particles rise from the bottom.
// Each has a random chance of fading out before reaching the top.
// The few that reach the top become glowing "advisors" and broadcast
// downward-expanding rings — their advice radiating out.
// Most paths go silent. You only hear from those who made it.
// Represents: survivorship bias in advice.
let particles = [];
let advisors = [];
let time = 0;
function makeParticle() {
let survives = p.random() < 0.14;
return {
x: p.random(18, 382),
y: 282,
speed: p.random(0.45, 0.85),
alpha: 175,
survives: survives,
fadeStartY: survives ? 0 : p.random(28, 245),
size: p.random(1.8, 3.2),
ph: p.random(p.TWO_PI)
};
}
p.setup = function() {
p.createCanvas(400, 300);
p.colorMode(p.RGB);
for (let i = 0; i < 55; i++) {
let pt = makeParticle();
pt.y = p.random(22, 282);
if (!pt.survives && pt.y < pt.fadeStartY) {
pt.alpha = p.random(10, 140);
}
particles.push(pt);
}
};
p.draw = function() {
const colors = getThemeColors();
p.noStroke();
p.fill(...colors.bg, 32);
p.rect(0, 0, 400, 300);
time += 0.011;
if (p.frameCount % 16 === 0 && particles.length < 90) {
particles.push(makeParticle());
}
for (let i = particles.length - 1; i >= 0; i--) {
let pt = particles[i];
pt.y -= pt.speed;
if (!pt.survives && pt.y <= pt.fadeStartY) {
pt.alpha -= 4.5;
}
if (pt.alpha <= 0) {
particles.splice(i, 1);
continue;
}
if (pt.survives && pt.y < 20) {
advisors.push({ x: pt.x, y: 16, ph: pt.ph, rings: [], age: 0 });
particles.splice(i, 1);
continue;
}
let pct = p.constrain((282 - pt.y) / 260, 0, 1);
let cr = p.lerp(colors.accent3[0], colors.accent1[0], pct);
let cg = p.lerp(colors.accent3[1], colors.accent1[1], pct);
let cb = p.lerp(colors.accent3[2], colors.accent1[2], pct);
p.noStroke();
p.fill(cr, cg, cb, pt.alpha);
p.circle(pt.x, pt.y, pt.size * 2);
}
for (let sv of advisors) {
sv.age++;
sv.y = 16 + Math.sin(time * 1.7 + sv.ph) * 2.5;
if (sv.age % 52 === 8) {
sv.rings.push({ x: sv.x, y: sv.y + 10, alpha: 105, r: 5 });
}
for (let j = sv.rings.length - 1; j >= 0; j--) {
let ring = sv.rings[j];
ring.y += 1.3;
ring.r += 2.8;
ring.alpha -= 1.6;
if (ring.alpha <= 0 || ring.y > 290) {
sv.rings.splice(j, 1);
continue;
}
p.noFill();
p.stroke(...colors.accent2, ring.alpha);
p.strokeWeight(0.9);
p.ellipse(ring.x, ring.y, ring.r * 2, ring.r * 0.55);
}
let pulse = 0.82 + 0.18 * Math.sin(time * 2.3 + sv.ph);
p.noStroke();
p.fill(...colors.accent2, 42 * pulse);
p.circle(sv.x, sv.y, 20 * pulse);
p.fill(...colors.accent2, 215 * pulse);
p.circle(sv.x, sv.y, 5.5 * pulse);
}
if (advisors.length > 9) advisors.shift();
p.noStroke();
p.fill(...colors.accent3, 78);
p.textAlign(p.CENTER);
p.textSize(9);
p.text('you hear from the ones who made it', 200, 294);
};
};