Concept
I wanted to build something that felt alive but also structured, and the lunar cycle is a great mix of both. The project simulates the eight phases of the moon, from New Moon to Waning Crescent, with randomized textures (spots) and stars to make each redraw unique. The idea is that every click reveals a new “night sky,” giving the viewer a sense of time passing without relying on animation loops.
How It Works
-
Moon.js defines a
Moon
class. Each moon has a position, size, and current phase index. The phase controls how much of the circle is lit and how much is in shadow. -
The surface spots are stored in an array of objects, each with its own position, size, and shade of gray. This randomness makes the moon look organic rather than like a flat white circle.
-
I also made sure spots only show up in the “visible” lit portion of the moon, so the shadows aren’t just cosmetic.
-
Sketch.js handles the overall scene: the canvas, the star field (also an array of little objects), and the interaction. Every mouse click moves the moon forward by one phase, regenerates its surface spots, and refreshes the stars.
What I’m Proud Of
The hardest and most satisfying part was getting crescents and gibbous phases to look right. p5.js doesn’t have a neat intersection()
or shape-clipping tool for arcs, so I had to improvise using combinations of ellipses, arcs. It was like solving a puzzle with the wrong pieces, but in the end it worked.
Another “aha” moment came with the order of drawing. For example, if I drew the stars last, they’d sit awkwardly on top of the moon. If I drew spots before clipping, they’d spill into the shadows. It’s such a small detail, but the sequence of draw calls makes or breaks the illusion.
Aesthetic Choices
-
The moon is a soft gray (230) while shadows are much darker, to keep things simple but still distinguishable.
-
Stars are randomized each time you click, which makes the scene feel less static.
-
The phase name and “day” number are displayed in monospace above the moon to mimic an old-school astronomy chart.
Problems Along the Way
-
Figuring out crescents and gibbous shapes took the most trial-and-error. I tried a bunch of naive arc overlaps before landing on my ellipse-mask workaround.
-
Getting the spots to fit entirely inside the circle (without spilling over the edge) was trickier than expected; I had to add a distance check plus a little buffer zone.