Where ideas percolate and thoughts brew

The Sound of Thinking

About This Sketch

A visualization exploring attention, silence, and the space where thinking happens. The sketch shows information waves constantly bombarding from all edges, trying to penetrate a protected center where thoughts can actually form. The chaotic input from the periphery contrasts sharply with the calm, pulsing particles at the center—representing the insights that can only emerge in silence.

The post this accompanies examines how we've filled every cognitive gap with stimulus, eliminating the very conditions under which understanding develops.

Algorithm

The visualization explores the tension between constant information input and the protected space needed for genuine thinking. Input waves spawn continuously from all four edges of the canvas, representing the barrage of notifications, articles, podcasts, and stimuli that bombard us throughout the day. These waves move toward the center with an oscillating motion that creates a sense of chaos and urgency. As they approach the protected center zone, they quickly fade and dissipate—symbolizing how real thinking requires filtering out the noise. At the center sits a calm, glowing zone—the "quiet center" where thinking actually happens. Within this protected space, thought particles orbit slowly, pulsing gently and drifting in contemplative patterns. These particles represent the organic emergence of ideas when given space to form without interruption. The contrast between the chaotic waves from the edges and the serene particles in the center visualizes the core insight of the post: thinking requires silence and protected attention. The waves can't penetrate the center, just as true understanding can't happen when we're constantly consuming inputs. This sketch accompanies the blog post "The Sound of Thinking" and explores how modern life has filled every silence with stimulus, eliminating the cognitive space where real thinking emerges.

Pseudocode

SETUP:
  Create canvas (400x300)
  Initialize empty arrays for waves and center particles
  Spawn 30 thought particles in center zone (radius 60)
  Each particle has random orbit parameters

DRAW (every frame):
  Get current theme colors
  Fade background (creates trail effect)
  Draw protected center circle with glowing inner gradient

  SPAWN INPUT WAVES (every 8 frames):
    Choose random edge (top/right/bottom/left)
    Create wave pointing toward center
    Give wave oscillation parameters

  UPDATE AND DRAW INPUT WAVES:
    For each wave:
      Move toward center along angle
      Add perpendicular oscillation for chaotic feel
      If approaching center (within 40px of radius):
        Rapidly fade out (suppressed by protected space)
      Otherwise fade slowly
      Draw as elongated ellipse with trail
      Remove if dead

  UPDATE AND DRAW THOUGHT PARTICLES:
    For each particle in center:
      Orbit around center point
      Drift radius in/out slowly
      Bounce off inner boundaries
      Pulse opacity gently
      Draw as soft glowing circles (multiple layers)

  DRAW CENTER FOCAL POINT:
    Small bright circle at exact center
    Represents the self/consciousness

  LABELS:
    Title, annotations about input vs. quiet center

  PULSE EFFECT (every 180 frames):
    Expanding ring from center
    Represents moments of insight emerging

Source Code

