💣 Stage 2 Laundry List

Same bomb. Dramatically better JavaScript.

🔥 The S.P.A.R.K. That Lit This Fuse

🤖  Prompt given to GitHub Copilot (Claude Sonnet 4.6) — 2026-05-01

“Claude, you are a professional ‘coding mentor’ for a high school student, and you want them to be marketable in the tech/coding world upon high school graduation. Assuming they studied a previous app, origRocket.html and its affiliated files and internalized the laundry list suggestions from that app’s laundry list, what are the top 3 suggestions you’d make for elevating the origBomb app (without worrying about styles or SWClass incorporation (yet)). For reference, please summarize the main checklist ideas from the attached laundry list (it would be cool if you could provide a rocket or bomb-themed acronym!) and then provide your specific recommendations for this current app, origBomb.html and its associated files. Mimic the type of styles you used in the prior laundry list, and include this prompt so users can ‘see’ how we communicate!. Call this new file laundryListBombStage2.html Feel free to link back to Griffin’s rocket app if that helps.”

🧺 What Is a Laundry List?

A laundry list is a planning tool — a checklist of specific improvements to make before you start typing. It prevents “spaghetti sessions” where you try to fix five things at once and accidentally break everything.

The non-negotiable rule for Stage 2: when we’re done, the bomb must look exactly the same as Stage 1. Clouds in the same spots. Bomb body unchanged. Particles still explode on click. The only thing changing is how the code is written and organized. This is called refactoring.

If you haven’t already read Griffin’s Rocket Saga — Stage 2 Laundry List, do that first. That page walks through each improvement with full before/after examples. This page builds on those ideas, so you’ll want them internalized.

🚀 The ROCKETS Recap

The Rocket Saga’s Stage 2 laundry list covered 7 improvements. Use the acronym ROCKETS to remember them. (Yes, it’s fitting that the rocket gave us the framework we now apply everywhere else.)

🚀 Improvement Key Idea
R Read me first Add a professional file header (who wrote it, when, what it does)
O Only named constants, no magic numbers Replace every bare literal with a descriptive const
C Color palette up front Group all colors as named arrays at the top of the file
K Keep draw() thin Decompose draw() into named helper functions (table of contents)
E Early setup = stable data Pre-generate arrays/objects in setup(), not in draw()
T Tagged sections Add // === section divider comments as visual landmarks
S State independence Each function explicitly sets its own fill/stroke state — no silent inheritance

🔗 Full before/after examples for every letter above: Griffin’s Rocket Stage 2 Laundry List

💣 The Bomb Brings Something New

The bomb sketch has all the same problems the rocket had — no file header, magic numbers everywhere, a monolithic draw(), and drawing state that bleeds silently between shapes. All of those need the full ROCKETS treatment.

But the bomb also introduces two things the rocket sketch didn’t have:

  • A JavaScript class (Particle) — already great object-oriented thinking! But the class itself is packed with hardcoded “magic numbers” (velocities, gravity, size ranges, fade rates) that have no names and no explanation. The named-constants discipline from ROCKETS needs to extend inside the class.
  • Dead code — variables and settings that were declared with clear intent but were never actually used. They leave false trails for any future reader (including future-you, six weeks from now).

The top 3 improvements for the bomb form their own acronym: TNT. Of course they do.

💥 The TNT Improvements

💣 Improvement Key Concept Visual Change?
T Tackle the full ROCKETS checklist Header + constants + colors + decomposition + state ownership None
N Name the magic numbers inside Particle Named constants extend into classes too None
T Terminate dead code Remove zombie variables & zombie configuration None
T Tackle the Full ROCKETS Checklist

Every letter of ROCKETS applies to origBombSketch.js. The most visible payoff comes from Keeping draw() thin: the original is a single 50+ line block that mixes clouds, bomb geometry, clock-face details, and particle updates all in one place. There is no visual structure at all.

After decomposition, draw() becomes a four-line table of contents. Anyone can read the structure of the entire sketch in under five seconds.

