Where ideas percolate and thoughts brew

The Tyranny of the Perfect Moment

About This Sketch

Float around randomly

This sketch accompanies the blog post "The Tyranny of the Perfect Moment" and visualizes its core concepts through generative art.

Algorithm

Float around randomly This sketch was originally created as a visual companion to the blog post "The Tyranny of the Perfect Moment" and explores its themes through generative art.

Pseudocode

SETUP:
  Initialize canvas (400x300)
  Set up drawing parameters

DRAW (every frame):
  Get current theme colors
  Clear background
  Draw generative visualization
  Update animation state

Source Code

let sketch = function(p) {

    let ideas = [];
    let capturedIdeas = [];
    let time = 0;

    class Idea {
        constructor() {
            this.x = p.random(50, 350);
            this.y = p.random(50, 150);
            this.vx = p.random(-1, 1);
            this.vy = p.random(-1, 1);
            this.brightness = 255; // starts bright
            this.fadeRate = 1; // fades over time if not captured
            this.size = p.random(8, 15);
            this.captured = false;
            this.captureY = null;
            this.wobble = p.random(p.TWO_PI);
        }

        update() {
            if (!this.captured) {
                // Float around randomly
                this.x += this.vx;
                this.y += this.vy;

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

                // Fade over time (inspiration fading)
                this.brightness -= this.fadeRate;

                // Check if near capture zone (mobile device area)
                if (this.y > 200 && this.brightness > 50) {
                    this.captured = true;
                    this.captureY = this.y;
                    capturedIdeas.push(this);
                }
            } else {
                // Once captured, settle into organized position
                this.y = p.lerp(this.y, 250, 0.05);
                this.brightness = 255; // stays bright when captured
            }
        }

        display(colors) {
            if (this.brightness <= 0) return; // completely faded

            let alpha = p.map(this.brightness, 0, 255, 0, 200);

            if (this.captured) {
                // Captured ideas are solid and stable
                p.fill(...colors.accent2, alpha);
                p.noStroke();
            } else {
                // Uncaptured ideas are fading
                let wobbleX = p.sin(time * 2 + this.wobble) * 2;
                let wobbleY = p.cos(time * 2 + this.wobble) * 2;
                p.fill(...colors.accent1, alpha);
                p.noStroke();
                p.ellipse(this.x + wobbleX, this.y + wobbleY, this.size, this.size);
                return;
            }

            p.ellipse(this.x, this.y, this.size, this.size);
        }

        isDead() {
            return !this.captured && this.brightness <= 0;
        }
    }

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

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

        // Draw the "ideal workspace" zone (top)
        p.fill(...colors.accent3, 30);
        p.noStroke();
        p.rect(0, 0, 400, 180);
        p.fill(...colors.accent3, 150);
        p.textAlign(p.CENTER, p.TOP);
        p.textSize(10);
        p.text("'I'll capture this when I get to my desk...'", 200, 10);

        // Draw the capture zone (mobile device at bottom)
        p.fill(...colors.accent2, 50);
        p.rect(0, 200, 400, 2);
        p.fill(...colors.accent3, 150);
        p.textAlign(p.CENTER, p.BOTTOM);
        p.textSize(10);
        p.text("Capture zone: Create now, wherever you are", 200, 290);

        // Draw captured ideas count
        p.textAlign(p.LEFT, p.TOP);
        p.textSize(9);
        p.fill(...colors.accent2, 200);
        p.text("Captured: " + capturedIdeas.length, 10, 265);

        // Spawn new ideas occasionally
        if (p.frameCount % 90 === 0 && ideas.length < 12) {
            ideas.push(new Idea());
        }

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

            // Remove dead (faded) ideas
            if (ideas[i].isDead()) {
                ideas.splice(i, 1);
            }
        }

        // Visual indicator: ideas fade (get dimmer) as they wait
        p.textAlign(p.RIGHT, p.TOP);
        p.textSize(8);
        p.fill(...colors.accent3, 130);
        p.text("Ideas fade while you wait", 390, 170);

        time += 0.02;
    };
};