The Failure Fetish
About This Sketch
Visualizing two paths: the failure path (chaotic, expensive) vs. the learning path (efficient, controlled) Left side: "Learning through failure" - chaotic bouncing ball that crashes repeatedly Right side: "Learning through preparation" - smooth, careful path that navigates efficiently
This sketch accompanies the blog post "The Failure Fetish" and visualizes its core concepts through generative art.
Algorithm
Visualizing two paths: the failure path (chaotic, expensive) vs. the learning path (efficient, controlled) Left side: "Learning through failure" - chaotic bouncing ball that crashes repeatedly Right side: "Learning through preparation" - smooth, careful path that navigates efficiently
This sketch was originally created as a visual companion to the blog post "The Failure Fetish" 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: the failure path (chaotic, expensive) vs. the learning path (efficient, controlled)
// Left side: "Learning through failure" - chaotic bouncing ball that crashes repeatedly
// Right side: "Learning through preparation" - smooth, careful path that navigates efficiently
let failureBall;
let learningPath = [];
let time = 0;
let crashes = [];
class FailureBall {
constructor() {
this.reset();
}
reset() {
this.x = 50;
this.y = 50;
this.vx = p.random(2, 4);
this.vy = p.random(-2, 2);
this.radius = 8;
this.crashed = false;
this.crashTime = 0;
}
update() {
if (this.crashed) {
// After crash, pause briefly then reset
if (time - this.crashTime > 60) {
this.reset();
}
return;
}
// Move with unpredictable velocity (represents lack of planning)
this.vx += p.random(-0.3, 0.3);
this.vy += p.random(-0.3, 0.3);
// Keep speed in range
this.vx = p.constrain(this.vx, 1, 4);
this.x += this.vx;
this.y += this.vy;
// Crash at boundaries or obstacles (expensive failures)
if (this.x < 20 || this.x > 180 || this.y < 40 || this.y > 260) {
this.crash();
}
// Random obstacles representing predictable problems
if ((this.x > 80 && this.x < 100 && this.y > 120 && this.y < 140) ||
(this.x > 60 && this.x < 80 && this.y > 180 && this.y < 200)) {
this.crash();
}
}
crash() {
this.crashed = true;
this.crashTime = time;
crashes.push({x: this.x, y: this.y, time: time});
// Keep only recent crashes
crashes = crashes.filter(c => time - c.time < 120);
}
display(colors) {
if (this.crashed) {
// Show crash as expanding X
let crashAge = time - this.crashTime;
let alpha = p.map(crashAge, 0, 60, 255, 0);
let size = p.map(crashAge, 0, 60, 5, 20);
p.stroke(...colors.accent2, alpha);
p.strokeWeight(2);
p.line(this.x - size, this.y - size, this.x + size, this.y + size);
p.line(this.x - size, this.y + size, this.x + size, this.y - size);
} else {
// Draw moving ball
p.noStroke();
p.fill(...colors.accent2, 200);
p.ellipse(this.x, this.y, this.radius * 2);
// Trail showing chaotic path
p.fill(...colors.accent2, 80);
p.ellipse(this.x - this.vx * 2, this.y - this.vy * 2, this.radius);
}
}
}
class LearningPoint {
constructor(x, y, size) {
this.x = x;
this.y = y;
this.targetX = x;
this.targetY = y;
this.size = size;
this.age = 0;
}
update() {
// Smooth, planned movement
this.x += (this.targetX - this.x) * 0.1;
this.y += (this.targetY - this.y) * 0.1;
this.age++;
}
display(colors) {
let alpha = p.min(this.age * 5, 180);
p.noStroke();
p.fill(...colors.accent1, alpha);
p.ellipse(this.x, this.y, this.size);
}
}
p.setup = function() {
p.createCanvas(400, 300);
p.colorMode(p.RGB);
failureBall = new FailureBall();
// Create smooth learning path (right side) - efficient, navigates around problems
let pathPoints = [
{x: 250, y: 50},
{x: 280, y: 80}, // Careful navigation
{x: 320, y: 90},
{x: 340, y: 120},
{x: 330, y: 150}, // Deliberate path around obstacles
{x: 310, y: 180},
{x: 340, y: 210},
{x: 360, y: 240},
{x: 370, y: 270}
];
for (let point of pathPoints) {
learningPath.push(new LearningPoint(point.x, point.y, 6));
}
};
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
p.noStroke();
p.fill(...colors.accent3, 200);
p.textAlign(p.CENTER, p.TOP);
p.textSize(10);
p.text("Learning Through", 100, 10);
p.text("Failure", 100, 22);
p.text("Learning Through", 300, 10);
p.text("Preparation", 300, 22);
// Descriptions
p.textSize(8);
p.fill(...colors.accent3, 140);
p.text("Chaotic, expensive,", 100, 270);
p.text("repeat mistakes", 100, 280);
p.text("Efficient, deliberate,", 300, 270);
p.text("avoid problems", 300, 280);
// Draw boundaries for failure side
p.noFill();
p.stroke(...colors.accent3, 80);
p.strokeWeight(1);
p.rect(20, 40, 160, 220);
// Draw obstacles (predictable problems that should be avoided)
p.fill(...colors.accent3, 60);
p.noStroke();
p.rect(80, 120, 20, 20);
p.rect(60, 180, 20, 20);
// Draw past crashes with fading markers
for (let crash of crashes) {
let age = time - crash.time;
let alpha = p.map(age, 0, 120, 100, 0);
let size = 4;
p.fill(...colors.accent2, alpha);
p.noStroke();
p.ellipse(crash.x, crash.y, size);
}
// Update and display failure path
failureBall.update();
failureBall.display(colors);
// Update and display learning path
for (let i = 0; i < learningPath.length; i++) {
let point = learningPath[i];
// Stagger appearance
if (time > i * 15) {
point.update();
point.display(colors);
// Connect to previous point with smooth line
if (i > 0 && time > (i - 1) * 15) {
let prev = learningPath[i - 1];
p.stroke(...colors.accent1, 120);
p.strokeWeight(1.5);
p.line(prev.x, prev.y, point.x, point.y);
}
}
}
// Crash counter
p.textAlign(p.LEFT, p.BOTTOM);
p.textSize(9);
p.fill(...colors.accent2, 180);
p.noStroke();
let recentCrashes = crashes.filter(c => time - c.time < 120).length;
if (recentCrashes > 0) {
p.text("Failures: " + recentCrashes, 25, 260);
}
// Success indicator for learning path
if (time > learningPath.length * 15 + 30) {
p.textAlign(p.RIGHT, p.BOTTOM);
p.fill(...colors.accent1, 180);
p.text("Goal reached", 375, 260);
}
time++;
// Loop after learning path completes and failure ball has crashed a few times
if (time > 500) {
time = 0;
crashes = [];
failureBall.reset();
learningPath = [];
p.setup(); // Reset learning path
}
};
};