Where ideas percolate and thoughts brew

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