All Paths Orbit the Same Origin
About This Sketch
A central node radiates paths that appear to lead forward — but every curve bends back toward the same origin. Particles travel each path, fading in at the source and disappearing at the end, only to begin again. No path escapes the gravitational pull of where it started.
This accompanies the post "Why Most Mentorship Fails," visualizing how advice shaped by a mentor's biography tends to describe their world rather than chart yours.
Algorithm
A central mentor node emits seven bezier curve paths that fan outward
then curve back near their origin — appearing to lead forward but returning
to the mentor's own neighborhood. Small mentee particles travel along each
path, fading in at the source and fading out at the end. The paths never
escape the gravitational pull of the central node; they describe the mentor's
world, not an open future.
Pseudocode
SETUP:
Place mentor node at left-center of canvas
Generate 7 bezier paths fanning from mentor
Each path curves outward then pulls back near origin
Spawn one particle per path at random start positions
DRAW:
Clear background
FOR each path:
Draw faint bezier curve
Draw arrowhead at endpoint
Draw mentor node (glowing center)
FOR each particle:
Advance t along bezier path
Reset to 0 when t > 1
Compute bezier position
Draw particle with sin-based fade (bright in middle, dim at ends)
Draw caption
Source Code
let mentorX, mentorY;
let paths = [];
let numPaths = 7;
let time = 0;
function bezierPoint(x0, cx1, cx2, x3, t) {
let mt = 1 - t;
return mt*mt*mt*x0 + 3*mt*mt*t*cx1 + 3*mt*t*t*cx2 + t*t*t*x3;
}
let sketch = function(p) {
let particles = [];
p.setup = function() {
p.createCanvas(400, 300);
mentorX = p.width * 0.35;
mentorY = p.height * 0.5;
for (let i = 0; i < numPaths; i++) {
let angle = p.map(i, 0, numPaths, -p.PI * 0.6, p.PI * 0.6);
let len = p.random(90, 150);
let cpAngle = angle * 0.5;
let cpLen = len * 0.8;
paths.push({
x0: mentorX, y0: mentorY,
x3: mentorX + p.cos(angle) * len * 0.4 + p.random(20, 60),
y3: mentorY + p.sin(angle) * len * 0.3,
cx1: mentorX + p.cos(angle) * len * 0.6,
cy1: mentorY + p.sin(angle) * len,
cx2: mentorX + p.cos(cpAngle) * cpLen,
cy2: mentorY + p.sin(cpAngle) * cpLen * 0.5,
speed: p.random(0.003, 0.006)
});
}
for (let i = 0; i < numPaths; i++) {
particles.push({ pathIndex: i, t: p.random(1), speed: paths[i].speed });
}
};
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
time++;
for (let path of paths) {
p.noFill();
p.stroke(colors.dim[0], colors.dim[1], colors.dim[2], 100);
p.strokeWeight(1);
p.beginShape();
for (let t = 0; t <= 1; t += 0.02) {
let x = bezierPoint(path.x0, path.cx1, path.cx2, path.x3, t);
let y = bezierPoint(path.y0, path.cy1, path.cy2, path.y3, t);
p.vertex(x, y);
}
p.endShape();
let t1 = 0.97, t2 = 1.0;
let ax = bezierPoint(path.x0, path.cx1, path.cx2, path.x3, t2);
let ay = bezierPoint(path.y0, path.cy1, path.cy2, path.y3, t2);
let bx = bezierPoint(path.x0, path.cx1, path.cx2, path.x3, t1);
let by = bezierPoint(path.y0, path.cy1, path.cy2, path.y3, t1);
let angle = p.atan2(ay - by, ax - bx);
p.stroke(colors.dim[0], colors.dim[1], colors.dim[2], 80);
p.strokeWeight(1);
p.line(ax, ay, ax - 6 * p.cos(angle - 0.4), ay - 6 * p.sin(angle - 0.4));
p.line(ax, ay, ax - 6 * p.cos(angle + 0.4), ay - 6 * p.sin(angle + 0.4));
}
p.noStroke();
p.fill(colors.accent2[0], colors.accent2[1], colors.accent2[2], 50);
p.ellipse(mentorX, mentorY, 32, 32);
p.fill(colors.accent2[0], colors.accent2[1], colors.accent2[2], 200);
p.ellipse(mentorX, mentorY, 14, 14);
for (let particle of particles) {
let path = paths[particle.pathIndex];
particle.t += particle.speed;
if (particle.t > 1.0) particle.t = 0.0;
let x = bezierPoint(path.x0, path.cx1, path.cx2, path.x3, particle.t);
let y = bezierPoint(path.y0, path.cy1, path.cy2, path.y3, particle.t);
let alpha = p.sin(particle.t * p.PI) * 200;
p.noStroke();
p.fill(colors.accent1[0], colors.accent1[1], colors.accent1[2], alpha);
p.ellipse(x, y, 6, 6);
}
p.fill(colors.accent3[0], colors.accent3[1], colors.accent3[2], 140);
p.noStroke();
p.textSize(9);
p.textAlign(p.CENTER);
p.text("all paths orbit the same origin", p.width / 2, p.height - 12);
};
};