🧑‍🚀 Bonus Stage! RocketMan Showcase

Same rocket, but with pulsing rocket flames. But wait! Who's peeking out the porthole?!

📂 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
💡 What actually changed? Two new functions drive everything new in this stage:

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.
🎭 The Canvas 2D Clip Path

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.
📈 Smooth Motion with 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()
💡 Why escape p5.js for the clip? p5.js does not expose a built-in clipping function. But p5.js draws on a regular HTML <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

rocketManStg2.html HTML5
Loading rocketManStg2.html…
sketches/rocketManStg2Sketch.js JavaScript
Loading rocketManStg2Sketch.js…
styles/rocketMan2Styles.css CSS
Loading rocketMan2Styles.css…

💪 Try It Yourself

Use the Copy button above to grab any file, then:

  1. Create a new project folder with sketches/ and styles/ sub-folders.
  2. Paste each file into VS Code and save it with the matching filename.
  3. Open rocketManStg2.html in your browser — wait 5 seconds for the astronaut to peek!
  4. Change ASTRO_CYCLE from 300 to 120. How does the timing feel? What does it mean for user experience when an animation repeats too quickly?
  5. Flame challenge: try drawFlame1(2) instead of drawFlame2(25). What is different about the motion? Which feels more like a real rocket engine and why?
  6. 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.
  7. easeInOut challenge: in getAstronautY(), replace the easeInOut(t) call with just t. 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.