The Consensus Illusion
About This Sketch
Two columns of nodes — the same people, seen differently. On the left, the social surface: uniform muted tones, all pulsing together, projecting a shared rhythm. On the right, the private interior: each node in its own color, vibrating at its own frequency, diverging in ways the left column conceals.
This is pluralistic ignorance made visible. The consensus you observe in a room is partly manufactured from private dissent. Accompanies the post "The Consensus Illusion."
Algorithm
A field of nodes split into two columns. The left column shows how nodes appear publicly:
all pulsing in near-synchrony with the same muted color — the visible consensus. The right
column shows the same nodes as they actually are: each vibrating at its own frequency, each
in its own warm hue — private divergence hidden beneath the uniform surface.
This sketch accompanies the blog post "The Consensus Illusion" and visualizes pluralistic
ignorance: the gap between what people show and what they hold.
Pseudocode
SETUP:
Initialize 28 nodes, each with:
- random position (shared between left and right columns)
- random private frequency and phase
- random color mix between accent1 and accent2
- random base size
DRAW (every frame):
Get current theme colors
Clear background
Compute global conformist pulse (sine wave, shared frequency)
For each node:
LEFT COLUMN (public behavior):
Position at left-half x coordinate
Pulse size using the global pulse
Draw in uniform accent3 color with soft glow
RIGHT COLUMN (private belief):
Position at right-half x coordinate
Pulse size using node's own private frequency and phase
Draw in node's individual warm color with independent alpha
Draw dividing line between columns
Draw column labels ("what you observe" / "what they hold")
Draw caption
Source Code
let sketch = function(p) {
// Two columns of nodes, representing a social group.
// Left column: "what you observe" — all nodes pulse in near-synchrony with the same muted color.
// Right column: "what they hold" — same nodes pulse independently with varied warm tones.
// The visual contrast shows how public uniformity conceals private divergence.
const N = 28;
let nodes = [];
let time = 0;
p.setup = function() {
p.createCanvas(400, 300);
p.colorMode(p.RGB);
p.randomSeed(42);
for (let i = 0; i < N; i++) {
nodes.push({
px: p.random(0.07, 0.93),
py: p.random(0.07, 0.93),
privatePhase: p.random(p.TWO_PI),
privateFreq: p.random(0.5, 2.1),
colorT: p.random(0, 1),
sizeBase: p.random(4.5, 8.5),
});
}
};
p.draw = function() {
const colors = getThemeColors();
p.background(...colors.bg);
time += 0.016;
let globalPulse = 0.88 + 0.12 * Math.sin(time * 1.4);
for (let n of nodes) {
let lx = 14 + n.px * 172;
let ly = n.py * 300;
let pubSize = n.sizeBase * globalPulse;
p.noStroke();
p.fill(...colors.accent3, 22);
p.circle(lx, ly, pubSize * 2.4);
p.fill(...colors.accent3, 145);
p.circle(lx, ly, pubSize);
let rx = 214 + n.px * 172;
let privPulse = 0.80 + 0.20 * Math.sin(time * n.privateFreq + n.privatePhase);
let privSize = n.sizeBase * privPulse;
let rv = p.lerp(colors.accent1[0], colors.accent2[0], n.colorT);
let gv = p.lerp(colors.accent1[1], colors.accent2[1], n.colorT);
let bv = p.lerp(colors.accent1[2], colors.accent2[2], n.colorT);
let alpha = 110 + 90 * (0.5 + 0.5 * Math.sin(time * n.privateFreq * 0.7 + n.privatePhase));
p.fill(rv, gv, bv, 25);
p.circle(rx, ly, privSize * 2.4);
p.fill(rv, gv, bv, alpha);
p.circle(rx, ly, privSize);
}
p.stroke(...colors.accent3, 20);
p.strokeWeight(0.8);
p.line(200, 5, 200, 292);
p.noStroke();
p.fill(...colors.accent3, 80);
p.textAlign(p.CENTER);
p.textSize(9);
p.text('what you observe', 100, 14);
p.text('what they hold', 300, 14);
p.fill(...colors.accent3, 55);
p.textSize(8.5);
p.text('public conformity \u00b7 private divergence', 200, 293);
};
};