Memory Reconstruction
About This Sketch
A visualization of memory reconstruction: each time we recall an event, we create a slightly altered copy. The central dot is the original experience; the constellation of fading, drifting points around it represents all subsequent reconstructions. Older memories drift further from the source, fade in color, and shrinkβwhile still remaining connected by gossamer threads to the event that spawned them.
Accompanies the post "Your Memory Is Lying to You," which explores how memory reconstruction undermines the self-knowledge we build from experience.
Algorithm
A central "original event" dot sits at the canvas center. Radiating outward are "memory copies" β each spawned with a random position offset from the original and an age value. As time progresses, new copies are spawned and all existing ones age. Older memories drift more, fade in opacity, and shift from warm accent color toward a cooler muted tone. Faint lines connect each memory back to the origin, growing more transparent with age. Every 90 frames, a new recall event spawns another memory and ages all existing ones slightly, simulating how each retrieval creates an edited copy.
Pseudocode
SETUP:
Place originalDot at canvas center (200, 150)
Seed 12 initial memories at varying ages (0 to 6)
SPAWN MEMORY (age):
Pick random angle and radius from center
Add age-based positional drift
Store position, drift parameters, and age
DRAW (every frame):
Get theme colors
Clear background
Increment spawnTimer
Every 90 frames (recall event):
Spawn a new memory at random age
Age all existing memories slightly
Increase each memory's drift radius
For each memory:
Draw faint line back to original (alpha fades with age)
For each memory:
Update position using sinusoidal drift over time
Interpolate color from accent1 (fresh) to accent3 (old)
Draw circle with size and alpha mapped to age
Draw subtle ring for mid-aged memories
Draw original event dot (bright, stable)
Draw labels
Source Code
let sketch = function(p) {
let originalDot;
let memories = [];
let time = 0;
let spawnTimer = 0;
p.setup = function() {
p.createCanvas(400, 300);
p.colorMode(p.RGB);
originalDot = { x: 200, y: 150 };
for (let i = 0; i < 12; i++) {
spawnMemory(i * 0.5);
}
};
function spawnMemory(age) {
let angle = p.random(p.TWO_PI);
let radius = p.random(20, 120);
let drift = age * 4;
let mx = originalDot.x + p.cos(angle) * radius + p.random(-drift, drift);
let my = originalDot.y + p.sin(angle) * radius * 0.6 + p.random(-drift, drift);
mx = p.constrain(mx, 20, 380);
my = p.constrain(my, 20, 280);
memories.push({
x: mx,
y: my,
originX: mx,
originY: my,
age: age,
size: p.map(age, 0, 8, 8, 3),
driftAngle: p.random(p.TWO_PI),
driftSpeed: p.random(0.003, 0.008),
driftRadius: p.random(2, 6 + age),
connectionAlpha: p.map(age, 0, 8, 180, 20)
});
}
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
time += 0.016;
spawnTimer += 1;
if (spawnTimer > 90) {
spawnTimer = 0;
if (memories.length < 28) {
spawnMemory(p.random(0.5, 7));
}
for (let m of memories) {
m.age = Math.min(m.age + 0.3, 8);
m.connectionAlpha = p.map(m.age, 0, 8, 180, 20);
m.size = p.map(m.age, 0, 8, 8, 3);
m.driftRadius = p.min(m.driftRadius + 0.2, 14);
}
}
for (let m of memories) {
let alpha = m.connectionAlpha * 0.4;
p.stroke(...colors.accent3, alpha);
p.strokeWeight(0.5);
p.line(originalDot.x, originalDot.y, m.x, m.y);
}
for (let m of memories) {
m.x = m.originX + p.cos(m.driftAngle + time * m.driftSpeed * 60) * m.driftRadius;
m.y = m.originY + p.sin(m.driftAngle + time * m.driftSpeed * 60) * m.driftRadius * 0.5;
let r1 = colors.accent1[0], g1 = colors.accent1[1], b1 = colors.accent1[2];
let r3 = colors.accent3[0], g3 = colors.accent3[1], b3 = colors.accent3[2];
let t = p.map(m.age, 0, 8, 0, 1);
let r = p.lerp(r1, r3, t);
let g = p.lerp(g1, g3, t);
let b = p.lerp(b1, b3, t);
let alpha = p.map(m.age, 0, 8, 220, 80);
p.noStroke();
p.fill(r, g, b, alpha);
p.circle(m.x, m.y, m.size);
if (m.age > 1.5 && m.age < 6) {
p.noFill();
p.stroke(r, g, b, alpha * 0.3);
p.strokeWeight(0.8);
p.circle(m.x, m.y, m.size * 2.2);
}
}
p.noStroke();
p.fill(...colors.accent2, 230);
p.circle(originalDot.x, originalDot.y, 12);
p.fill(...colors.accent1, 180);
p.circle(originalDot.x, originalDot.y, 6);
p.noStroke();
p.fill(...colors.accent2, 200);
p.textAlign(p.CENTER);
p.textSize(9);
p.text('original event', originalDot.x, originalDot.y - 14);
p.fill(...colors.accent1, 120);
p.textSize(9);
p.text('each recall creates a new copy β slightly changed', 200, 290);
};
};