Where ideas percolate and thoughts brew

The Experience Trap

About This Sketch

A generative visualization exploring the difference between collecting experiences and actually living them. Watch as fast-moving collectors rapidly accumulate hollow experiences (shown as empty circles) while slower, more deliberate livers create deeply felt, glowing moments.

This sketch accompanies the essay "The Experience Trap" which argues that experience consumption has become just another form of mindless consumerism—optimized for collection and status rather than genuine presence and depth.

Algorithm

This sketch visualizes two fundamentally different approaches to experiences: **Experience Collectors (triangles):** - Move quickly between experiences - Capture many experiences rapidly - Leave experiences hollow (just the outline) - Display a count of captured experiences - Optimize for quantity and coverage **Experience Livers (circles):** - Move slowly and deliberately - Dwell at each experience - Transform experiences into glowing, lived moments - Focus on depth over breadth - Create lasting impact (experiences continue to glow) The visualization shows how collectors accumulate hollow experiences while livers create fewer but deeply meaningful ones. Collected experiences appear as empty circles—captured but not truly experienced. Lived experiences glow and pulse, representing genuine engagement and lasting impact.

Pseudocode

SETUP:
  Create 4 collectors (fast-moving triangles)
  Create 2 livers (slow-moving circles)
  Populate canvas with experience points

DRAW (every frame):
  Generate new experiences periodically

  FOR each collector:
    Find closest uncaptured experience
    Move quickly toward it (speed: 2.5)
    Capture on contact (mark as hollow)
    Increment collection counter

  FOR each liver:
    IF currently dwelling at experience:
      Increment dwell time
      IF dwelled for 60 frames:
        Mark experience as "lived" (glowing)
        Move to next experience
    ELSE:
      Find closest uncaptured experience
      Move slowly toward it (speed: 0.8)
      Begin dwelling when close

  Draw experiences:
    - Uncaptured: subtle, faded circles
    - Captured but not lived: hollow circles (outline only)
    - Lived: filled, glowing circles with pulsing aura

  Remove experiences that have faded completely

Source Code

