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