Where ideas percolate and thoughts brew

Passion as Privilege

About This Sketch

Visualizing two paths to career success: Left side: "Follow your passion" - starts with heart, but needs safety net (platform) to explore Right side: "Build competence first" - starts with foundation, builds up, passion develops at top

This sketch accompanies the blog post "Passion as Privilege" and visualizes its core concepts through generative art.

Algorithm

Visualizing two paths to career success: Left side: "Follow your passion" - starts with heart, but needs safety net (platform) to explore Right side: "Build competence first" - starts with foundation, builds up, passion develops at top This sketch was originally created as a visual companion to the blog post "Passion as Privilege" 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 two paths to career success:
    // Left side: "Follow your passion" - starts with heart, but needs safety net (platform) to explore
    // Right side: "Build competence first" - starts with foundation, builds up, passion develops at top

    let time = 0;
    let passionPath = [];
    let competencePath = [];

    class PathElement {
        constructor(x, y, size, delay, type) {
            this.x = x;
            this.y = y;
            this.size = size;
            this.delay = delay;
            this.type = type; // 'foundation', 'building', 'passion'
            this.alpha = 0;
        }

        update() {
            if (time > this.delay) {
                this.alpha = p.min(this.alpha + 5, 200);
            }
        }

        display(colors) {
            if (this.alpha === 0) return;

            p.noStroke();

            if (this.type === 'foundation') {
                // Solid base - represents stability/resources
                p.fill(...colors.accent3, this.alpha);
                p.rect(this.x - this.size/2, this.y - this.size/2, this.size, this.size, 2);
            } else if (this.type === 'building') {
                // Building blocks - represents competence
                p.fill(...colors.accent1, this.alpha);
                p.rect(this.x - this.size/2, this.y - this.size/2, this.size, this.size, 2);
            } else if (this.type === 'passion') {
                // Heart shape - represents passion
                p.fill(...colors.accent2, this.alpha);
                this.drawHeart(this.x, this.y, this.size);
            } else if (this.type === 'falling') {
                // Falling/unstable - represents pursuing passion without foundation
                p.fill(...colors.accent2, this.alpha * 0.6);
                this.drawHeart(this.x, this.y, this.size);
                // Draw motion lines
                p.stroke(...colors.accent2, this.alpha * 0.3);
                p.strokeWeight(1);
                p.line(this.x - 5, this.y - 15, this.x - 8, this.y - 20);
                p.line(this.x + 5, this.y - 15, this.x + 8, this.y - 20);
            } else if (this.type === 'gap') {
                // Gap/missing foundation - dotted line
                p.stroke(...colors.accent3, this.alpha * 0.4);
                p.strokeWeight(2);
                p.drawingContext.setLineDash([5, 5]);
                p.line(this.x - this.size/2, this.y, this.x + this.size/2, this.y);
                p.drawingContext.setLineDash([]);
            }
        }

        drawHeart(x, y, size) {
            p.push();
            p.translate(x, y);
            p.scale(size / 20);
            p.beginShape();
            for (let a = 0; a < p.TWO_PI; a += 0.1) {
                let r = p.sin(a) * p.sqrt(p.abs(p.cos(a))) / (p.sin(a) + 7/5) - 2 * p.sin(a) + 2;
                let px = r * p.cos(a);
                let py = -r * p.sin(a);
                p.vertex(px * 3, py * 3);
            }
            p.endShape(p.CLOSE);
            p.pop();
        }
    }

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

        // Left side: "Follow your passion" path - starts with passion, needs foundation
        // Heart at top (passion)
        passionPath.push(new PathElement(100, 60, 20, 20, 'passion'));

        // Falling indicator - passion without foundation
        for (let i = 0; i < 3; i++) {
            passionPath.push(new PathElement(100, 100 + i * 30, 16 - i * 2, 50 + i * 20, 'falling'));
        }

        // Gap - missing foundation
        passionPath.push(new PathElement(100, 200, 40, 110, 'gap'));

        // Foundation that passion needs (appears late, bottom)
        for (let i = 0; i < 3; i++) {
            passionPath.push(new PathElement(70 + i * 30, 235, 25, 150 + i * 10, 'foundation'));
        }

        // Text indicator
        passionPath.push({
            display: function(colors) {
                if (time > 180) {
                    p.noStroke();
                    p.fill(...colors.accent2, p.min((time - 180) * 3, 180));
                    p.textSize(8);
                    p.textAlign(p.CENTER);
                    p.text("Needs safety net", 100, 260);
                }
            },
            update: function() {}
        });

        // Right side: "Build competence first" path - foundation first, passion develops
        // Foundation at bottom
        for (let i = 0; i < 3; i++) {
            competencePath.push(new PathElement(270 + i * 30, 235, 25, 20 + i * 10, 'foundation'));
        }

        // Building competence - stacking blocks
        let buildingLayers = [
            [300, 200], // bottom layer
            [285, 175], [315, 175], // second layer
            [300, 150], // third layer
            [285, 125], [315, 125], // fourth layer
            [300, 100] // fifth layer
        ];

        for (let i = 0; i < buildingLayers.length; i++) {
            let [x, y] = buildingLayers[i];
            competencePath.push(new PathElement(x, y, 22, 70 + i * 15, 'building'));
        }

        // Passion at top (develops last)
        competencePath.push(new PathElement(300, 60, 20, 200, 'passion'));

        // Text indicator
        competencePath.push({
            display: function(colors) {
                if (time > 220) {
                    p.noStroke();
                    p.fill(...colors.accent1, p.min((time - 220) * 3, 180));
                    p.textSize(8);
                    p.textAlign(p.CENTER);
                    p.text("Passion from mastery", 300, 260);
                }
            },
            update: function() {}
        });
    };

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

        // Dividing line
        p.stroke(...colors.accent3, 60);
        p.strokeWeight(1);
        p.line(200, 0, 200, 300);

        // Labels at top
        p.noStroke();
        p.fill(...colors.accent3, 200);
        p.textAlign(p.CENTER, p.TOP);
        p.textSize(11);
        p.text("Follow Passion", 100, 15);
        p.text("Build Competence", 300, 15);

        // Descriptions
        p.textSize(8);
        p.fill(...colors.accent3, 140);
        p.text("(requires privilege)", 100, 32);
        p.text("(works for most)", 300, 32);

        // Update and display both paths
        for (let element of passionPath) {
            element.update();
            element.display(colors);
        }

        for (let element of competencePath) {
            element.update();
            element.display(colors);
        }

        // Bottom message
        if (time > 250) {
            p.textAlign(p.CENTER, p.BOTTOM);
            p.textSize(9);
            p.fill(...colors.accent3, p.min((time - 250) * 2, 160));
            p.text("Passion is often the result of success, not its prerequisite", 200, 290);
        }

        time++;

        // Loop animation
        if (time > 350) {
            time = 0;
            for (let element of passionPath) {
                if (element.alpha !== undefined) element.alpha = 0;
            }
            for (let element of competencePath) {
                if (element.alpha !== undefined) element.alpha = 0;
            }
        }
    };
};