Where ideas percolate and thoughts brew

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);
    };
};