The Empathy Trap
About This Sketch
Seventy dots, each representing a person. A spotlight drifts between them one at a time, illuminating whoever it lands on while leaving the rest in near-darkness. Attention particles flow from all directions toward the single lit figure. The statistics in the corner stay stubbornly fixed: one in focus, sixty-nine in shadow. The spotlight will move again soon — but never to more than one.
Algorithm
Seventy people-dots are scattered randomly across the canvas. A spotlight
drifts between them, randomly selecting a new target every 150 frames and
smoothly lerping toward it. The person nearest the spotlight glows brightly
while all others remain near-invisible. Attention particles spawn from the
canvas edges and stream toward the spotlight position, representing how
resources and concern flow toward whoever is currently visible. A readout
tracks how many are in focus versus in shadow at any moment.
Pseudocode
SETUP:
Place 70 person-dots at random positions across canvas
Initialize spotlight at first random target
DRAW each frame:
Increment spotlight timer
If timer > 150:
Choose new random target (different from current)
Reset timer
Lerp spotlight position toward current target
For each person:
Compute distance to spotlight
Set target brightness: high if near spotlight, near-zero otherwise
Lerp current brightness toward target
Every 8 frames: spawn attention particle from random canvas edge
For each attention particle:
Move toward spotlight position
Fade alpha over time
Remove if reached spotlight or fully faded
Draw ambient glow rings under spotlight
Draw all person-dots sized and colored by brightness
Draw attention particles as tiny dots
Display in-focus / in-shadow count
Draw caption
Source Code
let sketch = function(p) {
const W = 400, H = 300;
const N = 70;
let people = [];
let spotlight = { x: 200, y: 150, targetIdx: 0, timer: 0 };
let attention = [];
let attentionTimer = 0;
p.setup = function() {
p.createCanvas(W, H);
p.randomSeed(42);
for (let i = 0; i < N; i++) {
people.push({
x: 15 + p.random(370),
y: 15 + p.random(260),
brightness: 0.05,
size: 2 + p.random(3)
});
}
spotlight.targetIdx = Math.floor(p.random(N));
spotlight.x = people[spotlight.targetIdx].x;
spotlight.y = people[spotlight.targetIdx].y;
};
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
spotlight.timer++;
if (spotlight.timer > 150) {
spotlight.timer = 0;
let newTarget;
do { newTarget = Math.floor(p.random(N)); } while (newTarget === spotlight.targetIdx);
spotlight.targetIdx = newTarget;
}
let tgt = people[spotlight.targetIdx];
spotlight.x = p.lerp(spotlight.x, tgt.x, 0.04);
spotlight.y = p.lerp(spotlight.y, tgt.y, 0.04);
for (let i = 0; i < N; i++) {
let d = p.dist(people[i].x, people[i].y, spotlight.x, spotlight.y);
let targetB = d < 25 ? p.map(d, 0, 25, 1.0, 0.15) : 0.04;
people[i].brightness = p.lerp(people[i].brightness, targetB, 0.06);
}
attentionTimer++;
if (attentionTimer > 8) {
attentionTimer = 0;
let edge = Math.floor(p.random(4));
let ax, ay;
if (edge === 0) { ax = p.random(W); ay = 0; }
else if (edge === 1) { ax = p.random(W); ay = H; }
else if (edge === 2) { ax = 0; ay = p.random(H); }
else { ax = W; ay = p.random(H); }
attention.push({ x: ax, y: ay, alpha: 130, speed: 1.5 + p.random(1.5) });
}
for (let r = 52; r > 0; r -= 6) {
let a = p.map(r, 0, 52, 32, 0);
p.noStroke();
p.fill(...colors.accent1, a);
p.ellipse(spotlight.x, spotlight.y, r * 2);
}
p.noStroke();
for (let i = 0; i < N; i++) {
let b = people[i].brightness;
let sz = people[i].size;
if (b > 0.5) {
p.fill(...colors.accent1, b * 55);
p.ellipse(people[i].x, people[i].y, sz * 5);
}
p.fill(colors.accent2[0], colors.accent2[1], colors.accent2[2], b * 220 + 10);
p.ellipse(people[i].x, people[i].y, sz * (1 + b * 0.8));
}
p.noStroke();
for (let i = attention.length - 1; i >= 0; i--) {
let a = attention[i];
let dx = spotlight.x - a.x;
let dy = spotlight.y - a.y;
let d = Math.sqrt(dx * dx + dy * dy);
if (d < 5) { attention.splice(i, 1); continue; }
a.x += (dx / d) * a.speed;
a.y += (dy / d) * a.speed;
a.alpha -= 2;
if (a.alpha <= 0) { attention.splice(i, 1); continue; }
p.fill(...colors.accent1, a.alpha * 0.55);
p.ellipse(a.x, a.y, 2);
}
let litCount = 0;
for (let i = 0; i < N; i++) { if (people[i].brightness > 0.4) litCount++; }
p.noStroke();
p.textAlign(p.LEFT);
p.textSize(8);
p.fill(...colors.accent2, 140);
p.text('in focus: ' + litCount, 10, 14);
p.fill(...colors.accent3, 90);
p.text('in shadow: ' + (N - litCount), 10, 26);
p.noStroke();
p.fill(...colors.accent3, 55);
p.textAlign(p.CENTER);
p.textSize(8);
p.text('one face visible \u00b7 thousands in the dark', W / 2, H - 5);
};
};