The Resilience Trap
About This Sketch
Particles bounce endlessly inside a circular gravity well. They adapt to the walls, absorb the impacts, reach equilibrium—but the well itself never changes. Visualizing the post's core argument: that becoming better at enduring a bad situation is not the same as improving it.
The central attractor represents the pull that keeps people in situations they could otherwise leave. Particles brighten when near the walls—resilience activating at the moment of impact—then dim as they drift back toward center, still trapped.
Algorithm
Particles are initialized inside a circular boundary (the "well") and subjected
to a central gravitational pull plus small random perturbations. When they reach
the wall, they bounce elastically with slight energy loss—adapting to the constraint
rather than escaping it. The well itself never moves or changes.
Particles near the wall brighten, representing resilience activating at the moment
of impact. The central attractor glows dimly—the pull of the situation. The overall
system reaches dynamic equilibrium: perpetual motion within fixed walls.
This sketch accompanies the blog post "The Resilience Trap" and visualizes the
core argument: that optimizing endurance within a bad situation is different from
removing the situation.
Pseudocode
SETUP:
Initialize 55 particles at random positions within circular well
Assign random initial velocities and sizes
DRAW (every frame):
Get current theme colors
Fade background slightly (motion trail effect)
Draw well boundary circle (the fixed situation)
Draw glowing center attractor
For each particle:
Apply gravity toward center
Add small random perturbation
Cap maximum speed
Update position
If particle hits well wall:
Reflect velocity elastically
Lose slight energy (adaptation)
Push back inside boundary
Color particle: warmer/brighter near walls, cooler near center
Draw caption text
Source Code
let sketch = function(p) {
// Resilience trap: particles bounce energetically within a circular well
// They absorb impacts, adapt to the walls, never escape
// The well itself—the situation—never changes
// Represents endurance as optimization within a fixed, bad constraint
let particles = [];
let wellX = 200;
let wellY = 148;
let wellR = 108;
let time = 0;
p.setup = function() {
p.createCanvas(400, 300);
p.colorMode(p.RGB);
for (let i = 0; i < 55; i++) {
let angle = p.random(p.TWO_PI);
let r = p.random(18, wellR - 18);
particles.push({
x: wellX + p.cos(angle) * r,
y: wellY + p.sin(angle) * r,
vx: p.random(-1.8, 1.8),
vy: p.random(-1.8, 1.8),
size: p.random(2.5, 4.5)
});
}
};
p.draw = function() {
const colors = getThemeColors();
// Soft trail fade
p.noStroke();
p.fill(...colors.bg, 32);
p.rect(0, 0, 400, 300);
time += 0.012;
// Draw the well boundary — the unchanging situation
p.noFill();
p.stroke(...colors.accent3, 70);
p.strokeWeight(2);
p.circle(wellX, wellY, wellR * 2);
// Inner glow of the well center — the attractor that keeps them in
for (let r = wellR * 0.55; r >= 10; r -= 12) {
let a = p.map(r, 10, wellR * 0.55, 25, 0);
p.noStroke();
p.fill(...colors.accent2, a);
p.circle(wellX, wellY, r * 2);
}
p.fill(...colors.accent2, 160);
p.noStroke();
p.circle(wellX, wellY, 8);
// Update and draw particles
for (let pt of particles) {
// Gravity toward center (the pull of the situation)
let dx = wellX - pt.x;
let dy = wellY - pt.y;
let dist = p.sqrt(dx * dx + dy * dy) + 0.001;
let pull = 0.07;
pt.vx += (dx / dist) * pull;
pt.vy += (dy / dist) * pull;
// Tiny noise — "trying" within the walls
pt.vx += p.random(-0.06, 0.06);
pt.vy += p.random(-0.06, 0.06);
// Speed cap
let speed = p.sqrt(pt.vx * pt.vx + pt.vy * pt.vy);
if (speed > 2.8) {
pt.vx = (pt.vx / speed) * 2.8;
pt.vy = (pt.vy / speed) * 2.8;
}
pt.x += pt.vx;
pt.y += pt.vy;
// Elastic bounce off well walls
let newDist = p.sqrt((pt.x - wellX) * (pt.x - wellX) + (pt.y - wellY) * (pt.y - wellY));
if (newDist > wellR - 6) {
let nx = (pt.x - wellX) / newDist;
let ny = (pt.y - wellY) / newDist;
let dot = pt.vx * nx + pt.vy * ny;
pt.vx -= 2 * dot * nx;
pt.vy -= 2 * dot * ny;
// Slight energy loss on impact — adaptation, not escape
pt.vx *= 0.88;
pt.vy *= 0.88;
pt.x = wellX + nx * (wellR - 7);
pt.y = wellY + ny * (wellR - 7);
}
// Color by proximity to wall: near wall = brighter (resilience activated)
let wallProximity = newDist / wellR;
let alpha = p.map(wallProximity, 0, 1, 100, 220);
if (wallProximity > 0.7) {
p.fill(...colors.accent2, alpha);
} else {
p.fill(...colors.accent1, alpha * 0.8);
}
p.noStroke();
p.circle(pt.x, pt.y, pt.size);
}
// Caption
p.fill(...colors.accent3, 95);
p.noStroke();
p.textAlign(p.CENTER);
p.textSize(9);
p.text('bouncing endlessly — the walls never change', 200, 293);
};
};