Where ideas percolate and thoughts brew

The Friction Solution

About This Sketch

A particle system where each particle experiences different levels of friction, demonstrating how resistance affects movement and persistence. Particles with higher friction must continuously apply force to maintain motion, while those with lower friction glide more easily. The visualization explores how friction isn't always the enemyβ€”it creates the conditions for meaningful effort and sustained growth.

Algorithm

This sketch visualizes the concept of friction through a particle system where each particle experiences different levels of resistance. Particles move through space with varying friction coefficients (0.95 to 0.99), causing some to maintain momentum while others slow down quickly. Higher-friction particles (shown in lighter tones) must work harder to maintain movement, continuously applying small random forces to counteract the resistance. Lower-friction particles glide more easily. The subtle horizontal waves represent "zones of resistance" - areas where friction would be higher in a physical system. The varying friction levels among particles demonstrate how different amounts of resistance affect movement and persistence. This sketch accompanies the blog post "The Friction Solution" and explores how resistance (friction) isn't always the enemy - it creates the conditions for meaningful effort and sustained growth. Just as particles with friction must continuously apply force to move, humans build capacity by pushing against resistance.

Pseudocode

SETUP:
  Initialize canvas (400x300)
  Create particle class with random friction values
  Spawn initial set of particles

PARTICLE CLASS:
  Properties:
    - position (x, y)
    - velocity (vx, vy)
    - age and maxAge (lifespan)
    - friction coefficient (0.95-0.99)
    - size

  Update:
    - Apply friction to velocity (multiply by friction coefficient)
    - Add small random force (continuous "effort")
    - Constrain maximum speed
    - Update position based on velocity
    - Bounce off canvas edges
    - Increment age

  Display:
    - Calculate alpha based on age (fade over time)
    - Choose color based on friction level
    - Draw motion trail (previous positions)
    - Draw particle circle

DRAW (every frame):
  Get current theme colors
  Clear background
  Periodically spawn new particles
  For each particle:
    - Update physics
    - Display with trail
    - Remove if past maxAge
  Draw resistance zone lines (sine waves)

Source Code

let sketch = function(p) {
    let particles = [];
    let time = 0;

    class Particle {
        constructor(x, y) {
            this.x = x;
            this.y = y;
            this.vx = p.random(-1, 1);
            this.vy = p.random(-1, 1);
            this.age = 0;
            this.maxAge = p.random(100, 300);
            this.size = p.random(2, 6);
            this.friction = p.random(0.95, 0.99); // Each particle has different friction
        }

        update() {
            // Apply friction differently to each particle
            this.vx *= this.friction;
            this.vy *= this.friction;

            // Add small random force (the "effort")
            this.vx += p.random(-0.1, 0.1);
            this.vy += p.random(-0.1, 0.1);

            // Constrain velocity
            let speed = p.sqrt(this.vx * this.vx + this.vy * this.vy);
            if (speed > 3) {
                this.vx = (this.vx / speed) * 3;
                this.vy = (this.vy / speed) * 3;
            }

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

            // Bounce off edges
            if (this.x < 0 || this.x > 400) this.vx *= -1;
            if (this.y < 0 || this.y > 300) this.vy *= -1;

            this.x = p.constrain(this.x, 0, 400);
            this.y = p.constrain(this.y, 0, 300);
        }

        display(colors) {
            let alpha = p.map(this.age, 0, this.maxAge, 255, 0);

            // Color based on friction level - high friction particles are lighter
            let frictionAmount = p.map(this.friction, 0.95, 0.99, 0, 1);
            let col = frictionAmount > 0.5 ? colors.accent1 : colors.accent2;

            p.noStroke();
            p.fill(col[0], col[1], col[2], alpha);

            // Draw trail
            for (let i = 1; i < 4; i++) {
                let trailX = this.x - this.vx * i;
                let trailY = this.y - this.vy * i;
                let trailAlpha = alpha * (1 - i / 4);
                p.fill(col[0], col[1], col[2], trailAlpha);
                p.circle(trailX, trailY, this.size * (1 - i / 5));
            }

            p.fill(col[0], col[1], col[2], alpha);
            p.circle(this.x, this.y, this.size);
        }

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

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

        // Initialize with some particles
        for (let i = 0; i < 30; i++) {
            particles.push(new Particle(p.random(400), p.random(300)));
        }
    };

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

        time++;

        // Add new particles periodically
        if (time % 10 === 0 && particles.length < 80) {
            particles.push(new Particle(p.random(400), p.random(300)));
        }

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

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

        // Draw some "resistance lines" - areas of higher friction
        p.stroke(...colors.accent3, 30);
        p.strokeWeight(1);
        for (let i = 0; i < 5; i++) {
            let y = 60 * i + 30;
            p.line(0, y + p.sin(time * 0.02 + i) * 10, 400, y + p.sin(time * 0.02 + i) * 10);
        }
    };
};