Where ideas percolate and thoughts brew

The Feedback Paradox

About This Sketch

Watch as a center point gets pulled in contradictory directions by multiple voices, each representing a piece of feedback. Early feedback provides clear direction—just a few voices create coherent movement. But as more voices appear, each with their own agenda, the center becomes chaotic and confused. The clarity metric drops as voice count rises. This is the feedback paradox: the more input you collect, the less you know what to do. You're not synthesizing wisdom—you're drowning in contradictions.

Algorithm

The Feedback Paradox visualizes how collecting more feedback creates confusion rather than clarity. A central point (representing you) is pulled in multiple directions by "voices"—each representing a piece of feedback from a different person. Initially, with just a few voices, the center point moves in relatively clear directions. But as more voices spawn over time, each pulling toward their own preferred position, the central point becomes increasingly chaotic. The pulls contradict each other: one voice wants you to move left while another wants you to move right. The result isn't averaged wisdom—it's paralysis and jittery confusion. The "Clarity" metric decreases as more voices are added, demonstrating the core thesis: past the saturation point, more feedback makes you less certain about what to do, not more. The voices fade over time (representing how feedback becomes outdated), but new ones keep appearing faster than old ones disappear, maintaining the chaos. This sketch accompanies the blog post "The Feedback Paradox" which argues that organizations' push for constant feedback creates confusion rather than improvement.

Pseudocode

SETUP:
  Initialize canvas (400x300)
  Create center point at canvas center
  Initialize empty array for feedback voices

VOICE CLASS:
  Properties: target position, pull strength, oscillation phase
  Each voice wants you to move toward a specific location
  Generate random "opinion" from contradictory set:
    ("Be more assertive", "Listen more", "Delegate", "Be hands-on", etc.)

DRAW (every frame):
  Get current theme colors
  Clear background

  Spawn new voices periodically (rate increases over time)
  Maximum 25 voices active at once

  For each voice:
    Calculate pull force toward its target position
    Add oscillation (opinions shift over time)
    Display as anchor point with connecting line
    Age the voice, remove when expired

  Sum all pull forces from all voices
  Apply to center point velocity
  Update center point position with dampening

  Display center point with jitter (proportional to voice count)
  Show statistics: active voice count, clarity level
  Display state message:
    Few voices: "Early feedback provides clear direction"
    Many voices: "Paralyzed by conflicting advice"

RESULT:
  As more voices accumulate, center point becomes more chaotic
  Demonstrates: more feedback ≠ more clarity

Source Code

