The Intelligence Trap
About This Sketch
Ninety particles orbit a fixed central point, pulled inward by gravity while Perlin noise creates the appearance of complex, searching motion. Every path spirals toward the same conclusion. The central dot pulsesβthe predetermined answer around which all the "thinking" organizes itself.
A visualization of motivated reasoning: the feeling of thorough inquiry, the reality of a predetermined destination.
Algorithm
Ninety particles are seeded at random positions around a central fixed point (the attractor).
Each frame, every particle experiences two forces: a gravitational pull toward the attractor
(stronger when further away) and a small turbulence force driven by Perlin noise. The noise
gives motion the appearance of complex, searching exploration. But the attractor always winsβ
every particle spirals inward eventually and is reset to the outer ring to begin again.
The central dot pulses gently, representing the predetermined conclusion that organizes
all the apparently dynamic thinking around it.
This sketch accompanies the blog post "The Intelligence Trap" and visualizes motivated
reasoning: how sophisticated thinking can feel like genuine inquiry while every consideration
gets funneled toward a conclusion that was never seriously in doubt.
Pseudocode
SETUP:
Initialize canvas (400x300)
Place attractor at canvas center
Seed 90 particles at random radii (40β190px) around attractor
Each particle has: position, velocity, Perlin noise offsets, age counter
DRAW (every frame):
Get current theme colors
Apply semi-transparent background overlay (creates motion trails)
Increment global time counter
FOR each particle:
Compute vector from particle to attractor
Apply pull force (stronger at distance, weaker near center)
Apply Perlin noise turbulence (creates complex-seeming motion)
Apply velocity damping (0.96x per frame)
Update position
Decrement age
IF particle reaches center OR age expires:
Reset particle to random point on outer ring
Reset age
Draw particle with theme-appropriate color
(outer ring: accent1, near center: accent2)
Draw pulsing ring and dot at attractor position
Render caption text
Source Code
let sketch = function(p) {
// Motivated reasoning visualization
// Particles swirl dynamically but are all drawn toward a fixed central attractor
// Represents how sophisticated thinking can feel like exploration
// while every path leads back to the same predetermined conclusion
let particles = [];
let attractor;
let time = 0;
p.setup = function() {
p.createCanvas(400, 300);
p.colorMode(p.RGB);
attractor = { x: 200, y: 148 };
for (let i = 0; i < 90; i++) {
let angle = p.random(p.TWO_PI);
let radius = p.random(40, 190);
particles.push({
x: attractor.x + p.cos(angle) * radius,
y: attractor.y + p.sin(angle) * radius,
vx: p.random(-0.8, 0.8),
vy: p.random(-0.8, 0.8),
age: p.random(80, 260),
maxAge: p.random(80, 260),
nx: p.random(1000),
ny: p.random(1000)
});
}
};
p.draw = function() {
const colors = getThemeColors();
// Soft fade for trail effect
p.noStroke();
p.fill(...colors.bg, 28);
p.rect(0, 0, 400, 300);
time += 0.009;
for (let i = 0; i < particles.length; i++) {
let pt = particles[i];
// Pull force toward attractor
let dx = attractor.x - pt.x;
let dy = attractor.y - pt.y;
let dist = p.sqrt(dx * dx + dy * dy) + 0.001;
let pullStrength = p.map(dist, 0, 200, 0.55, 0.04);
pt.vx += (dx / dist) * pullStrength;
pt.vy += (dy / dist) * pullStrength;
// Perlin noise turbulence β "thinking," but still drawn inward
let noiseAngle = p.noise(pt.nx + time * 0.4, pt.ny + time * 0.35) * p.TWO_PI * 2;
pt.vx += p.cos(noiseAngle) * 0.12;
pt.vy += p.sin(noiseAngle) * 0.12;
// Gentle damping
pt.vx *= 0.96;
pt.vy *= 0.96;
pt.x += pt.vx;
pt.y += pt.vy;
pt.age--;
// Reset when too close to center or age runs out
if (dist < 6 || pt.age <= 0) {
let angle = p.random(p.TWO_PI);
let radius = p.random(90, 190);
pt.x = attractor.x + p.cos(angle) * radius;
pt.y = attractor.y + p.sin(angle) * radius;
pt.vx = p.random(-0.6, 0.6);
pt.vy = p.random(-0.6, 0.6);
pt.age = pt.maxAge;
}
// Color: further from center = cooler accent, closer = warmer
let alpha = p.map(pt.age, 0, 40, 0, 200);
if (pt.age > 40) alpha = 200;
if (dist > 80) {
p.fill(...colors.accent1, alpha * 0.85);
} else {
p.fill(...colors.accent2, alpha);
}
p.noStroke();
p.circle(pt.x, pt.y, 2.2);
}
// Draw attractor β the fixed conclusion
let pulse = p.sin(time * 2.5) * 4 + 13;
p.noFill();
p.stroke(...colors.accent2, 130);
p.strokeWeight(1.5);
p.circle(attractor.x, attractor.y, pulse);
p.fill(...colors.accent2, 240);
p.noStroke();
p.circle(attractor.x, attractor.y, 5.5);
// Caption
p.fill(...colors.accent3, 110);
p.noStroke();
p.textAlign(p.CENTER);
p.textSize(9);
p.text('all roads lead to the conclusion you started with', 200, 293);
};
};