📂 The RocketMan Stage 2 Files
RocketMan Stage 2 builds on Stage 3’s scrolling star field by
adding two new ideas: a sinusoidally pulsing flame
powered by drawFlame2(), and an astronaut who
periodically peeks out of the porthole and then slides back
inside. The peek effect uses the Canvas 2D clip path API
to confine drawing to the porthole circle, so the astronaut appears to
emerge from inside the rocket rather than floating over the hull.
Three files make up the build:
| File | What it does | New in this stage |
|---|---|---|
rocketManStg2.html |
HTML shell — loads p5.js, CSS, and the sketch | Title and stylesheet/sketch pointers updated |
rocketManStg2Sketch.js |
The p5.js sketch — all drawing logic | drawFlame2() for pulsing flame;
drawAstronaut() with
drawingContext.clip() for porthole masking;
easeInOut() for smooth rise/fall animation |
rocketMan2Styles.css |
CSS for the sketch page layout | Canvas border updated to coral red |
Pulsing flame —
drawFlame2(period)
uses Math.sin() to oscillate the flame tip length each
frame. A sine wave cycles smoothly between −1 and +1, so
multiplying it by a deltaDrop amplitude gives a flame
that breathes in and out naturally.Astronaut peek —
drawAstronaut()
calls drawingContext.save(), defines a circular clip
path with drawingContext.arc() and
drawingContext.clip(), draws the full astronaut figure
(suit → neck → head → eyes → grin),
then calls drawingContext.restore() to release the mask.
The porthole ring is redrawn after the restore so its stroke
always sits on top.
p5.js draws everything using the browser’s underlying Canvas 2D API. The
drawingContext
object exposes that API directly — including clipping.drawingContext.save() snapshots the current state.drawingContext.beginPath() starts a new path.drawingContext.arc(cx, cy, r, 0, Math.PI*2) traces a full circle.drawingContext.clip() restricts all subsequent drawing to that circle.drawingContext.restore() lifts the clip and restores state.Anything drawn between
clip() and restore()
is silently masked to the circle — exactly like how a
circular cookie-cutter works on dough. The astronaut slides in and
out of the porthole because only the part that overlaps the circle
is ever painted onto the canvas.
easeInOut()A simple
lerp(start, end, t) with a linearly
increasing t produces robotic, mechanical motion —
constant speed from start to finish.easeInOut(t) remaps that linear t
through a cosine curve: (1 - Math.cos(PI * t)) / 2.
The result starts slow, accelerates through the middle, then
decelerates back to slow — the same feel as a car pulling
away from a stop or a drawer sliding to a close.The astronaut rises, peeks, and descends on a
ASTRO_CYCLE = 300-frame clock (5 seconds at 60 fps).
Only the rise and descent phases use easing; the peek phase holds
still, so the astronaut lingers at the window before retreating.
🧠 Key Techniques: Sine Waves & Clip Paths
Two techniques drive the new animations in this stage. Neither requires any new p5.js functions — they use math and the Canvas 2D API that p5.js already sits on top of.
| Technique | What it does | Key code |
|---|---|---|
| Sinusoidal flame | Oscillates flame tip length smoothly each frame | FLAME_DROP + deltaDrop * Math.sin(B * frameCount) |
| Canvas clip path | Masks all drawing to the porthole circle | drawingContext.arc() + drawingContext.clip() |
| easeInOut curve | Gives the astronaut’s rise/fall natural acceleration | (1 - Math.cos(Math.PI * t)) / 2 |
| Phase-based timing | Drives the peek cycle without any extra state variables | let phase = frameCount % ASTRO_CYCLE |
| Layered drawing | Suit → neck → head drawn bottom-to-top so head sits on top | Draw order inside drawAstronaut() |
| Porthole ring on top | Porthole outline redrawn after clip release so it always overlaps the astronaut | drawingContext.restore() then ellipse() |
<canvas>, and drawingContext
is just that canvas’s 2D rendering context — always available,
fully standard. Using drawingContext directly is the correct
way to access Canvas 2D features that p5.js doesn’t wrap: clip paths,
composite operations, shadows, and more. The
save()/restore() pair is essential — without
it, the clip would remain active for every subsequent shape in the sketch.
📜 Source Code
Loading rocketManStg2.html…
Loading rocketManStg2Sketch.js…
Loading rocketMan2Styles.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
rocketManStg2.htmlin your browser — wait 5 seconds for the astronaut to peek! -
Change
ASTRO_CYCLEfrom300to120. How does the timing feel? What does it mean for user experience when an animation repeats too quickly? -
Flame challenge: try
drawFlame1(2)instead ofdrawFlame2(25). What is different about the motion? Which feels more like a real rocket engine and why? - Clip path challenge: give the astronaut sunglasses. Inside the clip region, after drawing the eyes, add two thin dark rectangles just above the eye circles. The clip will automatically keep them inside the porthole.
-
easeInOut challenge: in
getAstronautY(), replace theeaseInOut(t)call with justt. How does the motion change? Can you feel the difference between linear and eased interpolation?
⚠️ 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.