The Beginner Advantage
About This Sketch
A maze navigation where expertise follows the long efficient path, while beginner ignorance questions obstacles and finds shortcuts. Watch as removable constraints (dashed borders) get eliminated by the beginner's willingness to ask "why not?"—revealing paths the expert's pattern-matching couldn't see.
Algorithm
This sketch visualizes the key insight of "The Beginner Advantage" through a simple maze navigation metaphor.
Two paths are animated simultaneously through a grid maze:
The expert path (darker orange) navigates efficiently around all obstacles, following well-established patterns and best practices. It takes a longer route because it respects every constraint it encounters.
The beginner path (lighter gold) explores more tentatively but crucially asks: "Why is this obstacle here?" When it encounters removable obstacles (shown with dashed borders—legacy constraints that no longer apply), it questions their validity and removes them, discovering a much shorter route.
The animation loops to show the comparison repeatedly. The removable obstacles fade away when the beginner questions them, revealing how outdated constraints can be eliminated rather than worked around.
This accompanies the blog post exploring how expertise creates pattern blindness, while beginners' ignorance of "best practices" allows them to question fundamental assumptions and find simplified solutions.
Pseudocode
SETUP:
Create 400x300 canvas
Initialize 8x6 grid maze
Place obstacles (mix of real and removable)
Define expert path (long route around all obstacles)
Define beginner path (shorter route that removes obstacles)
DRAW (loops continuously):
Get theme colors for light/dark mode
Draw background
Draw grid structure
FOR each obstacle:
IF removable AND not yet removed:
Display with dashed border (visual hint)
ELSE IF real obstacle:
Display solid
IF currently being removed:
Fade out opacity
Animate both paths step by step:
Show expert path progressing efficiently but long
Show beginner path exploring
WHEN beginner reaches removable obstacle:
Remove it (fade out)
Continue on shorter path
Display legend and status messages
AFTER 150 frames:
Reset animation to loop
Source Code
let sketch = function(p) {
// Visualization: Two paths through a maze
// Expert path: Quick, efficient, but follows well-worn routes around obstacles
// Beginner path: Slower, exploratory, but occasionally finds shortcuts by questioning obstacles
// Some "obstacles" (shown faded) are actually removable - only beginner notices
let mazeWidth = 8;
let mazeHeight = 6;
let cellSize = 45;
let offsetX = 25;
let offsetY = 45;
let expertPath = [];
let beginnerPath = [];
let obstacles = [];
let removableObstacles = [];
let frame = 0;
class PathStep {
constructor(x, y, isExpert) {
this.x = x;
this.y = y;
this.isExpert = isExpert;
this.age = 0;
this.alpha = 0;
}
update() {
this.age++;
this.alpha = p.min(255, this.alpha + 15);
}
display(colors) {
let px = offsetX + this.x * cellSize + cellSize / 2;
let py = offsetY + this.y * cellSize + cellSize / 2;
if (this.isExpert) {
// Expert path - confident, direct, follows patterns
p.fill(...colors.accent2, this.alpha * 0.6);
p.noStroke();
p.circle(px, py, 8);
} else {
// Beginner path - exploratory, questioning
p.fill(...colors.accent1, this.alpha * 0.8);
p.noStroke();
p.circle(px, py, 6);
}
}
}
class Obstacle {
constructor(x, y, isRemovable) {
this.x = x;
this.y = y;
this.isRemovable = isRemovable;
this.removed = false;
this.opacity = isRemovable ? 150 : 220;
}
remove() {
if (this.isRemovable) {
this.removed = true;
}
}
display(colors) {
if (this.removed) {
this.opacity = p.max(0, this.opacity - 5);
if (this.opacity <= 0) return;
}
let px = offsetX + this.x * cellSize;
let py = offsetY + this.y * cellSize;
if (this.isRemovable && !this.removed) {
// Removable constraint (legacy/outdated)
p.stroke(...colors.accent3, this.opacity * 0.5);
p.strokeWeight(2);
p.fill(...colors.accent3, this.opacity * 0.3);
p.rect(px + 5, py + 5, cellSize - 10, cellSize - 10, 3);
// Visual hint it's removable (dashed)
p.noFill();
p.stroke(...colors.accent3, this.opacity * 0.4);
p.setLineDash([3, 3]);
p.rect(px + 3, py + 3, cellSize - 6, cellSize - 6, 3);
p.setLineDash([]);
} else if (!this.isRemovable) {
// Real constraint
p.noStroke();
p.fill(...colors.accent3, this.opacity);
p.rect(px + 5, py + 5, cellSize - 10, cellSize - 10, 3);
}
}
}
p.setup = function() {
p.createCanvas(400, 300);
// Create maze with mix of real and removable obstacles
obstacles = [
new Obstacle(2, 1, false),
new Obstacle(2, 2, false),
new Obstacle(2, 3, false),
new Obstacle(5, 2, true), // Legacy constraint - removable
new Obstacle(5, 3, true), // Legacy constraint - removable
new Obstacle(3, 4, false),
new Obstacle(4, 4, false),
];
removableObstacles = obstacles.filter(o => o.isRemovable);
// Expert path: Goes around all obstacles efficiently
// Follows learned patterns, never questions if obstacles can be removed
let expertRoute = [
[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [0, 5],
[1, 5], [2, 5], [3, 5], [4, 5], [5, 5],
[6, 5], [6, 4], [6, 3], [6, 2], [6, 1],
[7, 1], [7, 0]
];
// Beginner path: Starts exploring, questions the obstacle at 5,2
// Realizes it can be removed (asks "why not?"), takes shortcut
let beginnerRoute = [
[0, 0], [1, 0], [2, 0], [3, 0], [3, 1],
[4, 1], [4, 2], [5, 2], [5, 1], [6, 1], [7, 1], [7, 0]
];
expertPath = expertRoute.map(([x, y]) => new PathStep(x, y, true));
beginnerPath = beginnerRoute.map(([x, y]) => new PathStep(x, y, false));
};
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
frame++;
// Title
p.fill(...colors.accent3, 200);
p.noStroke();
p.textAlign(p.CENTER);
p.textSize(11);
p.text('The Beginner Advantage', 200, 20);
// Draw grid lightly
p.stroke(...colors.accent3, 30);
p.strokeWeight(1);
p.noFill();
for (let x = 0; x < mazeWidth; x++) {
for (let y = 0; y < mazeHeight; y++) {
p.rect(offsetX + x * cellSize, offsetY + y * cellSize, cellSize, cellSize);
}
}
// Start and end markers
p.noStroke();
p.fill(...colors.accent1, 150);
p.textSize(8);
p.textAlign(p.CENTER);
p.text('START', offsetX + cellSize / 2, offsetY + cellSize / 2 + 3);
p.text('END', offsetX + 7 * cellSize + cellSize / 2, offsetY + 3);
// Display obstacles
for (let obstacle of obstacles) {
obstacle.display(colors);
}
// Animate paths appearing
let stepsToShow = Math.floor(frame / 8);
// Expert path
for (let i = 0; i < Math.min(stepsToShow, expertPath.length); i++) {
expertPath[i].update();
expertPath[i].display(colors);
}
// Beginner path
for (let i = 0; i < Math.min(stepsToShow, beginnerPath.length); i++) {
beginnerPath[i].update();
beginnerPath[i].display(colors);
// When beginner reaches removable obstacle, remove it
if (i === 7 && !removableObstacles[0].removed) {
for (let ro of removableObstacles) {
ro.remove();
}
}
}
// Legend
p.textAlign(p.LEFT);
p.textSize(7);
// Expert indicator
p.fill(...colors.accent2, 200);
p.noStroke();
p.circle(30, 270, 6);
p.fill(...colors.accent3, 180);
p.text('Expert: Efficient within known patterns', 40, 273);
// Beginner indicator
p.fill(...colors.accent1, 200);
p.noStroke();
p.circle(250, 270, 6);
p.fill(...colors.accent3, 180);
p.text('Beginner: Questions constraints', 260, 273);
// Status message
p.textAlign(p.CENTER);
p.textSize(7);
p.fill(...colors.accent3, 180);
if (stepsToShow < 8) {
p.text('Both navigate around obstacles...', 200, 287);
} else if (stepsToShow < 12) {
p.text('Beginner asks: "Why is this obstacle here?" and removes it', 200, 287);
} else {
p.text('Beginner finds shorter path by questioning assumptions', 200, 287);
}
// Loop the animation
if (frame > 150) {
frame = 0;
for (let step of expertPath) step.alpha = 0;
for (let step of beginnerPath) step.alpha = 0;
for (let obstacle of removableObstacles) {
obstacle.removed = false;
obstacle.opacity = 150;
}
}
};
};