Where ideas percolate and thoughts brew

The Momentum Illusion

About This Sketch

A visualization contrasting momentum (busy activity) with progress (directed movement). Multiple particles on the left move quickly but randomly, while a single particle on the right moves deliberately toward a goal. Despite being slower, the progress particle actually reaches its destination—demonstrating that speed without direction is less valuable than intentional movement. Accompanies the essay "The Momentum Illusion" about confusing activity with achievement.

Algorithm

This sketch visualizes the difference between momentum (activity without direction) and progress (deliberate movement toward a goal). LEFT SIDE: Eight particles moving at high speed with frequently changing random directions, leaving short trails. They bounce off walls and constantly change course—lots of visible motion but no destination. Represents busy-work, constant activity, momentum culture. RIGHT SIDE: A single particle moving more slowly but directly toward a goal (represented by a rotating star). It leaves a longer, straighter trail showing its intentional path. Despite moving slower, it makes continuous progress toward its destination. The visual contrast demonstrates the core insight: High velocity without direction is less valuable than slower, intentional movement. The sketch tracks the progress particle's distance to the goal, showing measurable progress versus the directionless motion on the left. This accompanies the blog post "The Momentum Illusion" which explores how we confuse activity with progress and why stopping to check direction is often the most productive action.

Pseudocode

SETUP:
  Create canvas (400x300)
  Place goal star in right section
  Initialize 8 momentum particles (left side, random positions)
  Initialize 1 progress particle (center-right, aimed at goal)

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

  LEFT SECTION:
    For each momentum particle:
      - Add current position to trail
      - Move at high speed in current direction
      - Randomly change direction 3% of frames
      - Bounce off section boundaries
      - Draw fading trail (shows lots of motion)
      - Draw particle

  RIGHT SECTION:
    Draw rotating goal star

    For progress particle:
      - Add current position to trail
      - Calculate direction vector to goal
      - Move slowly but directly toward goal
      - Draw longer, straighter trail
      - Draw particle
      - Display distance remaining to goal

    If progress particle reaches goal:
      - Reset particle to starting position
      - Continue cycle

  Display alternating insight text
  Show labels for both sections

Source Code

