🛰 Stage 2: Source Code Showcase

Live code. Real improvements. Four big lessons every novice needs to know.

📂 The Stage 2 Files

Stage 2 takes Jackson's original, working sketch and improves it without changing how it looks. The result is code that is cleaner, safer, and ready to grow. Two files carry all the drawing logic:

File What it does Stage 2 changes
jacksonOrbit1.html HTML shell — loads p5js, SWClasses, and the sketch files Wrapped in the SW template; Bootstrap nav and hero added
sketches/jacksonOrbit1Sketch.js All of Jackson's drawing logic — stars, Earth, Moon, Station Magic numbers removed; canvas-relative sizing; push/pop orbits; organic land masses
🤖 How this page loads the code: This page uses the browser’s Fetch API to load each source file from the server at page-load time — the same technique websites use to pull JSON from an API. The advantage: this page always shows the live source. Edit a file on the server and the showcase updates automatically next visit. No copy-paste, no re-escaping HTML entities, just fetch(url).then(r => r.text()).
jacksonOrbit1.html — running live inside an <iframe>

💡 Four Big Lessons

Jackson's Stage 1 code was already impressive for a novice — it worked! But four common beginner habits made it fragile. Here’s what we fixed and why it matters.

🔮 Lesson 1 — No More Magic Numbers

A magic number is a raw numeric literal that appears in code without explanation — 800, 600, 0.3, 40. They are called “magic” because nobody remembers where the number came from or what happens if you change it.

Jackson's Stage 1 example:

❌ Stage 1 — magic numbers ✅ Stage 2 — named constants
createCanvas(800, 600);
for (let i = 0; i < 500; i++) {…}
translate(300, 0);
fill(10, 40, 120);
// canvas size set by template
for (let i = 0; i < 500; i++) {…} // 500 stars
translate(width * 0.4, 0); // 40% of canvas
fill(swDeepBlue.col); // named SWColor

The rule: if a number appears more than once, or if changing it would require a hunt through the file, it should be a named constant or tied to a reference value. Future-you will be grateful.

📏 Lesson 2 — Tie Sizes to the Canvas

Jackson's Stage 1 sketch created a 800×600 canvas and drew every object at fixed pixel coordinates. Resize the window and the illusion breaks — the Moon orbit extends beyond the edge, the station disappears, the Earth shrinks to a dot.

The fix: express every size as a fraction of canvas width/height.

❌ Fixed pixels ✅ Canvas-relative
drawEarth(200);
translate(300, 0);  // Moon orbit
rect(0, 0, 30, 10); // station core
drawEarth(width * 0.25);
translate(width * 0.4, 0);
let coreW = width * 0.05;
rect(0, 0, coreW, coreW * 0.33);

Now the whole scene scales proportionally: small canvas, small scene; large canvas, large scene. The template’s resize handler calls myResizeCanvas() automatically, so students get responsive sketches for free.

🔄 Lesson 3 — push/pop, rotate & translate for Orbits

Jackson’s original sketch handled orbits by calculating x = cx + r*cos(angle) and y = cy + r*sin(angle) manually. That works for simple circular paths, but it makes keeping things upright (like the space station) and offsetting objects from an orbit center very complicated.

Stage 2 uses p5.js’s transformation stack instead. Here is how the Moon orbit and the station upright correction both work:

Step What it does
push(); Save the current coordinate system so we can restore it later. Think of it as “bookmarking this spot.”
translate(width/2, height/2); Move the origin (0,0) to the centre of the canvas — Earth’s orbital centre.
rotate(angle * 0.3); Spin the coordinate system. Now “outward” means along the current angle.
translate(width * 0.4, 0); Step outward along the (now-rotated) x-axis. This is the orbit radius.
draw the Moon here Drawing at (0,0) now places the Moon at the right position in its orbit.
pop(); Restore the saved coordinate system. The Moon’s transform vanishes; the next object starts fresh.

Keeping the station upright adds one extra step: after rotate(angle) places us on the orbit, we apply rotate(-angle) to cancel that rotation for the station’s own coordinate frame. The net rotation on the station is zero — it always faces “up.” This trick is used in games, satellites, and camera rigs everywhere.

The golden rule: every push() must have a matching pop(). Missing one causes every subsequent object to inherit the orphaned transform — a notoriously tricky bug.

🌍 Lesson 4 — Better Land Masses: Polar Jitter vs Hard-Coded Vertices

Stage 1 defined each landmass as a fixed list of vertex() calls placed at hand-typed x, y pixel coordinates. Every continent was the same shape every time, placed at the same spot every time. Worse, the coordinates were relative to the screen, not to the Earth’s rotating surface, so they didn’t rotate with it.

Stage 2 generates land masses algorithmically, in three steps:

  1. Polar jitter blobs — each landmass is a closed polygon whose vertices are placed around a small circle at angle j * TWO_PI / numPts, then nudged outward/inward by a random multiplier. The result is an organic, irregular blob that looks like a real continent outline.
  2. Bounded placement — each blob’s centre is placed at a random distance from Earth’s centre, clamped so the farthest vertex cannot exceed Earth’s radius. No more landmasses spilling over the ocean edge.
  3. Stored in an array, drawn with the rotating frame — landmasses are generated once in initLandMasses(r) and stored in the landMasses array. They are drawn inside the rotate(earthRotation) block, so they orbit Earth’s axis naturally without any extra math.

The lesson: when a shape needs to be “random but constrained,” polar coordinates (angle + radius) are almost always the right tool. Cartesian (x, y) forces you to calculate boundaries manually; polar makes constraint trivial — just clamp the radius.

📜 Source Code

sketches/jacksonOrbit1Sketch.js JavaScript
Loading jacksonOrbit1Sketch.js…
jacksonOrbit1.html HTML5
Loading jacksonOrbit1.html…

💪 Try It Yourself

Use the Copy buttons above to grab either file, then try these challenges:

  1. Canvas resize test — open jacksonOrbit1.html in your browser, then resize the window. Notice how Earth, Moon, and the station all scale together. Now open origHTML.html (Stage 1) and do the same. Can you see the difference?
  2. Change the orbit speed — in jacksonOrbit1Sketch.js, find angle += 0.01; in drawMyDesign(). Change 0.01 to 0.05 and reload. How does it affect the Moon too? Why?
  3. Adjust the Moon's orbit radius — find translate(width * 0.4, 0) in drawMoon(). Change 0.4 to 0.6. What happens? What would happen if you used a fixed pixel value like 300 instead?
  4. Break and fix upright correction — in drawStation(), comment out rotate(+angle); and reload. Observe the station spinning. Now uncomment it. This is the “upright correction” from Lesson 3 — you just proved it works!
  5. Add more landmasses — find let numLands = 15; in initLandMasses(). Try 30 or 5. Notice you only need to change one number because of Lesson 1’s named variable.

⚠️ The Copy button requires a secure context (https:// or localhost). Viewing from a local file:// URL? Right-click the Run link and choose “View Page Source” instead.