Where ideas percolate and thoughts brew

The Fresh Start Fallacy

About This Sketch

Algorithm

This sketch visualizes the compounding difference between starting immediately versus waiting for a "fresh start" temporal landmark (like New Year's Day). Two groups of progress dots are created: - "Start Now" group begins accumulating immediately - "Wait for Fresh Start" group delays until a predetermined landmark As time progresses, dots "activate" and float upward to represent accumulated progress. The sketch tracks and displays the growing gap between immediate action and delayed action. A timeline shows the current moment and the future "fresh start" landmark. Progress bars quantify the cumulative difference, demonstrating that starting now—even imperfectly—builds more real progress than waiting for ideal conditions.

Pseudocode

SETUP:
  Initialize canvas (400x300)
  Create 30 "start now" dots with immediate, staggered activation times
  Create 30 "wait for fresh start" dots with delayed activation (after landmark)
  Set fresh start landmark at frame 120

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

  Draw timeline with:
    - Current time marker (moving)
    - Fresh start landmark (fixed, until reached)

  For each dot group:
    Update activation status based on current time
    If activated, move upward (accumulate progress)
    Display with theme colors and glow effect
    Count active dots

  Display labels and progress metrics:
    - Group names ("Start Now" vs "Wait for Fresh Start")
    - Active dot counts
    - Progress bars showing cumulative difference
    - Dynamic insight when gap becomes significant

  Show how immediate action compounds while waiting postpones

Source Code

let sketch = function(p) {
    // Visualization of starting now vs waiting for fresh start
    // Two groups of progress bars:
    // "Start now" bars begin immediately and accumulate
    // "Wait for fresh start" bars wait for landmark, then start

    let time = 0;
    let freshStartTime = 120; // The "fresh start" moment
    let maxTime = 400;

    // Progress trackers
    let startNowProgress = 0;
    let waitForStartProgress = 0;

    // Visual elements
    let startNowDots = [];
    let waitForStartDots = [];

    class ProgressDot {
        constructor(x, baseY, delay) {
            this.x = x;
            this.baseY = baseY;
            this.y = baseY;
            this.delay = delay;
            this.born = false;
            this.age = 0;
            this.size = 4;
            this.targetY = baseY - 80;
        }

        update(currentTime) {
            if (currentTime >= this.delay && !this.born) {
                this.born = true;
            }

            if (this.born) {
                this.age++;
                // Float upward (representing progress/accumulation)
                if (this.y > this.targetY) {
                    this.y -= 0.5;
                }
            }
        }

        display(colors, color) {
            if (this.born) {
                let opacity = p.map(this.age, 0, 100, 100, 200);
                opacity = p.min(opacity, 200);
                p.fill(...colors[color], opacity);
                p.noStroke();
                p.circle(this.x, this.y, this.size);

                // Subtle glow
                p.fill(...colors[color], opacity * 0.3);
                p.circle(this.x, this.y, this.size * 1.5);
            }
        }
    }

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

        // Create dots for "start now" - begin immediately
        let startNowX = 100;
        let startNowY = 200;
        for (let i = 0; i < 30; i++) {
            startNowDots.push(new ProgressDot(
                startNowX + p.random(-20, 20),
                startNowY,
                i * 8 // Start immediately, spaced out
            ));
        }

        // Create dots for "wait for fresh start" - delay until landmark
        let waitX = 300;
        let waitY = 200;
        for (let i = 0; i < 30; i++) {
            waitForStartDots.push(new ProgressDot(
                waitX + p.random(-20, 20),
                waitY,
                freshStartTime + (i * 8) // Wait until fresh start
            ));
        }
    };

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

        // Title
        p.fill(...colors.accent3);
        p.noStroke();
        p.textAlign(p.CENTER);
        p.textSize(12);
        p.text('The Fresh Start Fallacy', 200, 20);

        // Subtitle
        p.textSize(7);
        p.fill(...colors.accent3, 180);
        p.text('Starting now accumulates. Waiting for fresh starts postpones.', 200, 32);

        // Timeline marker
        let timelineY = 240;
        p.stroke(...colors.accent3, 100);
        p.strokeWeight(1);
        p.line(50, timelineY, 350, timelineY);

        // Current time marker
        let timeX = p.map(time, 0, maxTime, 50, 350);
        timeX = p.min(timeX, 350);
        p.stroke(...colors.accent3, 200);
        p.strokeWeight(2);
        p.line(timeX, timelineY - 5, timeX, timelineY + 5);

        // Fresh start landmark
        if (time < freshStartTime) {
            let landmarkX = p.map(freshStartTime, 0, maxTime, 50, 350);
            p.stroke(...colors.accent2, 150);
            p.strokeWeight(1);
            p.line(landmarkX, timelineY - 10, landmarkX, timelineY + 10);
            p.noStroke();
            p.fill(...colors.accent2, 180);
            p.textSize(7);
            p.text('Fresh Start', landmarkX, timelineY + 18);
        }

        // Update and display "start now" dots
        let startNowActive = 0;
        for (let dot of startNowDots) {
            dot.update(time);
            dot.display(colors, 'accent1');
            if (dot.born) startNowActive++;
        }

        // Update and display "wait for start" dots
        let waitActive = 0;
        for (let dot of waitForStartDots) {
            dot.update(time);
            dot.display(colors, 'accent2');
            if (dot.born) waitActive++;
        }

        // Labels
        p.textAlign(p.CENTER);
        p.textSize(8);

        // Start now label
        p.fill(...colors.accent1, 200);
        p.text('Start Now', 100, 215);
        p.textSize(6);
        p.fill(...colors.accent3, 150);
        p.text(`Progress: ${startNowActive}`, 100, 224);

        // Wait for start label
        p.fill(...colors.accent2, 200);
        p.textSize(8);
        p.text('Wait for Fresh Start', 300, 215);
        p.textSize(6);
        p.fill(...colors.accent3, 150);

        if (time < freshStartTime) {
            p.text('Waiting...', 300, 224);
        } else {
            p.text(`Progress: ${waitActive}`, 300, 224);
        }

        // Progress bars
        let barY = 260;
        let barWidth = 100;
        let barHeight = 8;

        // Start now bar
        let startNowFill = p.map(startNowActive, 0, 30, 0, barWidth);
        p.fill(...colors.accent3, 80);
        p.noStroke();
        p.rect(50, barY, barWidth, barHeight, 4);
        p.fill(...colors.accent1, 200);
        p.rect(50, barY, startNowFill, barHeight, 4);

        // Wait for start bar
        let waitFill = p.map(waitActive, 0, 30, 0, barWidth);
        p.fill(...colors.accent3, 80);
        p.rect(250, barY, barWidth, barHeight, 4);
        p.fill(...colors.accent2, 200);
        p.rect(250, barY, waitFill, barHeight, 4);

        // Dynamic insight
        if (time > 250) {
            let progressDiff = startNowActive - waitActive;
            if (progressDiff > 8) {
                p.fill(...colors.accent1, 200);
                p.textAlign(p.CENTER);
                p.textSize(7);
                p.text(`Starting immediately built ${progressDiff} more progress`, 200, 50);
            }
        }

        // "Now" marker
        p.fill(...colors.accent3);
        p.textSize(6);
        p.textAlign(p.CENTER);
        p.text('Now', timeX, timelineY - 10);
    };
};