Learning From Failure
About This Sketch
Two panels showing what failure produces with and without deliberate structure. On the left, particles driven by turbulence with no organizing force — they scatter, bounce, and never converge. On the right, the same initial chaos, but with a growing "pull" toward an orbit that represents accumulated structured analysis. Over time, the right side resolves from disorder into organized circulation. The learning isn't in the failure itself — it's in what you build around it.
Algorithm
Two-panel particle simulation contrasting unstructured and structured
responses to failure. Left panel: particles driven by Perlin noise
turbulence with no organizing force — perpetual disorder. Right panel:
identical initial chaos, but each particle acquires a growing "pull"
toward an orbital path around a central point. The pull strength
(learnFactor) increases over time, representing accumulated structured
analysis. Noise amplitude decreases as structure takes hold. Particle
color warms from muted to accent as convergence progresses.
Pseudocode
SETUP:
Create 16 left particles with random positions and velocities in left half
Create 16 right particles with random positions, velocities, and orbit phases in right half
DRAW EACH FRAME:
learnFactor = min(frameCount * 0.00048, 0.12)
LEFT PANEL (raw failure):
For each particle:
Apply Perlin noise turbulence (constant amplitude)
Cap speed at 1.6
Bounce off left-panel walls
Draw as muted circle
RIGHT PANEL (structured analysis):
Draw faint orbit ring (visibility scales with learnFactor)
For each particle:
Compute orbit target position (rotating around center)
Apply pull toward orbit target (strength = learnFactor * 0.055)
Apply Perlin noise (amplitude decreases as learnFactor grows)
Cap speed at 1.9
Bounce off right-panel walls
Interpolate color from muted to warm based on learnFactor progress
Draw circle
Draw panel labels
Source Code
let sketch = function(p) {
const W = 400, H = 300, MID = W / 2;
const N = 16;
let leftP = [];
let rightP = [];
p.setup = function() {
p.createCanvas(W, H);
p.colorMode(p.RGB);
for (let i = 0; i < N; i++) {
leftP.push({
x: p.random(12, MID - 12),
y: p.random(12, H - 12),
vx: p.random(-1.2, 1.2),
vy: p.random(-1.2, 1.2)
});
rightP.push({
x: p.random(MID + 12, W - 12),
y: p.random(12, H - 12),
vx: p.random(-1.2, 1.2),
vy: p.random(-1.2, 1.2),
phase: (i / N) * p.TWO_PI
});
}
};
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
const t = p.frameCount * 0.013;
const learnFactor = p.min(p.frameCount * 0.00048, 0.12);
p.stroke(...colors.accent3, 35);
p.strokeWeight(1);
p.line(MID, 14, MID, H - 14);
for (let a of leftP) {
a.vx += (p.noise(a.x * 0.025, a.y * 0.025, t) - 0.5) * 0.28;
a.vy += (p.noise(a.x * 0.025 + 60, a.y * 0.025, t) - 0.5) * 0.28;
let spL = p.sqrt(a.vx * a.vx + a.vy * a.vy);
if (spL > 1.6) { a.vx *= 1.6 / spL; a.vy *= 1.6 / spL; }
if (spL < 0.3) { a.vx += p.random(-0.15, 0.15); a.vy += p.random(-0.15, 0.15); }
a.x += a.vx;
a.y += a.vy;
if (a.x < 10) { a.x = 10; a.vx = Math.abs(a.vx); }
if (a.x > MID - 10) { a.x = MID - 10; a.vx = -Math.abs(a.vx); }
if (a.y < 10) { a.y = 10; a.vy = Math.abs(a.vy); }
if (a.y > H - 10) { a.y = H - 10; a.vy = -Math.abs(a.vy); }
p.fill(...colors.accent3, 165);
p.noStroke();
p.circle(a.x, a.y, 7);
}
const gcx = MID + (W - MID) / 2;
const gcy = H / 2;
let ringAlpha = p.map(learnFactor, 0, 0.12, 15, 80);
p.noFill();
p.stroke(...colors.accent1, ringAlpha);
p.strokeWeight(1.2);
p.circle(gcx, gcy, 58);
p.fill(...colors.accent1, ringAlpha * 1.8);
p.noStroke();
p.circle(gcx, gcy, 7);
for (let a of rightP) {
const orbitR = 22 + 6 * p.sin(a.phase * 1.7);
const tx = gcx + orbitR * p.cos(a.phase + t * 1.05);
const ty = gcy + orbitR * p.sin(a.phase + t * 1.05);
a.vx += (tx - a.x) * learnFactor * 0.055;
a.vy += (ty - a.y) * learnFactor * 0.055;
const noiseAmp = 0.22 * (1 - learnFactor / 0.12);
a.vx += (p.noise(a.x * 0.022, a.y * 0.022, t) - 0.5) * noiseAmp;
a.vy += (p.noise(a.x * 0.022 + 60, a.y * 0.022, t) - 0.5) * noiseAmp;
let spR = p.sqrt(a.vx * a.vx + a.vy * a.vy);
if (spR > 1.9) { a.vx *= 1.9 / spR; a.vy *= 1.9 / spR; }
a.x += a.vx;
a.y += a.vy;
if (a.x < MID + 6) { a.x = MID + 6; a.vx = Math.abs(a.vx); }
if (a.x > W - 6) { a.x = W - 6; a.vx = -Math.abs(a.vx); }
if (a.y < 6) { a.y = 6; a.vy = Math.abs(a.vy); }
if (a.y > H - 6) { a.y = H - 6; a.vy = -Math.abs(a.vy); }
const progress = learnFactor / 0.12;
const r = colors.accent3[0] + (colors.accent2[0] - colors.accent3[0]) * progress;
const g = colors.accent3[1] + (colors.accent2[1] - colors.accent3[1]) * progress;
const b = colors.accent3[2] + (colors.accent2[2] - colors.accent3[2]) * progress;
p.fill(r, g, b, 175);
p.noStroke();
p.circle(a.x, a.y, 7);
}
p.noStroke();
p.fill(...colors.accent3, 90);
p.textAlign(p.CENTER);
p.textSize(8.5);
p.text("raw failure", MID / 2, H - 10);
p.text("structured analysis", MID + (W - MID) / 2, H - 10);
};
};