Where ideas percolate and thoughts brew

The Compound Interest Delusion

About This Sketch

Visualizing knowledge decay vs. compound interest fantasy Left side: The compound interest fantasy (smooth exponential growth) Right side: Reality (growth with constant decay, looks more like maintenance)

This sketch accompanies the blog post "The Compound Interest Delusion" and visualizes its core concepts through generative art.

Algorithm

Visualizing knowledge decay vs. compound interest fantasy Left side: The compound interest fantasy (smooth exponential growth) Right side: Reality (growth with constant decay, looks more like maintenance) This sketch was originally created as a visual companion to the blog post "The Compound Interest Delusion" 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 knowledge decay vs. compound interest fantasy
    // Left side: The compound interest fantasy (smooth exponential growth)
    // Right side: Reality (growth with constant decay, looks more like maintenance)

    let time = 0;
    let fantasyPoints = [];
    let realityPoints = [];
    let learningEvents = [];

    class LearningEvent {
        constructor(time) {
            this.time = time;
            this.strength = 1.0;
            this.decayRate = 0.015; // Skills decay over time
        }

        update() {
            this.strength = Math.max(0, this.strength - this.decayRate);
        }

        getValue() {
            return this.strength;
        }
    }

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

        // Initialize with some learning events
        for (let i = 0; i < 8; i++) {
            learningEvents.push(new LearningEvent(i * 40));
        }
    };

    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("Knowledge Accumulation: Fantasy vs. Reality", 200, 15);

        // Two panels
        let leftX = 90;
        let rightX = 290;
        let graphY = 80;
        let graphHeight = 140;

        // Labels
        p.textSize(9);
        p.fill(...colors.accent1, 200);
        p.text("The Fantasy", leftX, 45);
        p.textSize(7);
        p.fill(...colors.accent3, 150);
        p.text("(compound interest model)", leftX, 58);

        p.textSize(9);
        p.fill(...colors.accent2, 200);
        p.text("The Reality", rightX, 45);
        p.textSize(7);
        p.fill(...colors.accent3, 150);
        p.text("(with decay)", rightX, 58);

        // Left side: Fantasy compound interest curve
        p.stroke(...colors.accent1);
        p.strokeWeight(2);
        p.noFill();
        p.beginShape();
        for (let i = 0; i < 100; i++) {
            let x = leftX - 60 + i * 1.2;
            // Exponential growth: 1.01^i
            let y = graphY + graphHeight - (Math.pow(1.025, i) - 1) * 8;
            y = Math.max(graphY, y);
            p.vertex(x, y);
        }
        p.endShape();

        // Add "learning events" markers to fantasy side
        for (let i = 0; i < 8; i++) {
            let markerX = leftX - 60 + i * 15;
            let markerY = graphY + graphHeight;
            p.stroke(...colors.accent1, 150);
            p.strokeWeight(1);
            p.line(markerX, markerY, markerX, markerY - 10);
            p.noStroke();
            p.fill(...colors.accent1, 120);
            p.circle(markerX, markerY - 12, 4);
        }

        // Right side: Reality with decay
        // Update all learning events
        for (let event of learningEvents) {
            event.update();
        }

        // Add new learning event periodically
        if (time % 40 === 0 && learningEvents.length < 20) {
            learningEvents.push(new LearningEvent(time));
        }

        // Calculate total value (sum of all non-decayed learning)
        let totalValue = 0;
        for (let event of learningEvents) {
            totalValue += event.getValue();
        }

        // Store this value for graphing
        realityPoints.push(totalValue);
        if (realityPoints.length > 120) {
            realityPoints.shift();
        }

        // Draw reality curve
        p.stroke(...colors.accent2);
        p.strokeWeight(2);
        p.noFill();
        p.beginShape();
        for (let i = 0; i < realityPoints.length; i++) {
            let x = rightX - 60 + i * 1.0;
            let y = graphY + graphHeight - realityPoints[i] * 25;
            p.vertex(x, y);
        }
        p.endShape();

        // Show active learning events on reality side
        let activeEvents = learningEvents.filter(e => e.strength > 0.1);
        for (let i = 0; i < activeEvents.length; i++) {
            let event = activeEvents[i];
            let markerX = rightX - 60 + (time - event.time) * 1.0;
            if (markerX >= rightX - 60 && markerX <= rightX + 60) {
                let markerY = graphY + graphHeight;

                // Bar showing decay
                let barHeight = event.strength * 30;
                p.noStroke();
                p.fill(...colors.accent2, event.strength * 150);
                p.rect(markerX - 2, markerY - barHeight, 4, barHeight);

                // Marker at bottom
                p.fill(...colors.accent2, event.strength * 200);
                p.circle(markerX, markerY - barHeight, 4);
            }
        }

        // Axes and grid for both
        p.stroke(...colors.accent3, 60);
        p.strokeWeight(1);

        // Left axis
        p.line(leftX - 60, graphY, leftX - 60, graphY + graphHeight);
        p.line(leftX - 60, graphY + graphHeight, leftX + 60, graphY + graphHeight);

        // Right axis
        p.line(rightX - 60, graphY, rightX - 60, graphY + graphHeight);
        p.line(rightX - 60, graphY + graphHeight, rightX + 60, graphY + graphHeight);

        // Axis labels
        p.noStroke();
        p.fill(...colors.accent3, 120);
        p.textSize(7);
        p.textAlign(p.CENTER, p.TOP);
        p.text("Time", leftX, graphY + graphHeight + 5);
        p.text("Time", rightX, graphY + graphHeight + 5);

        p.push();
        p.translate(leftX - 70, graphY + 70);
        p.rotate(-p.PI / 2);
        p.text("Value", 0, 0);
        p.pop();

        p.push();
        p.translate(rightX - 70, graphY + 70);
        p.rotate(-p.PI / 2);
        p.text("Value", 0, 0);
        p.pop();

        // Annotations
        if (time > 60) {
            let annotAlpha = Math.min((time - 60) * 2, 150);

            p.textAlign(p.CENTER, p.TOP);
            p.textSize(7);
            p.fill(...colors.accent1, annotAlpha);
            p.text("Smooth exponential\ngrowth", leftX, graphY - 8);

            p.fill(...colors.accent2, annotAlpha);
            p.text("Constant fight\nagainst decay", rightX, graphY - 8);
        }

        // Bottom insights
        if (time > 180) {
            let insightAlpha = Math.min((time - 180) * 1.5, 170);

            p.textAlign(p.CENTER, p.BOTTOM);
            p.textSize(9);
            p.fill(...colors.accent3, insightAlpha);
            p.text("The compound interest model ignores maintenance costs", 200, 260);

            p.textSize(8);
            p.fill(...colors.accent2, insightAlpha);
            p.text("Knowledge decays unless actively maintained", 200, 275);
        }

        // Final message
        if (time > 320) {
            let msgAlpha = Math.min((time - 320) * 2, 160);

            p.textAlign(p.CENTER, p.BOTTOM);
            p.textSize(8);
            p.fill(...colors.accent1, msgAlpha);
            p.text("You're not accumulating—you're spinning plates", 200, 293);
        }

        time++;

        // Loop animation
        if (time > 500) {
            time = 0;
            realityPoints = [];
            learningEvents = [];
            for (let i = 0; i < 8; i++) {
                learningEvents.push(new LearningEvent(i * 40));
            }
        }
    };
};