BEFORE — one 50+ line draw() with no visual structure
function draw() {
  background(135, 206, 235);

  noStroke();
  fill(255);

  // Draw a cluster of circles to form one cloud
  circle(50, 100, 50);
  circle(80, 100, 70);
  circle(110, 100, 50);
  circle(80, 80, 50);

  circle(330, 200, 50);
  circle(360, 200, 70);
  circle(390, 200, 50);
  circle(360, 180, 50);

  fill(10, 0, 0);
  stroke(0);
  rect(110, 100, 180, 180)       // <-- also: missing semicolon!
  triangle(200, 100, 330, 330, 70, 330);
  circle(200, 100, 180.3);

  stroke(100);
  strokeWeight(4);
  line(200, 260, 200, 330);
  line(105, 260, 65, 333);
  line(295, 260, 336, 333);

  fill(255, 255, 0);
  noStroke();
  circle(200, 100, 100);
  // ... clock face triangles, inner circles, stroke ring ...
  // ... 20 more lines ...

  for (let i = particles.length - 1; i >= 0; i--) {
    particles[i].update();
    particles[i].show();
    if (particles[i].finished()) {
      particles.splice(i, 1);
    }
  }
}
AFTER — draw() is a 4-line table of contents
// ── Main draw loop ────────────────────────────────────────────────────────
// draw() is called by p5 at FRAME_RATE times per second.
// Its only job is to call helpers in the right order (back to front,
// like layers in Photoshop). All the real work is in the helpers below.
function draw() {
  drawBackground();      // paint the sky (erases last frame)
  drawClouds();          // two fluffy clouds
  drawBomb();            // full bomb: body, clock face, fuse lines
  updateParticles();     // advance and render the explosion particles
}
AFTER — sample helper function with constants and explicit state
// ── Canvas / Frame ──────────────────────────────────────────────────────────
const CANVAS_W   = 400;
const CANVAS_H   = 400;
const FRAME_RATE =  60;

// ── Bomb geometry ────────────────────────────────────────────────────────────
// The outer circle defines the bomb. Every other measurement is derived
// from these three values, so repositioning the bomb means changing
// BOMB_CX and BOMB_CY — nothing else.
const BOMB_CX  = 200;        // horizontal center of the bomb
const BOMB_CY  = 100;        // vertical center of the bomb (top of canvas area)
const BOMB_D   = 180;        // outer bomb diameter  (original used 180.3)
const BOMB_R   = BOMB_D / 2; // radius = 90 — derived, not a new magic number

// The background rect that gives the bomb a "solid" look
const BOX_X = BOMB_CX - BOMB_R;  // 110  (left edge)
const BOX_Y = BOMB_CY;            // 100  (top edge)
const BOX_W = BOMB_D;             // 180  (same as bomb diameter — square box)
const BOX_H = BOMB_D;             // 180

// ── Clock face ───────────────────────────────────────────────────────────────
const CLOCK_D      = 100;   // outer yellow circle diameter
const CLOCK_RING_D =  90;   // inner yellow stroke ring
const CLOCK_DOT_D  =  20;   // small yellow center dot
const CLOCK_NUB_D  =  15;   // tiny black center nub
const CLOCK_RING_W =  10;   // stroke weight of the ring

// ── Fuse lines ───────────────────────────────────────────────────────────────
const FUSE_TOP_Y   = 260;   // where all three fuse lines start (y)
const FUSE_C_X     = 200;   // center fuse — x stays at BOMB_CX
const FUSE_BOT_Y   = 330;   // straight fuse bottom tip
const FUSE_L_TOP_X = 105;   // left fuse top x
const FUSE_L_END_X =  65;   // left fuse bottom x
const FUSE_L_END_Y = 333;   // left fuse bottom y
const FUSE_R_TOP_X = 295;   // right fuse top x
const FUSE_R_END_X = 336;   // right fuse bottom x
const FUSE_R_END_Y = 333;   // right fuse bottom y
const FUSE_WEIGHT  =   4;   // fuse stroke weight

// ── Color palette ────────────────────────────────────────────────────────────
// Each entry is [r, g, b].  Use with fill(...CLR_NAME) or fill(...CLR_NAME, a)
const CLR_SKY        = [135, 206, 235];  // sky blue background
const CLR_CLOUD      = [255, 255, 255];  // white clouds
const CLR_BOMB       = [ 10,   0,   0];  // near-black bomb body (tiny red tint)
const CLR_OUTLINE    = [  0,   0,   0];  // black outlines
const CLR_FUSE       = [100, 100, 100];  // medium gray fuse lines
const CLR_CLOCK_FACE = [255, 255,   0];  // bright yellow clock face
const CLR_CLOCK_HAND = [  0,   0,   0];  // black clock hands


// ── drawBackground ────────────────────────────────────────────────────────────
function drawBackground() {
  background(...CLR_SKY);    // p5 spread operator trick from the Rocket Saga
}


// ── drawClouds ────────────────────────────────────────────────────────────────
// Each cloud is a cluster of overlapping circles.
// noStroke() is declared here explicitly — never rely on inherited state.
function drawClouds() {
  noStroke();
  fill(...CLR_CLOUD);

  // Cloud 1 — upper left
  circle( 50, 100, 50);
  circle( 80, 100, 70);
  circle(110, 100, 50);
  circle( 80,  80, 50);

  // Cloud 2 — right side
  circle(330, 200, 50);
  circle(360, 200, 70);
  circle(390, 200, 50);
  circle(360, 180, 50);
}


