The Growth Mindset Trap
About This Sketch
A visual metaphor for the core thesis of "The Growth Mindset Trap." Watch as matched climbers reach their peaks while the mismatched climber grinds endlessly on the wrong mountain. Growth mindset can't overcome fundamental mismatches between person and pursuit. Sometimes the growth you need comes from accepting what you're not and choosing better-matched mountains.
Algorithm
This sketch visualizes the core argument of "The Growth Mindset Trap" through an animated mountain-climbing metaphor.
Three mountains represent different pursuits. The left two mountains are "good matches" for their climbers - pursuits aligned with their actual aptitudes. The right mountain is a "poor match" - representing someone grinding at a pursuit that doesn't fit their capabilities.
Well-matched climbers steadily ascend their mountains and reach the peaks, where they celebrate with radiating success indicators. The poorly-matched climber on the right mountain climbs slowly, shows visible struggle (wobbling movement), and eventually stalls partway up the mountain. Even after stalling, they continue expending effort (shown through exhaustion indicators like sweat drops and effort lines) but make no progress.
The visualization demonstrates that effort and persistence alone cannot overcome fundamental mismatches between person and pursuit. Growth mindset culture would tell the stalled climber to "believe harder" and "persist more." The sketch shows the reality: they're exhausting themselves on the wrong mountain while the right mountains go unclimbed.
The theme-aware colors ensure the sketch adapts to light and dark modes, with matched climbers shown in warm accent tones (success) and unmatched climbers in cooler tones (struggle).
Pseudocode
CLASSES:
Mountain
- Position, height, width
- Matched/unmatched flag
- Draw as triangle with label
Climber
- Reference to assigned mountain
- Progress (0 to 1)
- Max progress (1.0 if matched, 0.3 if unmatched)
- Climb speed (fast if matched, slow if unmatched)
- Exhaustion level
- Success/stalled state
SETUP:
Initialize canvas (400x300)
Create 3 mountains (2 matched, 1 unmatched)
Create 1 climber per mountain
DRAW (each frame):
Get current theme colors
Clear background
For each mountain:
Draw mountain triangle
Draw match label
For each climber:
UPDATE:
If not at max progress:
Increment progress based on climb speed
Update position on mountain
Accumulate exhaustion (faster if unmatched)
Add wobble if struggling
Else:
If matched: Mark as succeeded
If unmatched: Mark as stalled, continue accumulating exhaustion
DRAW:
If succeeded:
Draw at peak with celebration rays
Else if stalled:
Draw partway up with exhaustion indicators
Show effort lines (grinding but not moving)
Else:
Draw climbing with trail behind
Draw explanatory labels
Source Code
let sketch = function(p) {
let climbers = [];
let mountains = [];
let time = 0;
class Mountain {
constructor(x, difficulty, matched) {
this.x = x;
this.difficulty = difficulty; // How hard to climb
this.matched = matched; // Whether this climber is matched to this mountain
this.height = 250;
this.base = 280;
this.width = 80;
}
draw(colors) {
// Draw mountain as triangle
let brightness = this.matched ? 200 : 120;
p.fill(...colors.accent3, brightness);
p.noStroke();
p.triangle(
this.x - this.width / 2, this.base,
this.x + this.width / 2, this.base,
this.x, this.base - this.height
);
// Draw difficulty indicators
p.fill(...colors.accent2, 150);
p.textAlign(p.CENTER);
p.textSize(8);
let label = this.matched ? "Good Match" : "Poor Match";
p.text(label, this.x, this.base + 15);
}
getPeak() {
return this.base - this.height;
}
}
class Climber {
constructor(mountain) {
this.mountain = mountain;
this.x = mountain.x;
this.y = mountain.base;
this.progress = 0; // 0 to 1
this.maxProgress = mountain.matched ? 1.0 : 0.3; // Matched climbers reach peak, unmatched stall
this.climbSpeed = mountain.matched ? 0.003 : 0.001;
this.exhaustion = 0;
this.gaveUp = false;
this.succeeded = false;
this.stalled = false;
}
update() {
if (this.gaveUp || this.succeeded) return;
// Try to climb
if (this.progress < this.maxProgress) {
this.progress += this.climbSpeed;
this.y = p.lerp(this.mountain.base, this.mountain.getPeak(), this.progress);
// Accumulate exhaustion faster if poorly matched
if (!this.mountain.matched) {
this.exhaustion += 0.02;
}
} else {
// Either reached peak (matched) or stalled (unmatched)
if (this.mountain.matched) {
this.succeeded = true;
} else {
this.stalled = true;
this.exhaustion += 0.01; // Still trying but not moving
}
}
// Add some wobble to show struggle
if (!this.mountain.matched && !this.stalled) {
this.x = this.mountain.x + p.sin(time * 0.1 + this.mountain.x) * 3;
}
}
draw(colors) {
if (this.succeeded) {
// Climber at peak - celebrating
p.fill(...colors.accent1, 255);
p.noStroke();
p.circle(this.x, this.y - 8, 10);
// Success rays
p.stroke(...colors.accent1, 150);
p.strokeWeight(2);
for (let i = 0; i < 6; i++) {
let angle = (i / 6) * p.TWO_PI + time * 0.05;
let x2 = this.x + p.cos(angle) * 15;
let y2 = this.y - 8 + p.sin(angle) * 15;
p.line(this.x, this.y - 8, x2, y2);
}
} else if (this.stalled) {
// Stuck on wrong mountain
p.fill(...colors.accent2, 200);
p.noStroke();
p.circle(this.x, this.y, 8);
// Exhaustion indicators - sweat drops
p.fill(...colors.accent2, 100);
for (let i = 0; i < 3; i++) {
let dropY = this.y + 12 + p.sin(time * 0.08 + i) * 5;
p.circle(this.x - 6 + i * 6, dropY, 3);
}
// Effort lines (grinding but not moving)
p.stroke(...colors.accent2, 100);
p.strokeWeight(1);
for (let i = 0; i < 5; i++) {
let lineY = this.y + 5 + i * 3;
let lineLen = p.sin(time * 0.2 + i) * 4 + 4;
p.line(this.x - lineLen, lineY, this.x + lineLen, lineY);
}
} else {
// Still climbing
let alpha = this.mountain.matched ? 220 : 180;
p.fill(...(this.mountain.matched ? colors.accent1 : colors.accent2), alpha);
p.noStroke();
p.circle(this.x, this.y, 7);
// Trail showing path
p.stroke(...(this.mountain.matched ? colors.accent1 : colors.accent2), 50);
p.strokeWeight(2);
p.line(this.x, this.y, this.mountain.x, this.mountain.base);
}
}
}
p.setup = function() {
p.createCanvas(400, 300);
p.colorMode(p.RGB);
// Create mountains - left side matched, right side unmatched
mountains.push(new Mountain(100, 0.5, true));
mountains.push(new Mountain(200, 0.5, true));
mountains.push(new Mountain(300, 0.5, false));
// Create climbers for each mountain
for (let mountain of mountains) {
climbers.push(new Climber(mountain));
}
};
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
time++;
// Draw mountains
for (let mountain of mountains) {
mountain.draw(colors);
}
// Update and draw climbers
for (let climber of climbers) {
climber.update();
climber.draw(colors);
}
// Draw labels
p.fill(...colors.accent3);
p.noStroke();
p.textAlign(p.LEFT, p.TOP);
p.textSize(9);
p.text("Left: Climbers matched to their mountains (reach peak)", 10, 10);
p.text("Right: Climber grinding on wrong mountain (stalls, exhausts)", 10, 23);
// Draw title
p.textAlign(p.CENTER, p.TOP);
p.textSize(10);
p.fill(...colors.accent2);
p.text("Growth mindset can't make wrong mountains right", p.width / 2, 45);
};
};