The Clarity Trap
About This Sketch
A visual exploration of the tension between explicit and implicit knowledge. The left side shows clear, geometric forms that are rigid and brittle. The right side shows organic, flowing shapes that resist definition but remain rich and alive. In between, translation attempts fragment and fade—some things lose their essence when forced into clarity.
This sketch accompanies the essay "The Clarity Trap," which argues that while clarity is valuable in certain domains, many of our most important human capacities—intuition, tacit knowledge, artistic sensibility—are valuable precisely because they resist being made fully explicit.
Algorithm
This sketch visualizes the tension between explicit and implicit knowledge through contrasting visual languages.
The left side displays rigid geometric forms—perfect squares with clear grid structures that snap to precise angles. These represent explicit knowledge: clear, measurable, easily articulated, but rigid and brittle.
The right side shows organic, flowing shapes with soft curves and subtle breathing motions. Vertices drift organically using Perlin noise, creating forms that resist precise definition. These represent implicit knowledge: intuitive expertise, artistic sensibility, tacit understanding—rich and adaptive but ineffable.
In the center "translation zone," ephemeral lines attempt to bridge between these two domains, but they fragment and fade, visualizing the information loss that occurs when we force implicit knowledge into explicit structures or vice versa.
The sketch illustrates the core thesis: some valuable things resist clarity, and forcing them into explicit frameworks destroys rather than preserves them.
Pseudocode
SETUP:
Create 3 explicit elements (left side, geometric)
Create 3 implicit elements (right side, organic)
Initialize empty translation array
EXPLICIT ELEMENT:
Position: Fixed on left side
Shape: Perfect square with grid lines
Motion: Snap to precise angles (no smoothness)
Appearance: Sharp, clear, structured
IMPLICIT ELEMENT:
Position: Fixed on right side
Shape: Organic blob with 8 vertices
Motion: Smooth rotation, breathing animation
Vertices: Drift using Perlin noise
Appearance: Flowing, curved, ineffable
TRANSLATION:
Spawn periodically in center zone
Life: Fades over time
Display: Fragmented lines showing information loss
From explicit: Lines break apart and scatter
From implicit: Lines fade and lose nuance
DRAW (each frame):
Clear background with theme colors
Draw labels and center dividing line
Highlight translation zone with subtle fill
Update all explicit elements (rigid motion)
Update all implicit elements (organic motion)
Create new translation attempts periodically
Update and display all translations
Remove dead translations
Display key insight text
Source Code
let sketch = function(p) {
// Visualization: The Clarity Trap
// Left side: clear, explicit, geometric structure (rigid, brittle)
// Right side: implicit, flowing forms (rich, adaptive, alive)
// Center: attempting to translate one to the other (loss of information)
let explicits = [];
let implicits = [];
let translations = [];
let time = 0;
class ExplicitElement {
constructor(y) {
this.x = 75;
this.y = y;
this.size = 30;
this.angle = 0;
this.targetAngle = 0;
this.clarity = 1.0;
}
update() {
// Rigid, precise rotation
this.targetAngle = p.floor(p.frameCount / 60) * p.PI / 4;
this.angle = this.targetAngle; // Snap to angle, no smoothness
}
display(colors) {
p.push();
p.translate(this.x, this.y);
p.rotate(this.angle);
// Sharp, clear geometric shapes
p.stroke(...colors.accent2, 200);
p.strokeWeight(2);
p.noFill();
// Perfect square - maximally clear
p.rect(-this.size/2, -this.size/2, this.size, this.size);
// Grid lines inside - explicitly structured
p.stroke(...colors.accent2, 100);
p.strokeWeight(1);
p.line(-this.size/2, 0, this.size/2, 0);
p.line(0, -this.size/2, 0, this.size/2);
p.pop();
}
}
class ImplicitElement {
constructor(y) {
this.x = 325;
this.y = y;
this.baseSize = 35;
this.size = this.baseSize;
this.angle = p.random(p.TWO_PI);
this.angleSpeed = p.random(-0.03, 0.03);
this.breathe = p.random(p.TWO_PI);
this.vertices = [];
this.generateVertices();
}
generateVertices() {
// Organic, flowing shape - resists clear definition
let points = 8;
this.vertices = [];
for (let i = 0; i < points; i++) {
let angle = (i / points) * p.TWO_PI;
let r = this.size/2 * p.random(0.7, 1.3);
this.vertices.push({
angle: angle,
radius: r,
noiseOffset: p.random(1000)
});
}
}
update() {
this.angle += this.angleSpeed;
this.breathe += 0.02;
// Organic breathing motion
this.size = this.baseSize + p.sin(this.breathe) * 5;
// Vertices drift subtly
for (let v of this.vertices) {
v.radius += p.noise(v.noiseOffset + time * 0.01) * 0.3 - 0.15;
v.radius = p.constrain(v.radius, this.baseSize * 0.4, this.baseSize * 0.9);
}
}
display(colors) {
p.push();
p.translate(this.x, this.y);
p.rotate(this.angle);
// Soft, flowing forms
p.stroke(...colors.accent1, 150);
p.strokeWeight(2);
p.noFill();
// Draw organic shape
p.beginShape();
for (let v of this.vertices) {
let x = p.cos(v.angle) * v.radius;
let y = p.sin(v.angle) * v.radius;
p.curveVertex(x, y);
}
// Close the shape smoothly
let first = this.vertices[0];
p.curveVertex(p.cos(first.angle) * first.radius, p.sin(first.angle) * first.radius);
let second = this.vertices[1];
p.curveVertex(p.cos(second.angle) * second.radius, p.sin(second.angle) * second.radius);
p.endShape();
// Subtle inner detail - hints at depth
p.stroke(...colors.accent1, 60);
p.strokeWeight(1);
for (let i = 0; i < 3; i++) {
let v = this.vertices[i * 2];
let x = p.cos(v.angle) * v.radius * 0.6;
let y = p.sin(v.angle) * v.radius * 0.6;
p.circle(x, y, 3);
}
p.pop();
}
}
class Translation {
constructor() {
this.x = 200;
this.y = p.random(80, 220);
this.life = 1.0;
this.fromSide = p.random() > 0.5 ? 'explicit' : 'implicit';
}
update() {
this.life -= 0.008;
}
display(colors) {
if (this.life <= 0) return;
let alpha = this.life * 100;
// Show information loss during translation
p.stroke(...colors.accent3, alpha);
p.strokeWeight(1);
if (this.fromSide === 'explicit') {
// Trying to force implicit into explicit - breaks apart
for (let i = 0; i < 5; i++) {
let offset = (1 - this.life) * 40;
p.line(
120 + i * 8, this.y,
120 + i * 8 + offset, this.y + p.random(-offset, offset)
);
}
} else {
// Trying to force explicit into implicit - loses nuance
let points = 8;
for (let i = 0; i < points - 1; i++) {
let progress = i / points;
let x1 = 280 - progress * 40;
let x2 = 280 - (progress + 0.1) * 40;
let fade = progress * this.life;
p.stroke(...colors.accent2, alpha * fade);
p.line(x1, this.y, x2, this.y);
}
}
}
isDead() {
return this.life <= 0;
}
}
p.setup = function() {
p.createCanvas(400, 300);
// Create elements
for (let i = 0; i < 3; i++) {
explicits.push(new ExplicitElement(80 + i * 70));
implicits.push(new ImplicitElement(80 + i * 70));
}
};
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
time++;
// Labels
p.fill(...colors.accent3, 220);
p.noStroke();
p.textAlign(p.CENTER, p.TOP);
p.textSize(11);
p.textFont('Georgia');
p.text('Explicit', 75, 15);
p.text('Implicit', 325, 15);
// Descriptions
p.textSize(8);
p.fill(...colors.accent3, 180);
p.text('Clear but brittle', 75, 30);
p.text('Rich but ineffable', 325, 30);
// Dividing line
p.stroke(...colors.accent3, 60);
p.strokeWeight(1);
p.line(200, 45, 200, 260);
// Warning zone in the middle
p.fill(...colors.accent2, 40);
p.noStroke();
p.rect(175, 45, 50, 215);
// Create new translation attempts periodically
if (time % 120 === 0) {
translations.push(new Translation());
}
// Update and display everything
for (let e of explicits) {
e.update();
e.display(colors);
}
for (let i of implicits) {
i.update();
i.display(colors);
}
for (let i = translations.length - 1; i >= 0; i--) {
translations[i].update();
translations[i].display(colors);
if (translations[i].isDead()) {
translations.splice(i, 1);
}
}
// Key insight
p.fill(...colors.accent2, 180);
p.textSize(8);
p.textAlign(p.CENTER);
p.text('Some things lose their value when forced into clarity', 200, 275);
// Small annotation for translation zone
if (time > 100) {
p.fill(...colors.accent3, 160);
p.textSize(7);
p.text('Lost in translation', 200, 55);
}
};
};