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);
}
};
};