Where ideas percolate and thoughts brew

The Resolution Theater

About This Sketch

A New Year's Eve visualization contrasting performative goal announcements with actual work. Watch as hollow announcement particles rise and quickly fade (theater without substance), then switch to solid work particles that slowly accumulate at the top (quiet, consistent effort that builds over time). The sketch alternates between these two modes, illustrating how public declarations feel productive but dissipate, while private work may be less dramatic but creates lasting results.

Algorithm

This sketch visualizes the contrast between performative goal announcements and actual work through two particle systems that alternate every 4 seconds. During the "Announcements" phase, hollow circles rise from the bottom and quickly fade away, representing the fleeting nature of public declarations that feel good but don't last. During the "Work" phase, solid circles rise more slowly and accumulate at the top of the canvas, representing how quiet, consistent effort builds over time. The sketch uses theme-aware colors and creates a clear visual metaphor: announcements are numerous but empty and temporary, while work is fewer particles but substantial and persistent.

Pseudocode

SETUP:
  Create canvas (400x300)
  Initialize 50 announcement particles
  Set cycle start time

DRAW (every frame):
  Get current theme colors
  Clear background
  Calculate cycle position (8 second cycles)

  IF in first half of cycle:
    Show announcement particles
    - Hollow circles (empty performance)
    - Rise and fade quickly
    - Reset when faded or off-screen
    - Display label: "Announcements: Loud but fleeting"

  IF in second half of cycle:
    Show work particles
    - Solid circles (actual substance)
    - Rise slowly and decelerate
    - Accumulate at top third of canvas
    - Stay visible (persistent results)
    - Display label: "Work: Quiet but accumulating"

VISUAL METAPHOR:
  Announcements = many, hollow, fleeting
  Work = fewer, solid, accumulating

Source Code

let sketch = function(p) {
    let particles = [];
    let showAnnouncements = true;
    let fadeStartTime = 0;
    let cycleDuration = 8000; // 8 seconds per cycle

    class Particle {
        constructor(isAnnouncement) {
            this.isAnnouncement = isAnnouncement;
            this.reset();
        }

        reset() {
            this.x = p.random(p.width);
            this.y = p.height + 20;
            this.size = p.random(3, 8);
            this.speedY = p.random(-2, -0.5);
            this.opacity = 255;
            this.lifespan = p.random(100, 200);
            this.age = 0;
        }

        update() {
            this.y += this.speedY;
            this.age++;

            // Announcements fade out quickly (theater - no substance)
            if (this.isAnnouncement) {
                this.opacity = p.map(this.age, 0, this.lifespan, 255, 0);
                if (this.age > this.lifespan || this.y < -20) {
                    this.reset();
                }
            } else {
                // Work particles persist (substance over performance)
                this.speedY *= 0.98; // Slow down
                if (this.y < p.height * 0.3) {
                    this.speedY = 0; // Stop at top
                    this.opacity = 255; // Stay visible
                }

                if (this.y < -20) {
                    this.reset();
                }
            }
        }

        display() {
            const colors = getThemeColors();

            if (this.isAnnouncement) {
                // Hollow circles - empty performance
                p.noFill();
                p.stroke(...colors.accent1, this.opacity);
                p.strokeWeight(1);
                p.circle(this.x, this.y, this.size);
            } else {
                // Solid circles - actual work
                p.noStroke();
                p.fill(...colors.accent2, this.opacity);
                p.circle(this.x, this.y, this.size);
            }
        }
    }

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

        // Start with announcements
        for (let i = 0; i < 50; i++) {
            particles.push(new Particle(true));
        }

        fadeStartTime = p.millis();
    };

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

        let currentTime = p.millis();
        let cyclePosition = (currentTime - fadeStartTime) % cycleDuration;

        // First half: show announcements (theater)
        // Second half: switch to work (substance)
        if (cyclePosition < cycleDuration / 2) {
            if (!showAnnouncements) {
                // Switch to announcements
                showAnnouncements = true;
                particles = [];
                for (let i = 0; i < 50; i++) {
                    particles.push(new Particle(true));
                }
            }
        } else {
            if (showAnnouncements) {
                // Switch to work
                showAnnouncements = false;
                particles = [];
                for (let i = 0; i < 30; i++) {
                    particles.push(new Particle(false));
                }
            }
        }

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

        // Label
        p.fill(...colors.accent3);
        p.noStroke();
        p.textAlign(p.CENTER, p.CENTER);
        p.textSize(12);
        if (showAnnouncements) {
            p.text("Announcements: Loud but fleeting", p.width/2, 20);
        } else {
            p.text("Work: Quiet but accumulating", p.width/2, 20);
        }
    };
};