The Legibility Trap
About This Sketch
A visualization of how being too legible—too easy to categorize and understand—traps you in boxes and diminishes your distinctive value. Bright particles drift freely as illegible entities. When they get too close to category clusters (Engineer, Designer, etc.), they become captured, fading and shrinking as they lose their uniqueness. Some escape back to freedom. The sketch illustrates the strategic value of maintaining ambiguity and resisting premature categorization.
Algorithm
This sketch visualizes the tension between illegibility (freedom) and categorization (constraint).
Particles represent people or value propositions. They start as "illegible"—drifting freely through space, bright and distinctive. Clusters represent career categories or identity boxes (Engineer, Designer, Marketer, Writer, Manager).
When illegible particles drift too close to a cluster's capture radius, they become "categorized"—pulled into orbit around that cluster, shrinking in size and fading in opacity. Categorized particles lose their freedom and distinctiveness, becoming just another member of that category.
Occasionally, particles "escape" categorization, breaking free from their cluster to drift independently again, regaining their brightness and size.
The sketch tracks the count of illegible vs categorized particles, illustrating how maintaining strategic ambiguity preserves value and freedom, while being too easily categorized leads to commoditization and constraint.
Pseudocode
SETUP:
Create 5 category clusters (job titles/boxes)
Initialize 60 particles at random positions
All particles start as "illegible" (uncategorized)
DRAW (every frame):
Get current theme colors
Clear background
FOR each cluster:
Draw cluster boundary circle
Display cluster label
FOR each particle:
IF particle is illegible:
Drift freely with random motion
Stay bright and distinctive
Check proximity to clusters
IF particle is categorized:
Get pulled toward cluster center
Orbit around cluster
Fade and shrink (commoditized)
Occasionally escape (1% chance)
Draw particle with appropriate color/opacity
Display counts of illegible vs categorized particles
Source Code
let sketch = function(p) {
let particles = [];
let clusters = [];
let numClusters = 5;
class Particle {
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.category = null;
this.transitionProgress = 0;
this.targetCluster = null;
this.size = p.random(3, 6);
this.opacity = 255;
}
update() {
if (this.category === null) {
this.vx += p.random(-0.1, 0.1);
this.vy += p.random(-0.1, 0.1);
this.vx *= 0.98;
this.vy *= 0.98;
this.x += this.vx;
this.y += this.vy;
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;
this.opacity = 200;
} else {
let cluster = clusters[this.category];
let dx = cluster.x - this.x;
let dy = cluster.y - this.y;
let dist = p.sqrt(dx * dx + dy * dy);
if (dist > 2) {
this.vx = dx * 0.02;
this.vy = dy * 0.02;
this.x += this.vx;
this.y += this.vy;
}
let angle = p.atan2(dy, dx);
angle += 0.02;
let orbitRadius = 25;
this.x += p.cos(angle) * 0.3;
this.y += p.sin(angle) * 0.3;
this.opacity = 120;
}
}
display(colors) {
p.noStroke();
if (this.category === null) {
p.fill(...colors.accent1, this.opacity);
} else {
p.fill(...colors.accent3, this.opacity);
}
p.circle(this.x, this.y, this.size);
}
attemptCategorize() {
for (let i = 0; i < clusters.length; i++) {
let cluster = clusters[i];
let dx = cluster.x - this.x;
let dy = cluster.y - this.y;
let dist = p.sqrt(dx * dx + dy * dy);
if (dist < cluster.captureRadius) {
this.category = i;
this.size *= 0.7;
return;
}
}
}
escape() {
if (this.category !== null && p.random() < 0.01) {
this.category = null;
this.size *= 1.4;
this.vx = p.random(-2, 2);
this.vy = p.random(-2, 2);
}
}
}
class Cluster {
constructor(x, y, label) {
this.x = x;
this.y = y;
this.label = label;
this.captureRadius = 40;
this.size = 50;
}
display(colors) {
p.noFill();
p.stroke(...colors.accent2, 80);
p.strokeWeight(1.5);
p.circle(this.x, this.y, this.captureRadius * 2);
p.noStroke();
p.fill(...colors.accent2, 150);
p.textAlign(p.CENTER, p.CENTER);
p.textSize(8);
p.text(this.label, this.x, this.y);
}
}
p.setup = function() {
p.createCanvas(400, 300);
clusters.push(new Cluster(80, 80, "ENGINEER"));
clusters.push(new Cluster(320, 80, "DESIGNER"));
clusters.push(new Cluster(80, 220, "MARKETER"));
clusters.push(new Cluster(320, 220, "WRITER"));
clusters.push(new Cluster(200, 150, "MANAGER"));
for (let i = 0; i < 60; i++) {
particles.push(new Particle(p.random(400), p.random(300)));
}
};
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
for (let cluster of clusters) {
cluster.display(colors);
}
for (let particle of particles) {
particle.update();
particle.attemptCategorize();
particle.escape();
particle.display(colors);
}
p.fill(...colors.accent3);
p.noStroke();
p.textAlign(p.CENTER);
p.textSize(11);
p.text('The Legibility Trap', 200, 15);
let illegible = particles.filter(p => p.category === null).length;
let categorized = particles.length - illegible;
p.textAlign(p.LEFT);
p.textSize(8);
p.fill(...colors.accent1);
p.text(`Illegible: ${illegible}`, 10, 290);
p.fill(...colors.accent3);
p.text(`Categorized: ${categorized}`, 110, 290);
p.fill(...colors.accent3, 150);
p.textSize(7);
p.text('Bright particles resist categories. Dim ones get trapped in boxes.', 10, 35);
};
};