The Self-Awareness Trap
About This Sketch
Two hundred particles flow through a noise field — smoothly, coherently, without conscious direction. A spotlight moves slowly across the canvas. Wherever it passes, the particles it catches become erratic and jittery, their natural motion broken by the act of observation. When the spotlight moves on, they gradually recover their flow.
This is the mechanism behind athletic choking, social anxiety, and writer's block: automated skill running cleanly until conscious observation reinstates the slow, brittle processing that the skill was designed to replace.
Algorithm
A moving spotlight traces a slow Lissajous curve across the canvas. Particles outside the spotlight flow smoothly through a Perlin noise vector field — organized, coherent, automated. Particles caught inside the spotlight transition to rapid, jittery motion: a faster noise frequency plus random perturbation, representing the breakdown of implicit skill under conscious observation. Color shifts from warm amber (natural flow) to burnt orange (disrupted) as each particle's disruption level increases.
This sketch accompanies the blog post "The Self-Awareness Trap" and visualizes how real-time self-monitoring degrades automated performance.
Pseudocode
SETUP:
Initialize 200 particles at random positions
Set spotlight radius to 68px
DRAW (every frame):
Get current theme colors
Fade background slightly (trail effect)
Move spotlight along Lissajous path (cos/sin with different frequencies)
Draw soft glow rings around spotlight
FOR each particle:
Measure distance to spotlight center
Smoothly lerp disruption level (0=natural, 1=observed)
IF disruption < 0.05:
Sample slow Perlin noise field → smooth angle → gentle velocity
ELSE:
Sample fast Perlin noise + random jitter → erratic velocity
Move particle, wrap at canvas edges
Reset age-expired particles
Color = lerp(accent1, accent2, disruption)
Size = 2.2 + disruption * 1.0
Draw particle with life-cycle fade
Source Code
let sketch = function(p) {
// Observation disrupting natural flow
// Particles outside a moving spotlight flow smoothly through a noise field
// Particles caught in the spotlight become erratic — the observed process breaks down
// Representing how real-time self-monitoring degrades automated skill
let particles = [];
let spotlightX, spotlightY;
const SPOTLIGHT_RADIUS = 68;
let time = 0;
p.setup = function() {
p.createCanvas(400, 300);
p.colorMode(p.RGB);
for (let i = 0; i < 200; i++) {
particles.push({
x: p.random(400),
y: p.random(300),
vx: 0,
vy: 0,
disrupted: 0,
age: p.random(220)
});
}
};
p.draw = function() {
const colors = getThemeColors();
p.noStroke();
p.fill(...colors.bg, 38);
p.rect(0, 0, 400, 300);
time += 0.007;
// Spotlight traces a slow Lissajous path
spotlightX = 200 + p.cos(time * 0.63) * 145;
spotlightY = 150 + p.sin(time * 0.41) * 95;
// Soft spotlight glow (rings fading outward)
p.noFill();
for (let r = SPOTLIGHT_RADIUS; r > 0; r -= 6) {
let alpha = p.map(r, 0, SPOTLIGHT_RADIUS, 18, 0);
p.stroke(...colors.accent1, alpha);
p.strokeWeight(1.2);
p.circle(spotlightX, spotlightY, r * 2);
}
// Update and draw particles
for (let pt of particles) {
let d = p.dist(pt.x, pt.y, spotlightX, spotlightY);
let isObserved = d < SPOTLIGHT_RADIUS;
// Smoothly transition disruption level
pt.disrupted = p.lerp(pt.disrupted, isObserved ? 1.0 : 0.0, 0.06);
if (pt.disrupted < 0.05) {
// Natural: smooth Perlin noise flow field
let n = p.noise(pt.x * 0.006 + time * 0.28, pt.y * 0.006 + time * 0.22);
let angle = n * p.TWO_PI * 2.0;
pt.vx = p.lerp(pt.vx, p.cos(angle) * 1.25, 0.07);
pt.vy = p.lerp(pt.vy, p.sin(angle) * 1.25, 0.07);
} else {
// Observed: rapid, jittery noise — self-correction breaking down
let n = p.noise(pt.x * 0.018 + time * 2.2, pt.y * 0.018 + time * 1.9);
let angle = n * p.TWO_PI * 2.0;
let jitter = pt.disrupted * 0.7;
pt.vx = p.lerp(pt.vx, p.cos(angle) * 0.8 + p.random(-jitter, jitter), 0.18);
pt.vy = p.lerp(pt.vy, p.sin(angle) * 0.8 + p.random(-jitter, jitter), 0.18);
}
pt.x += pt.vx;
pt.y += pt.vy;
pt.age++;
// Wrap
if (pt.x < 0) pt.x = 400;
if (pt.x > 400) pt.x = 0;
if (pt.y < 0) pt.y = 300;
if (pt.y > 300) pt.y = 0;
if (pt.age > 240) {
pt.x = p.random(400);
pt.y = p.random(300);
pt.vx = 0; pt.vy = 0;
pt.disrupted = 0;
pt.age = 0;
}
// Color: blend from accent1 (natural) to accent2 (disrupted)
let life = p.min(pt.age / 35, (240 - pt.age) / 35, 1.0);
let r = p.lerp(colors.accent1[0], colors.accent2[0], pt.disrupted);
let g = p.lerp(colors.accent1[1], colors.accent2[1], pt.disrupted);
let b = p.lerp(colors.accent1[2], colors.accent2[2], pt.disrupted);
let alpha = life * p.map(pt.disrupted, 0, 1, 120, 210);
p.noStroke();
p.fill(r, g, b, alpha);
p.circle(pt.x, pt.y, 2.2 + pt.disrupted * 1.0);
}
// Label
p.noStroke();
p.fill(...colors.accent3, 80);
p.textAlign(p.CENTER);
p.textSize(9);
p.text('unobserved flow — and observed', 200, 294);
};
};