Where ideas percolate and thoughts brew

The Clarity Trap

About This Sketch

A visual exploration of the tension between explicit and implicit knowledge. The left side shows clear, geometric forms that are rigid and brittle. The right side shows organic, flowing shapes that resist definition but remain rich and alive. In between, translation attempts fragment and fade—some things lose their essence when forced into clarity.

This sketch accompanies the essay "The Clarity Trap," which argues that while clarity is valuable in certain domains, many of our most important human capacities—intuition, tacit knowledge, artistic sensibility—are valuable precisely because they resist being made fully explicit.

Algorithm

This sketch visualizes the tension between explicit and implicit knowledge through contrasting visual languages. The left side displays rigid geometric forms—perfect squares with clear grid structures that snap to precise angles. These represent explicit knowledge: clear, measurable, easily articulated, but rigid and brittle. The right side shows organic, flowing shapes with soft curves and subtle breathing motions. Vertices drift organically using Perlin noise, creating forms that resist precise definition. These represent implicit knowledge: intuitive expertise, artistic sensibility, tacit understanding—rich and adaptive but ineffable. In the center "translation zone," ephemeral lines attempt to bridge between these two domains, but they fragment and fade, visualizing the information loss that occurs when we force implicit knowledge into explicit structures or vice versa. The sketch illustrates the core thesis: some valuable things resist clarity, and forcing them into explicit frameworks destroys rather than preserves them.

Pseudocode

SETUP:
  Create 3 explicit elements (left side, geometric)
  Create 3 implicit elements (right side, organic)
  Initialize empty translation array

EXPLICIT ELEMENT:
  Position: Fixed on left side
  Shape: Perfect square with grid lines
  Motion: Snap to precise angles (no smoothness)
  Appearance: Sharp, clear, structured

IMPLICIT ELEMENT:
  Position: Fixed on right side
  Shape: Organic blob with 8 vertices
  Motion: Smooth rotation, breathing animation
  Vertices: Drift using Perlin noise
  Appearance: Flowing, curved, ineffable

TRANSLATION:
  Spawn periodically in center zone
  Life: Fades over time
  Display: Fragmented lines showing information loss
  From explicit: Lines break apart and scatter
  From implicit: Lines fade and lose nuance

DRAW (each frame):
  Clear background with theme colors
  Draw labels and center dividing line
  Highlight translation zone with subtle fill
  Update all explicit elements (rigid motion)
  Update all implicit elements (organic motion)
  Create new translation attempts periodically
  Update and display all translations
  Remove dead translations
  Display key insight text

Source Code

