🛰 Stage 3: Source Code Showcase

Same orbit. Cleaner code. The ORB pattern — three habits that make sketches readable, predictable, and ready to grow.

📂 The Stage 3 Files

Stage 3 takes the Stage 2 orbit — which already looked great — and reorganizes the code without changing a single pixel of output. The goal: apply the ORB pattern so the sketch is easier to read, easier to debug, and ready for the SketchWave object model in future stages. Three files carry all the logic:

✍️ Student file (you edit this one)
sketches/jacksonOrbit3Sketch.js — All constants, animation state, and drawing functions live here. initMyDesign() populates stars and land masses once. drawMyDesign(ctx) is the per-frame entry point.
⚙️ Template file (mostly leave this alone)
sketches/jacksonOrbitBase3Sketch.js — Handles setup() and draw(), creates the canvas, wires the control panel, and calls your hooks (initThemeColor, initMyDesign, drawMyDesign) at the right moments.
File Who touches it Stage 3 changes
jacksonOrbit3.html No one — just loads scripts in order Script tags updated to Stage 3 sketch files
sketches/jacksonOrbit3Sketch.js Student (Jackson) ORB refactor: named constants block, two-phase draw, initMyDesign() hook
sketches/jacksonOrbitBase3Sketch.js Template (minimal changes) Star-field loop moved out; now calls initMyDesign() instead
🤖 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()).
jacksonOrbit3.html — running live inside an <iframe>

💡 The ORB Pattern

Stage 3 introduces three coding habits called the ORB pattern. The orbit looks identical to Stage 2 — only the internal structure changes. Together these three habits are a stepping stone toward the SketchWave object model.

O — Organize: Named Constants at the Top

Stage 2 was already much better than Stage 1, but it still had inline fractions scattered throughout the drawing functions: width * 0.25, angle * 0.30, d * 0.12. When you see 0.30 in the middle of drawMoon() you have to mentally trace “what is 30% of?” every single time.

The fix: hoist every meaningful fraction to a named constant at the top of the file, grouped under clear comment banners. The drawing functions then read like a mission-control panel.

❌ Stage 2 — inline fractions ✅ Stage 3 — named constants
drawEarth(width * 0.25);
translate(width * 0.4, 0);
rotate(angle * 0.3);
let numLands = 15;
const EARTH_SCALE = 0.25;
const MOON_ORBIT_RADIUS = 0.40;
const MOON_ORBIT_FACTOR = 0.30;
const LAND_COUNT = 15;

… then in draw:
drawEarth(width * EARTH_SCALE);
translate(width * MOON_ORBIT_RADIUS, 0);
rotate(angle * MOON_ORBIT_FACTOR);

The payoff: tuning the animation means changing one number in the constants block at the top. No hunting through a dozen function bodies. The constant name also documents what the number means, for free.

R — Render, Then Update: the Two-Phase Draw Loop

In Stage 2, drawMyDesign() called drawing functions and advanced state (angle += 0.01, star.x -= star.speed) interleaved inside the same helper functions. This makes it hard to reason about what the frame looks like at any given moment.

The fix: split drawMyDesign() into two explicit phases. Phase 1 renders. Phase 2 updates state. Every Phase-1 function sees a consistent “snapshot” of the world for the entire frame.

❌ Mixed render + update ✅ Two explicit phases
// inside drawStars():
ellipse(star.x, star.y, star.size);
star.x -= star.speed; // state mutation!
if (star.x < 0) star.x = width;

// at end of drawMyDesign():
angle += ORBIT_SPEED;
function drawMyDesign(ctx) {

  // Phase 1: Render (back → front)
  drawStars();
  drawEarth(width * EARTH_SCALE);
  drawMoon(width * MOON_SCALE);
  drawStation();

  // Phase 2: Advance state
  earthRotation += EARTH_ROTATION_SPEED;
  angle += ORBIT_SPEED;
}

Why it matters: when every draw function is pure read (it reads state but never writes it), you can move them around, comment one out, or add a new layer anywhere in Phase 1 without worrying about accidentally changing the animation speed or position.

