Where ideas percolate and thoughts brew

Ripple Effect

About This Sketch

Concentric circles expand outward like ripples on water, fading as they grow. This simple but mesmerizing effect captures the physics of wave propagation—energy spreading outward from a point source.

The overlapping ripples create beautiful interference patterns where they meet. The periodic spawning ensures the animation continues indefinitely with fresh ripples appearing as old ones fade away.

Algorithm

Expanding circular ripples that fade as they grow, like water droplets falling into a pond. Multiple ripples can coexist and overlap, creating interference-like patterns. **Key Concepts:** - **Expanding Circles**: Radius increases linearly over time - **Concentric Rings**: Multiple circles at different radii - **Opacity Fadeout**: Alpha decreases with distance from center - **Lifecycle Management**: Remove ripples when they exceed max radius - **Temporal Spawning**: New ripples appear periodically **How it works:** 1. Maintain array of active ripples, each with position and radius 2. Each frame, increase each ripple's radius by its speed 3. Draw multiple concentric circles from center outward 4. Inner circles have higher opacity (more visible) 5. When ripple exceeds max radius, remove from array 6. Periodically spawn new ripples at random positions 7. Color transitions as ripple expands

Pseudocode

CLASS Ripple:
  PROPERTIES:
    x, y = position
    radius = 0
    maxRadius = random(80, 150)
    speed = random(1, 2)

SETUP:
  CREATE 3 initial ripples at random positions

DRAW (every frame):
  CLEAR background

  FOR EACH ripple (backwards):
    radius = radius + speed

    IF radius > maxRadius:
      REMOVE ripple
      CONTINUE

    FOR r = radius DOWN TO 0 STEP 15:
      opacity = map(r, 0, radius, 255, 50)
      color = gradient based on radius/maxRadius

      DRAW circle at (x, y) with radius r

  IF frameCount % 60 == 0 AND ripples.count < 5:
    ADD new ripple at random position

Source Code

let sketch = function(p) {
    let ripples = [];

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

        // Start with a few ripples
        for (let i = 0; i < 3; i++) {
            ripples.push({
                x: p.random(p.width),
                y: p.random(p.height),
                radius: 0,
                maxRadius: p.random(80, 150),
                speed: p.random(0.3, 0.6)
            });
        }
    };

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

        // Update and draw ripples
        for (let i = ripples.length - 1; i >= 0; i--) {
            let ripple = ripples[i];

            ripple.radius += ripple.speed;

            if (ripple.radius > ripple.maxRadius) {
                ripples.splice(i, 1);
                continue;
            }

            // Draw multiple concentric circles
            for (let r = ripple.radius; r > 0; r -= 15) {
                let alpha = p.map(r, 0, ripple.radius, 255, 50);
                let t = (ripple.radius / ripple.maxRadius);
                let col = t < 0.5 ?
                    colors.accent1.map((c, idx) => p.lerp(c, colors.accent2[idx], t * 2)) :
                    colors.accent2.map((c, idx) => p.lerp(c, colors.accent3[idx], (t - 0.5) * 2));

                p.noFill();
                p.stroke(...col, alpha);
                p.strokeWeight(2);
                p.circle(ripple.x, ripple.y, r * 2);
            }
        }

        // Occasionally add new ripple
        if (p.frameCount % 60 === 0 && ripples.length < 5) {
            ripples.push({
                x: p.random(p.width),
                y: p.random(p.height),
                radius: 0,
                maxRadius: p.random(80, 150),
                speed: p.random(0.3, 0.6)
            });
        }
    };
};