let sketch = function(p) {
    // Visualization: The Momentum Illusion
    // Shows particles moving with high velocity but in random/circular directions
    // vs a single particle moving slowly but directly toward a goal
    // Demonstrates: Speed without direction vs. intentional progress

    let momentumParticles = [];
    let progressParticle;
    let goal;
    let frame = 0;

    class MomentumParticle {
        constructor(x, y) {
            this.x = x;
            this.y = y;
            this.vx = p.random(-2, 2);
            this.vy = p.random(-2, 2);
            this.size = 6;
            this.trail = [];
            this.maxTrail = 30;
        }

        update() {
            // High speed, random direction changes
            this.trail.push({ x: this.x, y: this.y });
            if (this.trail.length > this.maxTrail) {
                this.trail.shift();
            }

            this.x += this.vx;
            this.y += this.vy;

            // Random direction changes (momentum without purpose)
            if (p.random() < 0.03) {
                this.vx = p.random(-2, 2);
                this.vy = p.random(-2, 2);
            }

            // Bounce off walls
            if (this.x < 20 || this.x > 180) this.vx *= -1;
            if (this.y < 60 || this.y > 240) this.vy *= -1;

            // Keep in bounds
            this.x = p.constrain(this.x, 20, 180);
            this.y = p.constrain(this.y, 60, 240);
        }

        display(colors) {
            // Trail (showing lots of motion)
            p.noFill();
            p.beginShape();
            for (let i = 0; i < this.trail.length; i++) {
                let alpha = p.map(i, 0, this.trail.length, 0, 120);
                p.stroke(...colors.accent2, alpha);
                p.strokeWeight(1);
                p.vertex(this.trail[i].x, this.trail[i].y);
            }
            p.endShape();

            // Particle
            p.noStroke();
            p.fill(...colors.accent2, 180);
            p.circle(this.x, this.y, this.size);
        }
    }

    class ProgressParticle {
        constructor(x, y, goal) {
            this.x = x;
            this.y = y;
            this.goal = goal;
            this.size = 8;
            this.trail = [];
            this.maxTrail = 50;
            this.speed = 0.8; // Slower than momentum particles
        }

        update() {
            // Slow, deliberate movement toward goal
            this.trail.push({ x: this.x, y: this.y });
            if (this.trail.length > this.maxTrail) {
                this.trail.shift();
            }

            let dx = this.goal.x - this.x;
            let dy = this.goal.y - this.y;
            let distance = p.sqrt(dx * dx + dy * dy);

            if (distance > 5) {
                let angle = p.atan2(dy, dx);
                this.x += p.cos(angle) * this.speed;
                this.y += p.sin(angle) * this.speed;
            }
        }

        display(colors) {
            // Trail (showing direct path)
            p.noFill();
            p.beginShape();
            for (let i = 0; i < this.trail.length; i++) {
                let alpha = p.map(i, 0, this.trail.length, 0, 150);
                p.stroke(...colors.accent1, alpha);
                p.strokeWeight(2);
                p.vertex(this.trail[i].x, this.trail[i].y);
            }
            p.endShape();

            // Particle
            p.noStroke();
            p.fill(...colors.accent1, 200);
            p.circle(this.x, this.y, this.size);
        }

        distanceToGoal() {
            let dx = this.goal.x - this.x;
            let dy = this.goal.y - this.y;
            return p.sqrt(dx * dx + dy * dy);
        }
    }

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

        // Goal star in right section
        goal = { x: 340, y: 150 };

        // Create momentum particles (left side) - lots of them, moving fast
        for (let i = 0; i < 8; i++) {
            momentumParticles.push(
                new MomentumParticle(
                    p.random(40, 160),
                    p.random(80, 220)
                )
            );
        }

        // Create progress particle (right side) - single, slow, directed
        progressParticle = new ProgressParticle(220, 150, goal);
    };

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

        frame++;

        // Title
        p.fill(...colors.accent3, 200);
        p.noStroke();
        p.textAlign(p.CENTER);
        p.textSize(11);
        p.text('The Momentum Illusion', 200, 20);

        // Divider line
        p.stroke(...colors.accent3, 80);
        p.strokeWeight(1);
        p.line(200, 50, 200, 270);

        // LEFT SECTION: Momentum without direction
        p.fill(...colors.accent3, 180);
        p.noStroke();
        p.textSize(9);
        p.textAlign(p.CENTER);
        p.text('High Momentum', 100, 45);
        p.textSize(7);
        p.fill(...colors.accent3, 140);
        p.text('(Lots of motion, no direction)', 100, 55);

        // Update and display momentum particles
        for (let particle of momentumParticles) {
            particle.update();
            particle.display(colors);
        }

        // RIGHT SECTION: Progress toward goal
        p.fill(...colors.accent3, 180);
        p.noStroke();
        p.textSize(9);
        p.textAlign(p.CENTER);
        p.text('Intentional Progress', 300, 45);
        p.textSize(7);
        p.fill(...colors.accent3, 140);
        p.text('(Slower, but toward a goal)', 300, 55);

        // Draw goal
        p.noStroke();
        p.fill(...colors.accent3, 200);

        // Draw star for goal
        p.push();
        p.translate(goal.x, goal.y);
        p.rotate(frame * 0.02);
        p.beginShape();
        for (let i = 0; i < 5; i++) {
            let angle = (i * p.TWO_PI) / 5 - p.HALF_PI;
            let x = p.cos(angle) * 10;
            let y = p.sin(angle) * 10;
            p.vertex(x, y);
            angle += p.PI / 5;
            x = p.cos(angle) * 5;
            y = p.sin(angle) * 5;
            p.vertex(x, y);
        }
        p.endShape(p.CLOSE);
        p.pop();

        p.textSize(7);
        p.fill(...colors.accent3, 140);
        p.text('GOAL', goal.x, goal.y + 20);

        // Update and display progress particle
        progressParticle.update();
        progressParticle.display(colors);

        // Show distance to goal
        let distance = progressParticle.distanceToGoal();
        p.textSize(8);
        p.fill(...colors.accent1, 180);
        p.text(`Distance: ${Math.round(distance)}`, 300, 270);

        // Bottom insight text
        p.fill(...colors.accent3, 180);
        p.noStroke();
        p.textSize(7);
        p.textAlign(p.CENTER);

        if (frame < 200) {
            p.text('Activity without direction is just exhaustion with no destination.', 200, 290);
        } else if (frame < 400) {
            p.text('The fastest path to your goal: Stop moving. Check direction. Then move deliberately.', 200, 290);
        } else {
            p.text('Progress beats momentum. Slow and aimed beats fast and random.', 200, 290);
        }

        // Reset if progress particle reaches goal
        if (distance < 10) {
            progressParticle = new ProgressParticle(220, 150, goal);
        }

        // Labels for motion vs progress
        if (frame % 120 < 60) {
            p.textSize(6);
            p.fill(...colors.accent2, 140);
            p.textAlign(p.CENTER);
            p.text('Busy, but going nowhere', 100, 265);

            p.fill(...colors.accent1, 140);
            p.text('Slower, but getting there', 300, 260);
        }
    };
};