The Simplicity Trap
About This Sketch
A living visualization of the simplicity trap. A complex, interconnected network of points flows organically in the background—representing reality's true complexity. Overlaid on this are clean geometric shapes with reductive labels like "X → Y" and "ONE CAUSE"—representing our simple explanations.
The shapes are clear and easy to understand. The network beneath is messy and hard to track. But watch the "complexity captured" metric: our simple explanations typically capture only 20-40% of what's actually happening. The rest is discarded, ignored, or explained away.
Simple explanations feel like understanding. But this sketch makes visible what they cost—the 60-80% of reality we pretend doesn't exist because it doesn't fit our clean framework.
Algorithm
Pseudocode
SETUP:
Create 50 complex points at random positions
Give each point velocity and noise offset
Connect each point to its 4 nearest neighbors
Create 3 simple geometric overlays (circle, square, line)
Initialize each overlay with a reductive label
DRAW (every frame):
Get current theme colors
Clear background
For each complex point:
Update position using Perlin noise field
Apply velocity and damping
Wrap around canvas edges
Draw connections between nearby points (< 80px apart)
Draw all complex points as small circles
Overlay simple geometric shapes with labels
Fade in overlays gradually
Calculate how many complex points fall within simple shapes
Display percentage of "complexity captured"
Show alternating text about the trap
Source Code
let sketch = function(p) {
// Visualization: The Simplicity Trap
// Shows a complex underlying reality (organic, flowing, interconnected)
// Overlaid with simple geometric shapes that attempt to "explain" it
// The simple shapes are clear and clean but miss most of the complexity
let complexity = []; // Underlying complex reality
let simplifications = []; // Simple explanations overlaid
let time = 0;
class ComplexPoint {
constructor(x, y) {
this.x = x;
this.y = y;
this.vx = p.random(-0.5, 0.5);
this.vy = p.random(-0.5, 0.5);
this.offset = p.random(1000);
this.connections = [];
}
update() {
// Complex, organic movement
let noiseX = p.noise(this.x * 0.01, this.y * 0.01, time * 0.01 + this.offset);
let noiseY = p.noise(this.x * 0.01 + 100, this.y * 0.01 + 100, time * 0.01 + this.offset);
this.vx += (noiseX - 0.5) * 0.1;
this.vy += (noiseY - 0.5) * 0.1;
// Damping
this.vx *= 0.95;
this.vy *= 0.95;
this.x += this.vx;
this.y += this.vy;
// Wrap around
if (this.x < 0) this.x = 400;
if (this.x > 400) this.x = 0;
if (this.y < 0) this.y = 300;
if (this.y > 300) this.y = 0;
}
display(colors) {
p.noStroke();
p.fill(...colors.accent3, 100);
p.circle(this.x, this.y, 3);
}
drawConnections(colors) {
for (let other of this.connections) {
let d = p.dist(this.x, this.y, other.x, other.y);
if (d < 80) {
let alpha = p.map(d, 0, 80, 60, 0);
p.stroke(...colors.accent3, alpha);
p.strokeWeight(0.5);
p.line(this.x, this.y, other.x, other.y);
}
}
}
}
class Simplification {
constructor(type, x, y, size) {
this.type = type; // 'circle', 'square', 'line'
this.x = x;
this.y = y;
this.size = size;
this.alpha = 0;
this.targetAlpha = 180;
this.label = this.generateLabel();
}
generateLabel() {
const labels = ['X → Y', 'SIMPLE', 'A = B', 'ONE CAUSE', 'CLEAR'];
return p.random(labels);
}
update() {
// Fade in
if (this.alpha < this.targetAlpha) {
this.alpha += 3;
}
}
display(colors) {
p.noFill();
p.stroke(...colors.accent2, this.alpha);
p.strokeWeight(2);
if (this.type === 'circle') {
p.circle(this.x, this.y, this.size);
} else if (this.type === 'square') {
p.rectMode(p.CENTER);
p.rect(this.x, this.y, this.size, this.size);
} else if (this.type === 'line') {
p.line(this.x - this.size/2, this.y, this.x + this.size/2, this.y);
}
// Label
p.noStroke();
p.fill(...colors.accent2, this.alpha);
p.textAlign(p.CENTER, p.CENTER);
p.textSize(9);
p.text(this.label, this.x, this.y);
}
}
p.setup = function() {
p.createCanvas(400, 300);
// Create complex underlying reality
for (let i = 0; i < 50; i++) {
complexity.push(new ComplexPoint(
p.random(400),
p.random(300)
));
}
// Set up connections
for (let i = 0; i < complexity.length; i++) {
// Connect to nearest neighbors
let distances = [];
for (let j = 0; j < complexity.length; j++) {
if (i !== j) {
let d = p.dist(complexity[i].x, complexity[i].y, complexity[j].x, complexity[j].y);
distances.push({point: complexity[j], dist: d});
}
}
distances.sort((a, b) => a.dist - b.dist);
complexity[i].connections = distances.slice(0, 4).map(d => d.point);
}
// Create simple explanations that overlay the complexity
simplifications.push(new Simplification('circle', 200, 100, 80));
simplifications.push(new Simplification('square', 100, 200, 60));
simplifications.push(new Simplification('line', 300, 200, 100));
};
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
time++;
// Title
p.fill(...colors.accent3, 200);
p.noStroke();
p.textAlign(p.LEFT);
p.textSize(11);
p.text('The Simplicity Trap', 15, 25);
// Update and display complex reality (background layer)
for (let point of complexity) {
point.update();
}
// Draw connections first (behind)
for (let point of complexity) {
point.drawConnections(colors);
}
// Draw points
for (let point of complexity) {
point.display(colors);
}
// Overlay simple explanations
for (let simp of simplifications) {
simp.update();
simp.display(colors);
}
// Legend
p.textAlign(p.LEFT);
p.textSize(7);
p.fill(...colors.accent3, 150);
p.text('Gray network = Complex reality', 15, 45);
p.fill(...colors.accent2, 150);
p.text('Geometric shapes = Simple explanations', 15, 55);
// Bottom text
p.textAlign(p.CENTER);
p.textSize(7);
p.fill(...colors.accent3, 180);
if (time < 240) {
p.text('Reality is complex, interconnected, context-dependent.', 200, 275);
p.text('Our explanations are clean, geometric, certain—and wrong.', 200, 285);
} else if (time < 480) {
p.text('Simple explanations feel like understanding.', 200, 275);
p.text('But they discard essential complexity.', 200, 285);
} else {
time = 0; // Reset
}
// Show how much complexity is "missed"
let captured = 0;
let total = complexity.length;
for (let point of complexity) {
for (let simp of simplifications) {
let d = p.dist(point.x, point.y, simp.x, simp.y);
if (simp.type === 'circle' && d < simp.size/2) {
captured++;
break;
} else if (simp.type === 'square' &&
Math.abs(point.x - simp.x) < simp.size/2 &&
Math.abs(point.y - simp.y) < simp.size/2) {
captured++;
break;
}
}
}
p.textAlign(p.RIGHT);
p.textSize(7);
p.fill(...colors.accent2, 180);
let percent = Math.round((captured / total) * 100);
p.text(`Complexity captured: ${percent}%`, 385, 285);
};
};