Where ideas percolate and thoughts brew

Empathy Is Making You Worse at Helping People

About This Sketch

Thirty particles drift across the canvas. One is lit: glowing, labeled, drawing all attention toward it. The other twenty-nine are barely visible — same size, same motion, indistinguishable except that no spotlight has found them.

The focus shifts every few seconds. Each newly lit particle looks no different from the ones that went before it, and no different from the twenty-nine that remain unseen. The selection is arbitrary. The attention is total.

Accompanies the post on why empathy — the felt spotlight of moral attention — reliably directs care toward the most visible rather than the most needy.

Algorithm

Thirty particles drift freely across the canvas, each following simple Brownian-like motion with boundary reflection. At any moment, one particle is designated the "identified victim": it glows with concentric halos and has attention rays (animated lines) converging on it from all directions. The remaining 29 particles are rendered as dim, barely visible dots. Every 210 frames (~7 seconds at 30fps) the focus shifts to the next particle, showing how arbitrary the spotlight is — each particle looks identical when unlit, but becomes the center of all attention when selected. The annotation "29 others — unseen" remains constant, naming what the empathy spotlight renders invisible.

Pseudocode

SETUP:
  Initialize 30 particles at random positions with random drift velocities
  Assign random starting focus index

DRAW (every frame):
  Get theme colors
  Clear background

  Increment focus timer; if >= 210 frames, advance focus to next particle

  FOR each of 10 attention rays around focused particle:
    Compute ray start point at animated distance from focus center
    Animate alpha with sine wave for pulsing effect
    Draw line from ray start to focus center

  FOR each particle:
    Update position by velocity; bounce off walls

    IF focused:
      Draw two outer glow halos at decreasing opacity
      Draw bright core circle
      Draw "identified victim" label above

    ELSE:
      Draw dim small circle at low opacity

  Draw "29 others — unseen" annotation at bottom left

Source Code

let sketch = function(p) {
    let particles = [];
    let focusIndex = 0;
    let focusTimer = 0;
    const FOCUS_DURATION = 210;
    const NUM = 30;

    p.setup = function() {
        p.createCanvas(400, 300);
        p.frameRate(30);
        for (let i = 0; i < NUM; i++) {
            particles.push({
                x: p.random(15, p.width - 15),
                y: p.random(20, p.height - 20),
                vx: p.random(-0.35, 0.35),
                vy: p.random(-0.35, 0.35),
                size: p.random(2.5, 5)
            });
        }
        focusIndex = p.floor(p.random(NUM));
    };

    p.draw = function() {
        const colors = getThemeColors();
        p.background(...colors.bg);

        focusTimer++;
        if (focusTimer >= FOCUS_DURATION) {
            focusTimer = 0;
            focusIndex = (focusIndex + 1) % NUM;
        }

        let fp = particles[focusIndex];

        let numRays = 10;
        for (let i = 0; i < numRays; i++) {
            let angle = (i / numRays) * p.TWO_PI + p.frameCount * 0.002;
            let dist = 65 + p.sin(p.frameCount * 0.04 + i) * 12;
            let sx = fp.x + p.cos(angle) * dist;
            let sy = fp.y + p.sin(angle) * dist;
            let alpha = p.map(p.sin(p.frameCount * 0.07 + i * 0.9), -1, 1, 12, 65);
            p.stroke(...colors.accent1, alpha);
            p.strokeWeight(0.7);
            p.line(sx, sy, fp.x, fp.y);
        }

        for (let i = 0; i < particles.length; i++) {
            let part = particles[i];
            part.x += part.vx;
            part.y += part.vy;
            if (part.x < 5 || part.x > p.width - 5) part.vx *= -1;
            if (part.y < 5 || part.y > p.height - 5) part.vy *= -1;

            if (i === focusIndex) {
                let pulse = p.sin(p.frameCount * 0.07) * 2;
                p.noStroke();
                p.fill(...colors.accent2, 18);
                p.circle(part.x, part.y, (part.size + pulse) * 6);
                p.fill(...colors.accent2, 32);
                p.circle(part.x, part.y, (part.size + pulse) * 3.8);
                p.fill(...colors.accent1, 220);
                p.circle(part.x, part.y, part.size + pulse + 1);
                p.fill(...colors.accent3, 150);
                p.noStroke();
                p.textFont('Georgia');
                p.textSize(8);
                p.textAlign(p.CENTER);
                p.text('identified victim', part.x, part.y - part.size - 12);
            } else {
                p.noStroke();
                p.fill(...colors.accent3, 42);
                p.circle(part.x, part.y, part.size);
            }
        }

        p.noStroke();
        p.fill(...colors.text, 65);
        p.textFont('Georgia');
        p.textSize(8);
        p.textAlign(p.LEFT);
        p.text((NUM - 1) + ' others — unseen', 8, p.height - 8);
    };
};