The Originality Trap
About This Sketch
A visualization of the originality paradox: agents actively fleeing convention and each other (trying to be original) end up clustering in a ring of sameness, while agents drifting naturally without concern for originality achieve genuine distinctiveness. The sketch demonstrates how the performance of uniqueness creates a new conformity, while authentic exploration naturally finds unoccupied space.
Bright agents represent authentic creators following genuine curiosity—they glow because their distinctiveness is real. Dim agents represent those performing originality—they're numerous, busy, and all "different" in identical ways. Watch how the performers form a predictable ring around convention, while authentic agents scatter unpredictably across the space.
Algorithm
Pseudocode
SETUP:
Create canvas 400x300
Create 80 agents:
70% performing type (flee convention/others)
30% authentic type (drift naturally)
Place randomly on canvas
DRAW (every frame):
Clear background with theme colors
Draw center marker (convention)
Draw ring at radius 100 (where performers cluster)
FOR EACH agent:
IF performing type:
Calculate flee force from center
Calculate flee force from nearby performing agents
Apply pull toward ring radius (unconscious conformity)
Apply rotation around center (performing in a circle)
Move faster, work harder
ELSE IF authentic type:
Apply gentle random drift
If area is crowded, softly move toward space
Move slowly, naturally
Apply velocity with damping
Enforce canvas boundaries
Display agent:
Authentic = bright with glow (distinctive)
Performing = dim uniform appearance (sameness)
Show labels and legend
Source Code
let sketch = function(p) {
let agents = [];
let centerX = 200;
let centerY = 150;
let numAgents = 80;
class Agent {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type; // 'authentic' or 'performing'
this.vx = 0;
this.vy = 0;
this.targetAngle = p.random(p.TWO_PI);
this.speed = type === 'authentic' ? p.random(0.3, 0.8) : p.random(1, 2);
this.size = type === 'authentic' ? p.random(4, 8) : p.random(3, 5);
this.originalityAngle = p.random(p.TWO_PI);
this.rotationSpeed = p.random(0.01, 0.03);
}
update() {
if (this.type === 'performing') {
// Performing agents actively flee from center and each other
// They're "trying to be original" = avoiding similarity
// Flee from center
let dx = this.x - centerX;
let dy = this.y - centerY;
let distFromCenter = p.sqrt(dx * dx + dy * dy);
if (distFromCenter > 0) {
this.vx += (dx / distFromCenter) * 0.5;
this.vy += (dy / distFromCenter) * 0.5;
}
// Flee from nearby performing agents (everyone trying to be different)
for (let other of agents) {
if (other !== this && other.type === 'performing') {
let odx = this.x - other.x;
let ody = this.y - other.y;
let dist = p.sqrt(odx * odx + ody * ody);
if (dist < 30 && dist > 0) {
this.vx += (odx / dist) * 0.3;
this.vy += (ody / dist) * 0.3;
}
}
}
// But they end up in a ring - all "different" in the same way
let distFromIdealRing = Math.abs(distFromCenter - 100);
if (distFromCenter > 0) {
let pullX = (centerX - this.x) / distFromCenter;
let pullY = (centerY - this.y) / distFromCenter;
this.vx += pullX * distFromIdealRing * 0.01;
this.vy += pullY * distFromIdealRing * 0.01;
}
// Rotate around center (they're all performing in a circle)
this.originalityAngle += this.rotationSpeed;
let tangentX = -Math.sin(this.originalityAngle) * 0.2;
let tangentY = Math.cos(this.originalityAngle) * 0.2;
this.vx += tangentX;
this.vy += tangentY;
} else {
// Authentic agents just drift slowly, following genuine interest
// They're not trying to be original, just exploring
this.vx += p.random(-0.1, 0.1);
this.vy += p.random(-0.1, 0.1);
// Very gentle pull toward areas with fewer agents (naturally finding gaps)
let nearbyCount = 0;
let avgX = 0;
let avgY = 0;
for (let other of agents) {
let dx = other.x - this.x;
let dy = other.y - this.y;
let dist = p.sqrt(dx * dx + dy * dy);
if (dist < 50 && dist > 0) {
nearbyCount++;
avgX += other.x;
avgY += other.y;
}
}
if (nearbyCount > 5) {
avgX /= nearbyCount;
avgY /= nearbyCount;
let fleeX = this.x - avgX;
let fleeY = this.y - avgY;
this.vx += fleeX * 0.01;
this.vy += fleeY * 0.01;
}
}
// Apply velocity
this.vx *= 0.92; // Damping
this.vy *= 0.92;
this.x += this.vx;
this.y += this.vy;
// Boundary constraints
let margin = 20;
if (this.x < margin) this.vx += 0.5;
if (this.x > 400 - margin) this.vx -= 0.5;
if (this.y < margin) this.vy += 0.5;
if (this.y > 300 - margin) this.vy -= 0.5;
}
display(colors) {
p.noStroke();
if (this.type === 'authentic') {
// Authentic agents are brighter, more distinctive
p.fill(...colors.accent1, 220);
p.circle(this.x, this.y, this.size);
// Subtle glow
p.fill(...colors.accent1, 60);
p.circle(this.x, this.y, this.size * 1.8);
} else {
// Performing agents are dimmer, all looking the same
p.fill(...colors.accent3, 140);
p.circle(this.x, this.y, this.size);
}
}
}
p.setup = function() {
p.createCanvas(400, 300);
// Create agents - more performing than authentic (reflects reality)
for (let i = 0; i < numAgents; i++) {
let x = p.random(50, 350);
let y = p.random(50, 250);
// 70% performing originality, 30% authentic
let type = p.random() < 0.7 ? 'performing' : 'authentic';
agents.push(new Agent(x, y, type));
}
};
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
// Draw center point (representing "conventional")
p.noFill();
p.stroke(...colors.accent2, 80);
p.strokeWeight(1);
p.circle(centerX, centerY, 20);
p.circle(centerX, centerY, 200); // The ring where performers cluster
// Update and display agents
for (let agent of agents) {
agent.update();
agent.display(colors);
}
// Labels
p.noStroke();
p.fill(...colors.accent3);
p.textAlign(p.CENTER);
p.textSize(11);
p.text('The Originality Trap', 200, 15);
p.textSize(7);
p.textAlign(p.CENTER);
p.fill(...colors.accent2, 150);
p.text('CONVENTIONAL', centerX, centerY + 3);
// Legend
p.textAlign(p.LEFT);
p.textSize(8);
p.fill(...colors.accent1);
p.circle(15, 280, 6);
p.fill(...colors.accent3);
p.text('Authentic - drift naturally, end up distinctive', 25, 283);
p.fill(...colors.accent3, 140);
p.circle(15, 292, 4);
p.fill(...colors.accent3);
p.text('Performing - flee convention, cluster in sameness', 25, 295);
// Count stats
let authentic = agents.filter(a => a.type === 'authentic').length;
let performing = agents.filter(a => a.type === 'performing').length;
};
};