Where ideas percolate and thoughts brew

The Expertise Blindness

About This Sketch

Visualizing expertise blindness Expert path: narrow, deep, efficient but constrained by framework walls Beginner exploration: wide, shallow, inefficient but can see the whole landscape Shows how expertise's focused efficiency creates blindness to alternatives

This sketch accompanies the blog post "The Expertise Blindness" and visualizes its core concepts through generative art.

Algorithm

Visualizing expertise blindness Expert path: narrow, deep, efficient but constrained by framework walls Beginner exploration: wide, shallow, inefficient but can see the whole landscape Shows how expertise's focused efficiency creates blindness to alternatives This sketch was originally created as a visual companion to the blog post "The Expertise Blindness" and explores its themes through generative art.

Pseudocode

SETUP:
  Initialize canvas (400x300)
  Set up drawing parameters

DRAW (every frame):
  Get current theme colors
  Clear background
  Draw generative visualization
  Update animation state

Source Code

let sketch = function(p) {

    // Visualizing expertise blindness
    // Expert path: narrow, deep, efficient but constrained by framework walls
    // Beginner exploration: wide, shallow, inefficient but can see the whole landscape
    // Shows how expertise's focused efficiency creates blindness to alternatives

    let time = 0;
    let expertAgent;
    let beginnerAgents = [];
    let frameworkWalls = [];
    let target;

    class FrameworkWall {
        constructor(x, y, width, height) {
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
        }

        display(colors) {
            p.push();
            p.fill(...colors.accent3, 100);
            p.noStroke();
            p.rect(this.x, this.y, this.width, this.height);

            // Grid pattern to show structure
            p.stroke(...colors.accent3, 60);
            p.strokeWeight(1);
            for (let x = this.x; x < this.x + this.width; x += 15) {
                p.line(x, this.y, x, this.y + this.height);
            }
            for (let y = this.y; y < this.y + this.height; y += 15) {
                p.line(this.x, y, this.x + this.width, y);
            }
            p.pop();
        }
    }

    class ExpertAgent {
        constructor(x, y) {
            this.x = x;
            this.y = y;
            this.targetX = x;
            this.targetY = y;
            this.speed = 2;
            this.path = [{x: x, y: y}];
            this.stuck = false;
            this.confidence = 1;
        }

        update() {
            // Expert follows narrow, efficient path within framework
            // Moving toward target but constrained by walls
            if (!this.stuck) {
                // Try to move toward local optimization
                let dx = this.targetX - this.x;
                let dy = this.targetY - this.y;
                let dist = p.sqrt(dx * dx + dy * dy);

                if (dist > 2) {
                    this.x += (dx / dist) * this.speed;
                    this.y += (dy / dist) * this.speed;

                    // Record path
                    if (p.frameCount % 5 === 0) {
                        this.path.push({x: this.x, y: this.y});
                        if (this.path.length > 40) {
                            this.path.shift();
                        }
                    }
                }

                // Check if stuck (hit framework wall)
                if (time > 180 && this.y < 120) {
                    this.stuck = true;
                    this.confidence = 0.3;
                }
            } else {
                // Stuck, optimizing locally but can't escape framework
                this.x += p.sin(time * 0.05) * 0.5;
            }
        }

        display(colors) {
            // Draw path
            p.push();
            p.noFill();
            p.stroke(...colors.accent2, 150);
            p.strokeWeight(2);
            p.beginShape();
            for (let point of this.path) {
                p.vertex(point.x, point.y);
            }
            p.endShape();
            p.pop();

            // Agent
            p.push();
            p.fill(...colors.accent2, 200);
            p.noStroke();
            p.circle(this.x, this.y, 10);

            // Confidence rays (narrow focus)
            if (!this.stuck) {
                for (let a = -p.PI/8; a <= p.PI/8; a += p.PI/16) {
                    let rayLength = 30;
                    let endX = this.x + p.cos(a - p.PI/2) * rayLength;
                    let endY = this.y + p.sin(a - p.PI/2) * rayLength;
                    p.stroke(...colors.accent2, 80);
                    p.strokeWeight(1);
                    p.line(this.x, this.y, endX, endY);
                }
            }
            p.pop();

            // Label
            if (time > 60) {
                let labelAlpha = Math.min((time - 60) * 2, 160);
                p.fill(...colors.accent3, labelAlpha);
                p.textAlign(p.CENTER, p.TOP);
                p.textSize(7);
                p.text(this.stuck ? "Expert (stuck)" : "Expert", this.x, this.y + 15);
            }
        }
    }

    class BeginnerAgent {
        constructor(x, y, id) {
            this.x = x;
            this.y = y;
            this.id = id;
            this.vx = p.random(-1, 1);
            this.vy = p.random(-1, 1);
            this.path = [{x: x, y: y}];
            this.foundTarget = false;
            this.wandering = true;
        }

        update() {
            if (!this.foundTarget) {
                // Random exploration - inefficient but sees everything
                this.vx += p.random(-0.3, 0.3);
                this.vy += p.random(-0.3, 0.3);

                // Limit speed
                let speed = p.sqrt(this.vx * this.vx + this.vy * this.vy);
                if (speed > 1.5) {
                    this.vx *= 1.5 / speed;
                    this.vy *= 1.5 / speed;
                }

                this.x += this.vx;
                this.y += this.vy;

                // Bounce off edges
                if (this.x < 20 || this.x > 380) this.vx *= -1;
                if (this.y < 60 || this.y > 280) this.vy *= -1;

                // Record path
                if (p.frameCount % 8 === 0) {
                    this.path.push({x: this.x, y: this.y});
                    if (this.path.length > 25) {
                        this.path.shift();
                    }
                }

                // Check if found simple solution (reached target area)
                if (time > 300 && this.id === 1 &&
                    this.x > 320 && this.x < 360 && this.y > 60 && this.y < 100) {
                    this.foundTarget = true;
                }
            }
        }

        display(colors) {
            // Draw path (lighter, more exploratory)
            p.push();
            p.noFill();
            p.stroke(...colors.accent1, 80);
            p.strokeWeight(1);
            p.beginShape();
            for (let point of this.path) {
                p.vertex(point.x, point.y);
            }
            p.endShape();
            p.pop();

            // Agent
            p.push();
            let agentColor = this.foundTarget ? colors.accent1 : colors.accent3;
            let agentAlpha = this.foundTarget ? 220 : 160;
            p.fill(...agentColor, agentAlpha);
            p.noStroke();
            p.circle(this.x, this.y, this.foundTarget ? 12 : 7);

            if (this.foundTarget) {
                // Glow for success
                for (let r = 20; r > 0; r -= 5) {
                    p.fill(...colors.accent1, p.map(r, 0, 20, 0, 60));
                    p.circle(this.x, this.y, r);
                }
            }
            p.pop();
        }
    }

    class Target {
        constructor(x, y) {
            this.x = x;
            this.y = y;
            this.size = 20;
        }

        display(colors) {
            if (time > 240) {
                let targetAlpha = Math.min((time - 240) * 2, 180);
                p.push();

                // Outer ring
                p.noFill();
                p.stroke(...colors.accent1, targetAlpha);
                p.strokeWeight(2);
                p.circle(this.x, this.y, this.size);

                // Inner dot
                p.fill(...colors.accent1, targetAlpha);
                p.noStroke();
                p.circle(this.x, this.y, 8);

                // Label
                p.textAlign(p.CENTER, p.TOP);
                p.textSize(7);
                p.fill(...colors.accent3, targetAlpha);
                p.text("Simple solution", this.x, this.y + 15);

                p.pop();
            }
        }
    }

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

        // Create framework walls - constrain expert's path
        frameworkWalls.push(new FrameworkWall(60, 100, 260, 15)); // Top wall
        frameworkWalls.push(new FrameworkWall(60, 100, 15, 120)); // Left wall
        frameworkWalls.push(new FrameworkWall(305, 100, 15, 120)); // Right wall

        // Expert starts bottom, moves toward local optimum but gets trapped
        expertAgent = new ExpertAgent(180, 240);
        expertAgent.targetX = 180;
        expertAgent.targetY = 150;

        // Multiple beginners exploring randomly
        for (let i = 0; i < 4; i++) {
            beginnerAgents.push(new BeginnerAgent(
                p.random(80, 300),
                p.random(160, 260),
                i
            ));
        }

        // Target is outside framework walls - visible to beginners, not expert
        target = new Target(340, 80);
    };

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

        // Title
        p.noStroke();
        p.fill(...colors.accent3, 180);
        p.textAlign(p.CENTER, p.TOP);
        p.textSize(11);
        p.text("The Expertise Blindness", 200, 15);

        // Subtitle
        if (time > 30) {
            let subtitleAlpha = Math.min((time - 30) * 2, 140);
            p.textSize(7);
            p.fill(...colors.accent3, subtitleAlpha);
            p.text("Expert's efficient framework vs. Beginner's wandering exploration", 200, 30);
        }

        // Draw framework walls
        for (let wall of frameworkWalls) {
            wall.display(colors);
        }

        // Framework label
        if (time > 90) {
            let fAlpha = Math.min((time - 90) * 2, 150);
            p.fill(...colors.accent3, fAlpha);
            p.textAlign(p.CENTER, p.CENTER);
            p.textSize(8);
            p.text("Expert's Framework", 180, 160);
            p.textSize(6);
            p.text("(efficient but constraining)", 180, 172);
        }

        // Update and display
        expertAgent.update();
        expertAgent.display(colors);

        for (let agent of beginnerAgents) {
            agent.update();
            agent.display(colors);
        }

        // Target
        target.display(colors);

        // Annotations
        if (time > 120 && time < 240) {
            let annAlpha = Math.min((time - 120) * 1.5, 140);
            p.textAlign(p.LEFT, p.TOP);
            p.textSize(7);
            p.fill(...colors.accent2, annAlpha);
            p.text("Fast, confident,\noptimized path", 50, 200);

            p.fill(...colors.accent3, annAlpha);
            p.text("Slow, uncertain,\nexploratory wandering", 220, 240);
        }

        // Show the problem
        if (time > 300) {
            let probAlpha = Math.min((time - 300) * 1.5, 170);

            p.textAlign(p.CENTER, p.CENTER);
            p.textSize(8);
            p.fill(...colors.accent2, probAlpha);
            p.text("Expert optimized locally\nbut can't see beyond framework", 180, 200);
        }

        // Show beginner finding solution
        if (time > 420) {
            let winAlpha = Math.min((time - 420) * 2, 180);

            p.textAlign(p.CENTER, p.BOTTOM);
            p.textSize(9);
            p.fill(...colors.accent1, winAlpha);
            p.text("Beginner's 'naive' exploration found the simple solution", 200, 295);
        }

        // Final insight
        if (time > 540) {
            let insightAlpha = Math.min((time - 540) * 2, 160);

            p.push();
            p.textAlign(p.CENTER, p.TOP);
            p.textSize(7);
            p.fill(...colors.accent3, insightAlpha);
            p.text("The framework that makes experts efficient", 200, 45);
            p.text("can blind them to what's outside it", 200, 56);
            p.pop();
        }

        time++;

        // Loop
        if (time > 700) {
            time = 0;
            // Reset agents
            expertAgent = new ExpertAgent(180, 240);
            expertAgent.targetX = 180;
            expertAgent.targetY = 150;

            beginnerAgents = [];
            for (let i = 0; i < 4; i++) {
                beginnerAgents.push(new BeginnerAgent(
                    p.random(80, 300),
                    p.random(160, 260),
                    i
                ));
            }
        }
    };
};