let sketch = function(p) {
    // Visualization: The Clarity Trap
    // Left side: clear, explicit, geometric structure (rigid, brittle)
    // Right side: implicit, flowing forms (rich, adaptive, alive)
    // Center: attempting to translate one to the other (loss of information)

    let explicits = [];
    let implicits = [];
    let translations = [];
    let time = 0;

    class ExplicitElement {
        constructor(y) {
            this.x = 75;
            this.y = y;
            this.size = 30;
            this.angle = 0;
            this.targetAngle = 0;
            this.clarity = 1.0;
        }

        update() {
            // Rigid, precise rotation
            this.targetAngle = p.floor(p.frameCount / 60) * p.PI / 4;
            this.angle = this.targetAngle; // Snap to angle, no smoothness
        }

        display(colors) {
            p.push();
            p.translate(this.x, this.y);
            p.rotate(this.angle);

            // Sharp, clear geometric shapes
            p.stroke(...colors.accent2, 200);
            p.strokeWeight(2);
            p.noFill();

            // Perfect square - maximally clear
            p.rect(-this.size/2, -this.size/2, this.size, this.size);

            // Grid lines inside - explicitly structured
            p.stroke(...colors.accent2, 100);
            p.strokeWeight(1);
            p.line(-this.size/2, 0, this.size/2, 0);
            p.line(0, -this.size/2, 0, this.size/2);

            p.pop();
        }
    }

    class ImplicitElement {
        constructor(y) {
            this.x = 325;
            this.y = y;
            this.baseSize = 35;
            this.size = this.baseSize;
            this.angle = p.random(p.TWO_PI);
            this.angleSpeed = p.random(-0.03, 0.03);
            this.breathe = p.random(p.TWO_PI);
            this.vertices = [];
            this.generateVertices();
        }

        generateVertices() {
            // Organic, flowing shape - resists clear definition
            let points = 8;
            this.vertices = [];
            for (let i = 0; i < points; i++) {
                let angle = (i / points) * p.TWO_PI;
                let r = this.size/2 * p.random(0.7, 1.3);
                this.vertices.push({
                    angle: angle,
                    radius: r,
                    noiseOffset: p.random(1000)
                });
            }
        }

        update() {
            this.angle += this.angleSpeed;
            this.breathe += 0.02;

            // Organic breathing motion
            this.size = this.baseSize + p.sin(this.breathe) * 5;

            // Vertices drift subtly
            for (let v of this.vertices) {
                v.radius += p.noise(v.noiseOffset + time * 0.01) * 0.3 - 0.15;
                v.radius = p.constrain(v.radius, this.baseSize * 0.4, this.baseSize * 0.9);
            }
        }

        display(colors) {
            p.push();
            p.translate(this.x, this.y);
            p.rotate(this.angle);

            // Soft, flowing forms
            p.stroke(...colors.accent1, 150);
            p.strokeWeight(2);
            p.noFill();

            // Draw organic shape
            p.beginShape();
            for (let v of this.vertices) {
                let x = p.cos(v.angle) * v.radius;
                let y = p.sin(v.angle) * v.radius;
                p.curveVertex(x, y);
            }
            // Close the shape smoothly
            let first = this.vertices[0];
            p.curveVertex(p.cos(first.angle) * first.radius, p.sin(first.angle) * first.radius);
            let second = this.vertices[1];
            p.curveVertex(p.cos(second.angle) * second.radius, p.sin(second.angle) * second.radius);
            p.endShape();

            // Subtle inner detail - hints at depth
            p.stroke(...colors.accent1, 60);
            p.strokeWeight(1);
            for (let i = 0; i < 3; i++) {
                let v = this.vertices[i * 2];
                let x = p.cos(v.angle) * v.radius * 0.6;
                let y = p.sin(v.angle) * v.radius * 0.6;
                p.circle(x, y, 3);
            }

            p.pop();
        }
    }

    class Translation {
        constructor() {
            this.x = 200;
            this.y = p.random(80, 220);
            this.life = 1.0;
            this.fromSide = p.random() > 0.5 ? 'explicit' : 'implicit';
        }

        update() {
            this.life -= 0.008;
        }

        display(colors) {
            if (this.life <= 0) return;

            let alpha = this.life * 100;

            // Show information loss during translation
            p.stroke(...colors.accent3, alpha);
            p.strokeWeight(1);

            if (this.fromSide === 'explicit') {
                // Trying to force implicit into explicit - breaks apart
                for (let i = 0; i < 5; i++) {
                    let offset = (1 - this.life) * 40;
                    p.line(
                        120 + i * 8, this.y,
                        120 + i * 8 + offset, this.y + p.random(-offset, offset)
                    );
                }
            } else {
                // Trying to force explicit into implicit - loses nuance
                let points = 8;
                for (let i = 0; i < points - 1; i++) {
                    let progress = i / points;
                    let x1 = 280 - progress * 40;
                    let x2 = 280 - (progress + 0.1) * 40;
                    let fade = progress * this.life;
                    p.stroke(...colors.accent2, alpha * fade);
                    p.line(x1, this.y, x2, this.y);
                }
            }
        }

        isDead() {
            return this.life <= 0;
        }
    }

    p.setup = function() {
        p.createCanvas(400, 300);

        // Create elements
        for (let i = 0; i < 3; i++) {
            explicits.push(new ExplicitElement(80 + i * 70));
            implicits.push(new ImplicitElement(80 + i * 70));
        }
    };

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

        time++;

        // Labels
        p.fill(...colors.accent3, 220);
        p.noStroke();
        p.textAlign(p.CENTER, p.TOP);
        p.textSize(11);
        p.textFont('Georgia');
        p.text('Explicit', 75, 15);
        p.text('Implicit', 325, 15);

        // Descriptions
        p.textSize(8);
        p.fill(...colors.accent3, 180);
        p.text('Clear but brittle', 75, 30);
        p.text('Rich but ineffable', 325, 30);

        // Dividing line
        p.stroke(...colors.accent3, 60);
        p.strokeWeight(1);
        p.line(200, 45, 200, 260);

        // Warning zone in the middle
        p.fill(...colors.accent2, 40);
        p.noStroke();
        p.rect(175, 45, 50, 215);

        // Create new translation attempts periodically
        if (time % 120 === 0) {
            translations.push(new Translation());
        }

        // Update and display everything
        for (let e of explicits) {
            e.update();
            e.display(colors);
        }

        for (let i of implicits) {
            i.update();
            i.display(colors);
        }

        for (let i = translations.length - 1; i >= 0; i--) {
            translations[i].update();
            translations[i].display(colors);

            if (translations[i].isDead()) {
                translations.splice(i, 1);
            }
        }

        // Key insight
        p.fill(...colors.accent2, 180);
        p.textSize(8);
        p.textAlign(p.CENTER);
        p.text('Some things lose their value when forced into clarity', 200, 275);

        // Small annotation for translation zone
        if (time > 100) {
            p.fill(...colors.accent3, 160);
            p.textSize(7);
            p.text('Lost in translation', 200, 55);
        }
    };
};