let sketch = function(p) {
    let collectors = [];
    let livers = [];
    let experiences = [];
    let time = 0;

    class ExperiencePoint {
        constructor() {
            this.x = p.random(p.width);
            this.y = p.random(p.height);
            this.size = p.random(3, 8);
            this.captured = false;
            this.lived = false;
            this.fadeTime = 0;
        }

        draw(colors) {
            if (this.captured && !this.lived) {
                // Captured but not lived - hollow
                p.noFill();
                p.stroke(...colors.accent2, 150);
                p.strokeWeight(2);
                p.circle(this.x, this.y, this.size);
            } else if (this.lived) {
                // Actually experienced - filled and glowing
                let glow = p.sin(this.fadeTime * 0.05) * 30 + 100;
                p.fill(...colors.accent1, glow);
                p.noStroke();
                p.circle(this.x, this.y, this.size * 1.5);

                // Add glow effect
                p.fill(...colors.accent1, glow * 0.3);
                p.circle(this.x, this.y, this.size * 2.5);
            } else {
                // Uncaptured experience - subtle
                p.fill(...colors.accent3, 80);
                p.noStroke();
                p.circle(this.x, this.y, this.size);
            }

            if (this.lived) {
                this.fadeTime++;
            }
        }
    }

    class Collector {
        constructor() {
            this.x = p.random(p.width);
            this.y = p.random(p.height);
            this.experiences = 0;
            this.speed = 2.5;
            this.targetIndex = 0;
        }

        update() {
            // Rapidly move to experiences to collect them
            if (experiences.length > 0) {
                let closest = null;
                let closestDist = Infinity;

                for (let exp of experiences) {
                    if (!exp.captured && !exp.lived) {
                        let d = p.dist(this.x, this.y, exp.x, exp.y);
                        if (d < closestDist) {
                            closestDist = d;
                            closest = exp;
                        }
                    }
                }

                if (closest) {
                    let angle = p.atan2(closest.y - this.y, closest.x - this.x);
                    this.x += p.cos(angle) * this.speed;
                    this.y += p.sin(angle) * this.speed;

                    // Capture if close
                    if (closestDist < 10) {
                        closest.captured = true;
                        this.experiences++;
                    }
                }
            }
        }

        draw(colors) {
            // Draw as hurried triangle
            p.fill(...colors.accent2, 200);
            p.noStroke();
            let angle = p.atan2(
                experiences.length > 0 ? experiences[0].y - this.y : 0,
                experiences.length > 0 ? experiences[0].x - this.x : 0
            );
            p.push();
            p.translate(this.x, this.y);
            p.rotate(angle);
            p.triangle(8, 0, -5, 4, -5, -4);
            p.pop();

            // Draw collection trail
            p.fill(...colors.accent2, 100);
            p.textAlign(p.CENTER);
            p.textSize(8);
            p.text(this.experiences, this.x, this.y - 15);
        }
    }

    class Liver {
        constructor() {
            this.x = p.random(p.width);
            this.y = p.random(p.height);
            this.speed = 0.8;
            this.dwellTime = 0;
            this.currentExp = null;
        }

        update() {
            if (this.currentExp && !this.currentExp.lived) {
                // Dwell at experience
                this.dwellTime++;

                // Actually experience it after dwelling
                if (this.dwellTime > 60) {
                    this.currentExp.lived = true;
                    this.currentExp = null;
                    this.dwellTime = 0;
                }
            } else {
                // Find nearby uncaptured experience
                let closest = null;
                let closestDist = Infinity;

                for (let exp of experiences) {
                    if (!exp.captured && !exp.lived) {
                        let d = p.dist(this.x, this.y, exp.x, exp.y);
                        if (d < closestDist) {
                            closestDist = d;
                            closest = exp;
                        }
                    }
                }

                if (closest) {
                    let angle = p.atan2(closest.y - this.y, closest.x - this.x);
                    this.x += p.cos(angle) * this.speed;
                    this.y += p.sin(angle) * this.speed;

                    if (closestDist < 15) {
                        this.currentExp = closest;
                        this.dwellTime = 0;
                    }
                }
            }
        }

        draw(colors) {
            // Draw as circle with presence indicator
            p.fill(...colors.accent1, 200);
            p.noStroke();
            p.circle(this.x, this.y, 8);

            // Draw dwelling circle if present at experience
            if (this.currentExp) {
                p.noFill();
                p.stroke(...colors.accent1, 150);
                p.strokeWeight(2);
                let progress = (this.dwellTime / 60) * 25;
                p.circle(this.x, this.y, progress);
            }
        }
    }

    p.setup = function() {
        p.createCanvas(400, 300);
        p.colorMode(p.RGB);

        // Create collectors (fast, many)
        for (let i = 0; i < 4; i++) {
            collectors.push(new Collector());
        }

        // Create livers (slow, few)
        for (let i = 0; i < 2; i++) {
            livers.push(new Liver());
        }

        // Initial experiences
        for (let i = 0; i < 15; i++) {
            experiences.push(new ExperiencePoint());
        }
    };

    p.draw = function() {
        const colors = getThemeColors();
        p.background(...colors.bg);

        time++;

        // Occasionally add new experiences
        if (time % 40 === 0 && experiences.length < 25) {
            experiences.push(new ExperiencePoint());
        }

        // Remove old lived experiences (they fade but leave impact)
        experiences = experiences.filter(exp => {
            if (exp.lived && exp.fadeTime > 200) {
                return false;
            }
            return true;
        });

        // Draw experiences first
        for (let exp of experiences) {
            exp.draw(colors);
        }

        // Update and draw collectors
        for (let collector of collectors) {
            collector.update();
            collector.draw(colors);
        }

        // Update and draw livers
        for (let liver of livers) {
            liver.update();
            liver.draw(colors);
        }

        // Draw legend
        p.fill(...colors.accent3);
        p.noStroke();
        p.textAlign(p.LEFT, p.TOP);
        p.textSize(9);
        p.text("Triangles: Experience collectors (fast, hollow)", 10, 10);
        p.text("Circles: Experience livers (slow, deep)", 10, 23);
        p.text("Hollow circles: Collected but not lived", 10, 270);
        p.text("Glowing circles: Actually experienced", 10, 283);
    };
};