Notice that in Stage 3, drawStars() still moves stars (star.x -= star.speed) inside the loop for simplicity. The R principle is applied at the top level of drawMyDesign, where it has the most impact. Perfection at every level is a future stage.

B — Bootstrap: One-Time Init in initMyDesign()

In Stage 2, the star-field was populated inside setup() of the base template file. The land-mass algorithm was called from inside drawEarth() using a per-frame guard (if (landMasses.length === 0) {…}). Both approaches scatter initialization logic away from the data it initializes.

The fix: add one new hook — initMyDesign() — in the user sketch file. The base template calls it once from setup() after the canvas is ready. The result: all one-time work lives next to the data it sets up.

❌ Guard in a draw function ✅ One-time hook called from setup()
// inside drawEarth() — runs every frame!
if (landMasses.length === 0) {
  initLandMasses(r);
}

// stars built in base template's setup():
for (let i = 0; i < 500; i++) {
  stars.push({…});
}
// in jacksonOrbit3Sketch.js:
function initMyDesign() {
  for (let i = 0; i < STAR_COUNT; i++) {
    stars.push({…});
  }
  const r = (width * EARTH_SCALE) / 2;
  initLandMasses(r);
}

// in jacksonOrbitBase3Sketch.js setup():
if (typeof initMyDesign === 'function')
  initMyDesign();

The bonus: the Reset button can now fully restart the animation by clearing the arrays and calling initMyDesign() again. With a per-frame guard that was impossible — the guard would just fire on the very next frame.

📁 Why Two Sketch Files?

You may have noticed that Stage 3 loads two JavaScript files before running the sketch:

  1. sketches/jacksonOrbit3Sketch.js — loaded first
  2. sketches/jacksonOrbitBase3Sketch.js — loaded second

The load order matters. The base template reads constants declared in the user sketch (ULx, ULy, fr, DEFAULT_BG_HEX…) during its own initialization. If the base template loaded first, those constants would be undefined and the canvas would break.

The separation of concerns: the student file is a creative workspace — constants, colors, drawing functions. The base template is infrastructure — canvas creation, control-panel wiring, the p5.js setup() and draw() lifecycle. Students can do everything they need in one file and never touch the other.

This pattern — a user file plus a framework file — is exactly the direction SketchWave is heading. In a future stage, the “base template” role will be absorbed by the SketchWave class library and individual shape classes (SWDisk, SWColor, etc.).

📜 Source Code

sketches/jacksonOrbit3Sketch.js JavaScript
Loading jacksonOrbit3Sketch.js…
sketches/jacksonOrbitBase3Sketch.js Template JS
Loading jacksonOrbitBase3Sketch.js…
jacksonOrbit3.html HTML5
Loading jacksonOrbit3.html…

💪 Try It Yourself

Use the Copy buttons above to grab the student sketch file (jacksonOrbit3Sketch.js), then try these ORB-focused challenges:

  1. O — Tune the Moon orbit — find MOON_ORBIT_RADIUS in the constants block at the top of jacksonOrbit3Sketch.js. Change it from 0.40 to 0.55 and reload. Notice how the Moon jumps outward without you touching a single draw function. That’s the whole point of named constants.
  2. O — Speed up the station — find ORBIT_SPEED and change it from 0.01 to 0.04. The Moon automatically speeds up too (it uses angle * MOON_ORBIT_FACTOR). Why? Because they share the same angle state.
  3. R — Comment out Phase 2 — in drawMyDesign(), comment out both earthRotation += EARTH_ROTATION_SPEED; and angle += ORBIT_SPEED; and reload. The scene renders perfectly but nothing moves. This proves that Phase 1 (render) and Phase 2 (update) are truly independent.
  4. B — Change the star count — find STAR_COUNT = 500 and change it to 1500. Reload and press Start. Notice how the Reset button regenerates all 1500 stars — because initMyDesign() is called from the Reset handler, not just from setup().
  5. B — Break the load order — in jacksonOrbit3.html, swap the two <script> tags so the base template loads before the user sketch. Reload. What error appears in the browser console? Swap them back. What does this tell you about why load order matters in a two-file template design?

⚠️ 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.