The Plateau
About This Sketch
Most particles stop in the plateau zone. A few keep going and reach the destination — then loop back to begin again.
A visualization of the learning curve: early progress is fast, then a long flat stretch where nothing seems to be working, then acceleration once the threshold is crossed. The ones who quit in the middle never find out how close they were.
Algorithm
Eight particles travel horizontal tracks from left to right. The middle zone
(plateau) dramatically slows all particles and introduces vertical drift. Five
particles (quitters) stop and gray out in the plateau zone; three persisters
continue through, accelerating after the plateau and looping back to the start
when they reach the glowing destination on the right.
Pseudocode
SETUP:
Create 8 particles with staggered start positions
Mark bottom 3 as persisters, top 5 as quitters
Assign each quitter a random stop position within the plateau
DRAW:
Render plateau zone (subtle fill, label)
Render pulsing destination glow on right
For each particle:
If stopped: draw as faded gray dot, skip
Calculate speed multiplier based on zone:
Before plateau: normal speed
In plateau: 7% of normal speed + y jitter
After plateau: 170% of normal speed
If quitter and past quitX: mark stopped
Advance x position
If x > right edge and persister: reset to start
Draw with accent2 (persister) or accent1 (quitter)
Source Code
let sketch = function(p) {
let particles = [];
p.setup = function() {
p.createCanvas(400, 300);
for (let i = 0; i < 8; i++) {
particles.push(makeParticle(i));
}
};
function makeParticle(i) {
let persists = i >= 5;
return {
idx: i,
x: p.random(10, 35),
y: 28 + i * 32,
baseY: 28 + i * 32,
speed: p.random(0.5, 0.9),
persists: persists,
quitX: persists ? 9999 : p.random(175, 235),
stopped: false
};
}
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
p.noStroke();
p.fill(...colors.accent3, 18);
p.rect(155, 8, 95, 282);
p.fill(...colors.accent3, 55);
p.textAlign(p.CENTER);
p.textSize(8);
p.text("plateau", 202, 298);
let pulse = 0.5 + 0.5 * Math.sin(p.frameCount * 0.05);
p.noStroke();
p.fill(...colors.accent2, 18 + 12 * pulse);
p.ellipse(368, 150, 80, 80);
p.fill(...colors.accent2, 40 + 20 * pulse);
p.ellipse(368, 150, 40, 40);
p.fill(...colors.accent2, 130);
p.ellipse(368, 150, 14, 14);
for (let pt of particles) {
if (pt.stopped) {
p.fill(...colors.accent3, 65);
p.noStroke();
p.ellipse(pt.x, pt.y, 7, 7);
continue;
}
let inPlateau = pt.x >= 155 && pt.x < 250;
let afterPlateau = pt.x >= 250;
let spd = pt.speed;
if (inPlateau) spd *= 0.07;
else if (afterPlateau) spd *= 1.7;
if (!pt.persists && pt.x >= pt.quitX) {
pt.stopped = true;
continue;
}
pt.x += spd;
if (inPlateau) {
pt.y += p.random(-0.5, 0.5);
pt.y = Math.max(pt.baseY - 8, Math.min(pt.baseY + 8, pt.y));
}
if (pt.x > 382) {
pt.x = p.random(10, 35);
pt.y = pt.baseY;
}
let col = pt.persists ? colors.accent2 : colors.accent1;
p.fill(...col, 200);
p.noStroke();
p.ellipse(pt.x, pt.y, 8, 8);
}
};
};