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