// ── drawBomb ──────────────────────────────────────────────────────────────────
// Orchestrates all bomb sub-parts in back-to-front drawing order.
function drawBomb() {
  drawBombBody();
  drawFuseLines();
  drawClockFace();
}
🧑‍🏫 Teacher’s note: Count the lines in draw() after the refactor: four. Compare that to the original 50+. Anyone reading the refactored version — even without knowing p5.js — understands the sketch in seconds: sky, clouds, bomb, particles. When a bug shows up in the clock face, you open drawClockFace() — you don’t hunt through 50 entangled lines. Also notice that the missing semicolon after rect(110, 100, 180, 180) in the original would have been immediately obvious inside a short, focused function — another free benefit of decomposition.
N Name the Magic Numbers Inside Particle

The Particle class is the most sophisticated code in the sketch — and it came with a comment that says “I had some AI help with this part.” That’s completely fine! Learning when and how to use AI assistance is itself a professional skill. But the next step is to understand and own the code you accepted.

Right now the class is full of bare literals: random(5, 20), 0.15, 0.95, random(4, 120), 4, and 150 in mousePressed(). What does 0.95 mean? What does 4 do? A reader has to trace the physics mentally before understanding anything.

Named constants make the physics readable — and make it trivially easy to tune the explosion later without hunting through the class internals.

BEFORE — Particle class with unexplained magic numbers
class Particle {
  constructor(x, y) {
    this.pos = createVector(x, y);
    this.vel = p5.Vector.random2D().mult(random(5, 20));  // ← what range?
    this.acc = createVector(0, 0.15);                     // ← what is 0.15?
    this.alpha = 255;
    this.size = random(4, 120);                           // ← why 4 to 120?
    this.color = color(random(200, 255), random(0, 100), 0);
  }

  update() {
    this.vel.mult(0.95);   // ← 0.95 is "air drag" — but you'd never know
    this.vel.add(this.acc);
    this.pos.add(this.vel);
    this.alpha -= 4;       // ← fade speed — but why 4?
  }
  // ...
}

function mousePressed() {
  for (let i = 0; i < 150; i++) {     // ← why 150? is that adjustable?
    particles.push(new Particle(mouseX, mouseY));
  }
}
AFTER — named constants expose the physics vocabulary
// ── Particle configuration ────────────────────────────────────────────────────
// Keeping all tuning knobs together means tweaking the explosion is a
// one-section edit. Change a number here, and the behavior updates everywhere.

const PARTICLE_COUNT     = 150;    // sparks spawned per mouse click
const PARTICLE_SPEED_MIN =   5;    // slowest spark launch speed (px/frame)
const PARTICLE_SPEED_MAX =  20;    // fastest spark launch speed (px/frame)
const PARTICLE_GRAVITY   = 0.15;   // downward acceleration per frame
const PARTICLE_DRAG      = 0.95;   // speed multiplier per frame (air resistance)
const PARTICLE_ALPHA_DEC =   4;    // how fast each spark fades  (per frame, out of 255)
const PARTICLE_SIZE_MIN  =   4;    // smallest spark diameter (px)
const PARTICLE_SIZE_MAX  = 120;    // largest  spark diameter (px)
const PARTICLE_RED_MIN   = 200;    // minimum red channel — keeps sparks in the red-orange range
const PARTICLE_RED_MAX   = 255;    // maximum red channel
const PARTICLE_GRN_MAX   = 100;    // max green — raising this pushes sparks toward yellow


class Particle {
  constructor(x, y) {
    this.pos   = createVector(x, y);
    this.vel   = p5.Vector.random2D().mult(random(PARTICLE_SPEED_MIN, PARTICLE_SPEED_MAX));
    this.acc   = createVector(0, PARTICLE_GRAVITY);
    this.alpha = 255;
    this.size  = random(PARTICLE_SIZE_MIN, PARTICLE_SIZE_MAX);
    this.color = color(random(PARTICLE_RED_MIN, PARTICLE_RED_MAX), random(0, PARTICLE_GRN_MAX), 0);
  }

  update() {
    this.vel.mult(PARTICLE_DRAG);   // drag slows the spark each frame
    this.vel.add(this.acc);         // gravity accelerates it downward
    this.pos.add(this.vel);         // advance position
    this.alpha -= PARTICLE_ALPHA_DEC;
  }
  // ...
}

function mousePressed() {
  for (let i = 0; i < PARTICLE_COUNT; i++) {
    particles.push(new Particle(mouseX, mouseY));
  }
}
🧑‍🏫 Teacher’s note: Notice the comment on PARTICLE_GRN_MAX: “raising this pushes sparks toward yellow.” Before the refactor, discovering that relationship required mentally simulating the RGB values. After, it’s a single sentence. That’s the real power of named constants — they document the intent and the effect, not just the value. This principle works just as well inside a class as it does at the top of a file. Constants belong wherever the magic numbers are — not just at the global level.
T Terminate Dead Code