let sketch = function(p) {
    // Visualization: The Feedback Paradox
    // Multiple "voices" pull a center point in contradictory directions
    // As more voices appear, the center becomes more chaotic, not more stable
    // Represents how more feedback creates confusion rather than clarity

    let voices = [];
    let centerPoint = {x: 200, y: 150};
    let actualPosition = {x: 200, y: 150};
    let velocity = {x: 0, y: 0};
    let time = 0;
    let voiceSpawnTimer = 0;

    class Voice {
        constructor() {
            // Each voice has a preferred position they want you to move toward
            this.targetX = p.random(80, 320);
            this.targetY = p.random(80, 220);
            this.strength = p.random(0.3, 0.8);
            this.phase = p.random(0, p.TWO_PI);
            this.frequency = p.random(0.01, 0.03);
            this.opinion = this.generateOpinion();
            this.color = [p.random(100, 255), p.random(100, 200), p.random(50, 150)];
            this.age = 0;
            this.maxAge = p.random(200, 400);
        }

        generateOpinion() {
            const opinions = [
                "Be more assertive", "Listen more", "Delegate more",
                "Be more hands-on", "Think strategically", "Focus on details",
                "Speak up more", "Be more concise", "Show more empathy",
                "Be more direct", "Build relationships", "Drive results"
            ];
            return p.random(opinions);
        }

        update() {
            this.age++;
            this.phase += this.frequency;
        }

        getPull() {
            // Each voice pulls you toward their preferred position
            let pullX = (this.targetX - actualPosition.x) * this.strength * 0.01;
            let pullY = (this.targetY - actualPosition.y) * this.strength * 0.01;

            // Add oscillation to represent changing opinions
            let oscillation = p.sin(this.phase) * 0.5;
            pullX *= (1 + oscillation);
            pullY *= (1 + oscillation);

            return {x: pullX, y: pullY};
        }

        display(colors) {
            // Draw the voice as a point with a line pulling toward it
            let alpha = p.map(this.age, 0, this.maxAge, 255, 50);

            // Connection line showing the pull
            p.stroke(...colors.accent2, alpha * 0.3);
            p.strokeWeight(1);
            p.line(actualPosition.x, actualPosition.y, this.targetX, this.targetY);

            // The voice's anchor point
            p.noStroke();
            p.fill(...colors.accent1, alpha * 0.6);
            p.circle(this.targetX, this.targetY, 6);

            // Pulsing to show active pulling
            let pulse = p.sin(this.phase) * 2 + 4;
            p.fill(...colors.accent2, alpha * 0.3);
            p.circle(this.targetX, this.targetY, pulse);
        }

        isExpired() {
            return this.age > this.maxAge;
        }
    }

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

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

        time++;
        voiceSpawnTimer++;

        // Title
        p.fill(...colors.accent3, 220);
        p.noStroke();
        p.textAlign(p.LEFT, p.TOP);
        p.textSize(12);
        p.textFont('Georgia');
        p.text('The Feedback Paradox', 15, 15);

        // Add new voices periodically - accelerating over time
        let spawnRate = p.max(30 - Math.floor(time / 100), 10);
        if (voiceSpawnTimer > spawnRate && voices.length < 25) {
            voices.push(new Voice());
            voiceSpawnTimer = 0;
        }

        // Calculate total pull from all voices
        let totalPull = {x: 0, y: 0};
        for (let voice of voices) {
            let pull = voice.getPull();
            totalPull.x += pull.x;
            totalPull.y += pull.y;
        }

        // Apply pulls with dampening (more voices = more chaos)
        velocity.x += totalPull.x;
        velocity.y += totalPull.y;

        // Dampening to prevent explosion
        velocity.x *= 0.95;
        velocity.y *= 0.95;

        // Update position
        actualPosition.x += velocity.x;
        actualPosition.y += velocity.y;

        // Keep in bounds with soft boundary
        if (actualPosition.x < 50) {
            actualPosition.x = 50;
            velocity.x *= -0.5;
        }
        if (actualPosition.x > 350) {
            actualPosition.x = 350;
            velocity.x *= -0.5;
        }
        if (actualPosition.y < 50) {
            actualPosition.y = 50;
            velocity.y *= -0.5;
        }
        if (actualPosition.y > 250) {
            actualPosition.y = 250;
            velocity.y *= -0.5;
        }

        // Update and display voices
        for (let i = voices.length - 1; i >= 0; i--) {
            voices[i].update();
            voices[i].display(colors);

            if (voices[i].isExpired()) {
                voices.splice(i, 1);
            }
        }

        // Draw the center point (you) being pulled around
        // Trail showing recent path (confusion)
        p.noFill();
        p.stroke(...colors.accent3, 100);
        p.strokeWeight(2);

        // Central point - gets more chaotic with more voices
        let jitter = voices.length * 0.3;
        let jitterX = p.random(-jitter, jitter);
        let jitterY = p.random(-jitter, jitter);

        p.fill(...colors.accent2, 200);
        p.noStroke();
        p.circle(actualPosition.x + jitterX, actualPosition.y + jitterY, 10);

        // Inner core
        p.fill(...colors.bg, 150);
        p.circle(actualPosition.x, actualPosition.y, 5);

        // Show chaos level
        p.textAlign(p.LEFT);
        p.textSize(10);
        p.fill(...colors.accent3, 200);
        p.text(`Active voices: ${voices.length}`, 15, 265);

        // Calculate movement chaos (speed indicates confusion)
        let chaos = p.sqrt(velocity.x * velocity.x + velocity.y * velocity.y);
        let chaosLevel = p.constrain(p.floor(chaos * 3), 0, 10);
        p.text(`Clarity: ${p.max(0, 10 - chaosLevel)}/10`, 15, 280);

        // Message based on state
        p.textAlign(p.CENTER);
        p.textSize(9);
        if (voices.length < 3) {
            p.fill(...colors.accent1, 180);
            p.text('Early feedback provides clear direction', 200, 40);
        } else if (voices.length < 8) {
            p.fill(...colors.accent2, 180);
            p.text('More feedback, more contradictions', 200, 40);
        } else {
            p.fill(...colors.accent2, 220);
            p.text('Paralyzed by conflicting advice', 200, 40);
        }

        // Easter egg: occasionally show specific contradictory feedback
        if (time % 180 === 0 && voices.length > 5) {
            // This creates a brief visible contradiction
        }
    };
};