Where ideas percolate and thoughts brew

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