Dead code is code that exists in a file but has no effect on the running program. It ranges from harmless to actively misleading. In origBombSketch.js there are three examples, each telling a slightly different story about how dead code gets created.

Dead code #1 — a zombie variable

let angle = 0; is declared at the very top of the file but never read or updated anywhere. It’s a zombie variable: it was born with a purpose (perhaps the student planned to animate the clock hands) but that plan was abandoned. The variable still takes up a name in the file and silently confuses every reader who asks: “where is angle used?”

Dead code #2 — zombie configuration

angleMode(DEGREES); in setup() sets p5 to use degrees instead of radians. The comment even explains why: “Using degrees is easier for 120-degree spacing.” But no angle calculations exist anywhere in the sketch. The clock hands are all drawn as hardcoded triangle() coordinates, not with rotation. The comment describes an intention that was never implemented. This is aspirational code — the plan lived, but the code to execute it never arrived.

Dead code #3 — a redundant call

In draw(), immediately before drawing the clock ring: fill(0); is called, then on the very next line it is overridden by noFill();. The fill(0) has zero effect — it sets a color that is immediately discarded. This is the subtlest kind of dead code: a leftover from an edit that was partially completed.

BEFORE — three examples of dead code, each with a different origin story
let angle = 0;          // declared here, never used anywhere — zombie variable
let particles = [];

function setup() {
  createCanvas(400, 400);
  angleMode(DEGREES); // Using degrees is easier for 120-degree spacing
  // ↑ the intent was real; the implementation never arrived — zombie config
}

// ... inside draw(), drawing the clock ring ...
fill(0);        // ← sets black fill
noFill();       // ← immediately cancels the fill — fill(0) is dead
stroke('yellow');
strokeWeight(10);
circle(200, 100, 90);
AFTER — clean, every line earns its place
// let angle = 0;  ← removed: never used.
//                   If rotation is added in a future stage, re-introduce it then.
let particles = [];

function setup() {
  createCanvas(CANVAS_W, CANVAS_H);
  frameRate(FRAME_RATE);
  // angleMode(DEGREES) removed: no angles are calculated in this sketch.
  // If you add rotation later (e.g., spinning clock hands), add it back then.
}

// ... inside drawClockFace(), drawing the ring ...
noFill();                    // explicitly no fill — the ring is stroked only
stroke(...CLR_CLOCK_FACE);   // yellow ring
strokeWeight(CLOCK_RING_W);
circle(BOMB_CX, BOMB_CY, CLOCK_RING_D);
🧑‍🏫 Teacher’s note: Notice the comments in the “after” code above: “If rotation is added later, re-introduce it then.” Dead code is not removed and forgotten — it’s removed with a note about why it was there and when it would be appropriate to bring it back. This is the professional habit: your git history and your comments together tell the full story. Deleting code is not the same as losing the idea. A clean file with a good comment is better than a cluttered file with buried intentions.

One more bonus: removing angleMode(DEGREES) also removes a subtle trap for Stage 3 and beyond. When SketchWaveJS classes start using p5’s trigonometric functions, they will expect the default radian mode. A “zombie” degrees setting left in the file could cause hard-to-trace rotation bugs much later.

✅ Stage 2 Checklist

💣 Task Key Concept Visual Change?
🚀 Apply the ROCKETS checklist to origBombSketch.js
R Add a file header block Documentation & professionalism None
O Replace all magic numbers with named constants Readability & maintainability None
C Give all colors meaningful names Intent clarity & easy retheme later None
K Break draw() into helper functions Decomposition & separation of concerns None
E Move any one-time setup into setup() Performance & the setup/draw contract None
T Add section divider comments Navigation in larger files None
S Each function explicitly sets its drawing state Encapsulation & bug prevention None
💣 Bomb-specific additions (TNT)
N Extract named constants from Particle class Constants belong inside classes too None
T Remove let angle = 0 (never used) Dead code elimination None
T Remove angleMode(DEGREES) (intention without implementation) Dead code / zombie config None
T Remove redundant fill(0) before noFill() Dead code / leftover from partial edit None

🔭 What Comes After Stage 2?

Once the code is clean and well-organized, we’ll be ready to introduce SketchWaveJS classes and apply site-wide styles. That’s Stage 3 and beyond.

Think about what Stage 3 will look like after this refactor: replacing drawBombBody() with an SW class call is a one-function edit. Compare that to hunting through 50 tangled lines looking for the right circle() call without breaking the clock face coordinates.

The Particle class is also Stage 3 – ready the moment it has named constants. Down the road, it could become an SWParticle class that inherits from a SketchWave base class — a clean migration because the class already has clear, single-purpose methods and named configuration.

That’s the payoff of refactoring: every future change gets easier. The effort invested in Stage 2 is never wasted — it’s the foundation that everything else is built on.