📂 The Stage 3 Files
Stage 3 adds a single new idea on top of Stage 2: the star field scrolls downward each frame, creating the illusion that the rocket is flying upward. The rocket itself never moves. Three files make up the Stage 3 build:
| File | What it does | Stage 3 changes |
|---|---|---|
rocketStg3.html |
HTML shell — loads p5.js, CSS, and the sketch | Title updated; stylesheet and sketch pointers updated |
rocketStg3Sketch.js |
The p5.js sketch — all drawing logic | One new constant (STAR_SPEED); drawStars()
now advances and wraps each star’s y |
rocket3Styles.css |
Minimal CSS reset — canvas flush, no margin | Unchanged from Stage 2 (intentionally minimal) |
drawStars():
s.y += STAR_SPEED;if (s.y > CANVAS_H) { s.y = 0; s.x = random(CANVAS_W); }
That’s it. One line moves the star. One line wraps it when it exits the canvas. Everything else — the rocket, the colors, the constants — is identical to Stage 2.
The rocket never moves. It is drawn at exactly the same coordinates every frame. Only the star field moves — downward at
STAR_SPEED pixels per frame.The brain interprets “background moving down” as “foreground (rocket) moving up.” This is the same trick used in side-scrolling arcade games since the 1980s, and in cinematic backgrounds in film long before that.
Wrapping loop: when a star’s
y exceeds CANVAS_H, it resets to
y = 0 with a fresh random x. This makes
the star field appear infinite — the rocket is always
“flying through” new space, never running out of stars.
In Stage 2, each star object was created once in
setup() and never changed. The
stars array was pure read-only data.In Stage 3, each star’s
y
property is updated every single frame. The array is now
mutable state — it changes over time.Mutable state is not wrong; it’s often necessary. But it comes with a cost: the program is harder to reason about, because an object’s value depends on when you look at it, not just where it was created. In larger programs, uncontrolled mutable state is one of the most common sources of bugs. Stage 3 is a gentle introduction to that trade-off.
⚡ Performance: Stage 1 vs Stage 3
Stage 3 does more than Stage 1 — the stars actually move — yet it is measurably more efficient. The difference is entirely in how the stars are handled each frame.
| Operation | Stage 1 — per frame | Stage 3 — per frame |
|---|---|---|
random() calls (stars) |
300 — x, y, and size × 100 stars | 100 — alpha only; size is fixed |
| Object allocations | 100 new objects created and discarded | 0 — array created once in setup() |
| Garbage collection | Yes — 100 abandoned objects per frame | None — no objects abandoned |
| Per-star update work | 3 × random() calls |
1 × random() + 1 × += + 1 × if |
| Star positions | Fully re-randomized — random drift as a side-effect | Controlled scroll — intentional motion only |
| Rocket drawing | Identical — same 7 shapes, same coordinates | |
random() calls per second
for stars alone, and allocates then immediately discards
6,000 objects per second.
The JavaScript engine's garbage collector has to silently clean that up
between frames. Stage 3 makes 6,000 random() calls per
second (alpha only) and allocates zero new objects.
The lesson: Stage 1's star approach is one of the least efficient patterns possible in any animation loop — allocate, draw, discard, repeat. Stage 3 demonstrates the standard game-programming alternative: allocate once, mutate cheaply, reuse forever.
📜 Source Code
Loading rocketStg3.html…
Loading rocketStg3Sketch.js…
Loading rocket3Styles.css…
💪 Try It Yourself
Use the Copy button above to grab any file, then:
- Create a new project folder with
sketches/andstyles/sub-folders. - Paste each file into VS Code and save it with the matching filename.
- Open
rocketStg3.htmlin your browser — watch the stars scroll! -
Change
STAR_SPEEDfrom1to3. How does the illusion feel different? At what speed does it start to feel unrealistic? -
Parallax depth challenge: give each star a
speedproperty ingenerateStars()set torandom(0.5, 2.5). Then changes.y += STAR_SPEEDtos.y += s.speed. Larger stars should probably move faster. Why? -
Separation-of-concerns challenge: move the star
position update into its own
updateStars()function, and call it fromdraw()beforedrawStars(). How does this change the structure? Is it worth it at this scale?
⚠️ The Copy button requires a secure context
(https:// or localhost). If you’re viewing this file directly
from your file system (file://), right-click the
Run link above and choose “View Page Source” instead.