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