The Praise Trap
About This Sketch
Twelve potential paths radiate from a center point. One path is repeatedly reinforced by praise events — traveling flashes that strengthen it and dim the others. Over time, the praised path brightens while unchosen paths fade toward invisibility. A particle moves along only the reinforced trajectory. The paths not taken are still there, barely, if you look.
Algorithm
Twelve potential paths radiate from a central origin point. A single path
is designated as the "praised path." At regular intervals, a praise event
fires along that path — a traveling flash of light — and reinforces its
strength while slightly dimming all other paths. Transient flashes
occasionally appear on other paths but fade quickly. A particle oscillates
along the praised path. Path strength values are tracked and displayed.
Pseudocode
SETUP:
Create 12 paths at evenly-spaced angles from center
Assign equal initial strength to all paths
Mark one path as the reinforced (praised) path
DRAW each frame:
Increment praise timer
If timer threshold reached:
Fire praise event: traveling flash along praised path
Increase praised path strength
Decrease all other path strengths slightly
Reset timer
Occasionally fire transient flash on random non-praised path
For each path:
Draw line from center with alpha proportional to strength
Add glow if strength is high
Draw arrowhead at tip
For each active praise event:
Advance flash along path
Draw glowing circle at current position
Remove when complete
Draw particle oscillating along praised path
Display strength readout
Draw caption
Source Code
let sketch = function(p) {
const N_PATHS = 12;
let pathAngles = [];
let pathStrengths = [];
let praiseIndex = 2;
let particleT = 0;
let praiseEvents = [];
let praiseTimer = 0;
let phase = 0;
function pathEnd(idx, len) {
let angle = pathAngles[idx];
return {
x: 200 + Math.cos(angle) * len,
y: 150 + Math.sin(angle) * len
};
}
p.setup = function() {
p.createCanvas(400, 300);
p.randomSeed(7);
for (let i = 0; i < N_PATHS; i++) {
pathAngles.push((i / N_PATHS) * p.TWO_PI);
pathStrengths.push(i === praiseIndex ? 0.22 : 0.18);
}
};
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
phase += 0.008;
praiseTimer++;
if (praiseTimer > 90) {
praiseEvents.push({ age: 0, pathIdx: praiseIndex });
pathStrengths[praiseIndex] = Math.min(1.0, pathStrengths[praiseIndex] + 0.09);
for (let i = 0; i < N_PATHS; i++) {
if (i !== praiseIndex) {
pathStrengths[i] = Math.max(0.02, pathStrengths[i] - 0.012);
}
}
praiseTimer = 0;
}
if (p.frameCount % 210 === 0) {
let rnd = Math.floor(p.random(N_PATHS));
if (rnd !== praiseIndex) {
praiseEvents.push({ age: 0, pathIdx: rnd, transient: true });
}
}
for (let i = 0; i < N_PATHS; i++) {
let s = pathStrengths[i];
let end = pathEnd(i, 118);
if (s > 0.3) {
p.stroke(...colors.accent1, s * 22);
p.strokeWeight(6 * s);
p.noFill();
p.line(200, 150, end.x, end.y);
}
let alpha = s * 200;
let col = i === praiseIndex ? colors.accent2 : colors.accent3;
p.stroke(...col, alpha);
p.strokeWeight(i === praiseIndex ? 1.8 * s + 0.4 : 0.8 * s + 0.2);
p.noFill();
p.line(200, 150, end.x, end.y);
if (s > 0.08) {
let tipA = Math.atan2(end.y - 150, end.x - 200);
p.fill(...col, alpha * 0.9);
p.noStroke();
p.push();
p.translate(end.x, end.y);
p.rotate(tipA);
p.triangle(0, 0, -5, -2.5, -5, 2.5);
p.pop();
}
}
for (let i = praiseEvents.length - 1; i >= 0; i--) {
let ev = praiseEvents[i];
ev.age++;
let t = ev.age / 55;
if (t > 1) { praiseEvents.splice(i, 1); continue; }
let end = pathEnd(ev.pathIdx, 118 * t);
let a = ev.transient ? 120 : 220;
let fade = ev.transient ? (1 - t) * a : (1 - Math.abs(t - 0.5) * 2) * a;
let col = ev.transient ? colors.accent3 : colors.accent1;
p.noStroke();
p.fill(...col, fade);
p.circle(end.x, end.y, ev.transient ? 5 : 8);
if (!ev.transient) {
p.fill(...col, fade * 0.4);
p.circle(end.x, end.y, 16);
}
}
particleT = (Math.sin(phase) * 0.5 + 0.5);
let pEnd = pathEnd(praiseIndex, 118 * particleT);
p.noStroke();
p.fill(...colors.accent2, 55);
p.circle(pEnd.x, pEnd.y, 18);
p.fill(...colors.accent2, 200);
p.circle(pEnd.x, pEnd.y, 8);
p.fill(...colors.accent2, 255);
p.circle(pEnd.x, pEnd.y, 4);
p.noStroke();
p.fill(...colors.accent3, 100);
p.circle(200, 150, 10);
p.fill(...colors.accent3, 200);
p.circle(200, 150, 5);
let otherAvg = 0, otherCount = 0;
for (let i = 0; i < N_PATHS; i++) {
if (i !== praiseIndex) { otherAvg += pathStrengths[i]; otherCount++; }
}
otherAvg = otherAvg / otherCount;
p.noStroke();
p.textAlign(p.LEFT);
p.textSize(8);
p.fill(...colors.accent2, 130);
p.text('reinforced path: ' + Math.round(pathStrengths[praiseIndex] * 100) + '%', 10, 14);
p.fill(...colors.accent3, 100);
p.text('other paths avg: ' + Math.round(otherAvg * 100) + '%', 10, 26);
p.noStroke();
p.fill(...colors.accent3, 55);
p.textAlign(p.CENTER);
p.textSize(8);
p.text('praised paths persist \u00b7 unpraised paths fade', 200, 295);
};
};