Where ideas percolate and thoughts brew

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);
    };
};