let sketch = function(p) {
    // The Sound of Thinking
    // Visualization: Input waves overwhelming a quiet center
    // Shows the constant barrage of information (waves from edges)
    // versus the still center where thinking happens (protected space)
    // As waves penetrate less, clarity emerges in the center

    let waves = [];
    let centerParticles = [];
    let time = 0;
    let centerRadius = 60;

    class InputWave {
        constructor() {
            // Waves come from all edges
            let side = p.floor(p.random(4));
            if (side === 0) { // top
                this.x = p.random(400);
                this.y = 0;
                this.angle = p.HALF_PI;
            } else if (side === 1) { // right
                this.x = 400;
                this.y = p.random(300);
                this.angle = p.PI;
            } else if (side === 2) { // bottom
                this.x = p.random(400);
                this.y = 300;
                this.angle = -p.HALF_PI;
            } else { // left
                this.x = 0;
                this.y = p.random(300);
                this.angle = 0;
            }

            // Angle toward center with some variance
            let angleToCenter = p.atan2(150 - this.y, 200 - this.x);
            this.angle = angleToCenter + p.random(-0.3, 0.3);

            this.speed = p.random(1, 2.5);
            this.amplitude = p.random(8, 15);
            this.frequency = p.random(0.1, 0.2);
            this.offset = p.random(p.TWO_PI);
            this.life = 255;
            this.size = p.random(2, 4);
        }

        update() {
            // Move toward center
            this.x += p.cos(this.angle) * this.speed;
            this.y += p.sin(this.angle) * this.speed;

            // Oscillate perpendicular to direction
            let perpAngle = this.angle + p.HALF_PI;
            let oscillation = p.sin(time + this.offset) * this.amplitude;
            this.x += p.cos(perpAngle) * oscillation * 0.05;
            this.y += p.sin(perpAngle) * oscillation * 0.05;

            // Fade as it approaches center
            let distToCenter = p.dist(this.x, this.y, 200, 150);
            if (distToCenter < centerRadius + 40) {
                this.life -= 8;
            }

            this.life -= 0.5;
        }

        display(colors) {
            // Draw as chaotic, energetic marks
            p.noStroke();
            p.fill(...colors.accent2, this.life * 0.6);
            p.push();
            p.translate(this.x, this.y);
            p.rotate(this.angle);
            p.ellipse(0, 0, this.size * 3, this.size);
            p.pop();

            // Trailing dots
            p.fill(...colors.accent2, this.life * 0.3);
            p.circle(this.x, this.y, this.size * 0.5);
        }

        isDead() {
            return this.life <= 0;
        }
    }

    class ThoughtParticle {
        constructor() {
            // Start at center
            let angle = p.random(p.TWO_PI);
            let r = p.random(centerRadius * 0.3);
            this.x = 200 + p.cos(angle) * r;
            this.y = 150 + p.sin(angle) * r;

            this.angle = angle;
            this.orbitSpeed = p.random(-0.01, 0.01);
            this.orbitRadius = r;
            this.radiusVelocity = p.random(-0.1, 0.1);
            this.size = p.random(1.5, 3);
            this.pulse = p.random(p.TWO_PI);
            this.baseAlpha = p.random(150, 220);
        }

        update() {
            // Orbit around center with slow drift
            this.angle += this.orbitSpeed;
            this.orbitRadius += this.radiusVelocity;
            this.orbitRadius = p.constrain(this.orbitRadius, 5, centerRadius * 0.8);

            // If at edges, bounce back
            if (this.orbitRadius >= centerRadius * 0.8 || this.orbitRadius <= 5) {
                this.radiusVelocity *= -0.8;
            }

            this.x = 200 + p.cos(this.angle) * this.orbitRadius;
            this.y = 150 + p.sin(this.angle) * this.orbitRadius;

            this.pulse += 0.05;
        }

        display(colors) {
            let alpha = this.baseAlpha + p.sin(this.pulse) * 50;

            // Draw soft glowing particle
            p.noStroke();
            p.fill(...colors.accent1, alpha * 0.2);
            p.circle(this.x, this.y, this.size * 4);

            p.fill(...colors.accent1, alpha * 0.6);
            p.circle(this.x, this.y, this.size * 2);

            p.fill(...colors.accent1, alpha);
            p.circle(this.x, this.y, this.size);
        }
    }

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

        // Initialize thought particles in center
        for (let i = 0; i < 30; i++) {
            centerParticles.push(new ThoughtParticle());
        }
    };

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

        // Fade background for trails
        p.fill(...colors.bg, 40);
        p.noStroke();
        p.rect(0, 0, 400, 300);

        time += 0.1;

        // Draw protected center (the silence where thinking happens)
        p.noFill();
        p.stroke(...colors.accent3, 60);
        p.strokeWeight(1);
        p.circle(200, 150, centerRadius * 2);

        // Inner glow of center
        for (let r = centerRadius * 2; r > 0; r -= 10) {
            let alpha = p.map(r, 0, centerRadius * 2, 40, 0);
            p.fill(...colors.accent1, alpha);
            p.noStroke();
            p.circle(200, 150, r);
        }

        // Spawn input waves (information bombardment)
        if (p.frameCount % 8 === 0) {
            waves.push(new InputWave());
        }

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

            if (waves[i].isDead()) {
                waves.splice(i, 1);
            }
        }

        // Update and draw thought particles (what emerges in silence)
        for (let particle of centerParticles) {
            particle.update();
            particle.display(colors);
        }

        // Draw center point (the self)
        p.noStroke();
        p.fill(...colors.accent1, 200);
        p.circle(200, 150, 4);
        p.fill(...colors.accent1, 80);
        p.circle(200, 150, 8);

        // Labels
        p.textFont('Georgia');
        p.textAlign(p.CENTER);
        p.fill(...colors.accent2, 200);
        p.textSize(10);
        p.text('The Sound of Thinking', 200, 20);

        p.textSize(8);
        p.fill(...colors.accent2, 180);
        p.text('Input noise', 200, 40);

        p.fill(...colors.accent1, 180);
        p.text('The quiet center: where thoughts form', 200, 280);

        // Pulse effect on center every few seconds
        if (p.frameCount % 180 < 20) {
            let pulse = p.frameCount % 180;
            let alpha = p.map(pulse, 0, 20, 40, 0);
            p.noFill();
            p.stroke(...colors.accent1, alpha);
            p.strokeWeight(2);
            let size = centerRadius * 2 + pulse * 3;
            p.circle(200, 150, size);
        }
    };
};