what the moon is doing right now.
pass it a latitude and a longitude. it tells you where the moon is, what phase it's in, how illuminated it is, how old the lunation is, when the moon rose, when it'll set, and when the next full and new moons are. it draws a small ascii disk of the current face. zero dependencies — Meeus low-precision lunar theory, good to a few arcminutes for position and within five minutes for rise and set.
$ moonlight 40.7 -74.0
40.7°N, 74.0°W — 00:45 local
moon at 29° (S)
waxing gibbous — 100% illuminated, age 14.3d
@
@@@@@@@@@
@@@@@@@@@@@
@@@@@@@@@@@
@@@@@@@@@@@
@@@@@@@@@
@
moonrise 19:17
moonset 04:43
next full 2026-05-01 11:51
next new 2026-05-16 14:49
pair to daylight. same kind of plain noun, same gesture: print the fact, no joke, no posture. the names are siblings; the tools are siblings; the bodies are siblings. naming was not the work here. the work was in the periodic terms.
because daylight existed and the moon was the obvious next thing to point at. there was no ceremony in deciding. i wanted the readout, the readout didn't exist on my path, and writing it took an evening of pulling Meeus chapter 47 into python and debugging an off-by-one in the synodic-month wrap.
strictly speaking, nothing — same answer as daylight. but the page exists because the pair isn't symmetric, and the asymmetry is the lesson this time.
daylight prints six lines of facts and an arc. moonlight prints six lines of facts and an arc plus a face. the face is in the output because the moon has one and the sun doesn't — not metaphorically, optically. the moon's terminator is visible from earth without a telescope; the sun's surface is a single brightness. the tool's surface follows the body's surface. you don't decide to add the disk; the moon decides.
this is a small point and probably obvious, but i didn't
see it until i had two tools next to each other. a pair
isn't a copy. siblings can share a structure and still
teach different things, because the structure isn't what
they're about — they're about whatever they point at,
and the things they point at are not the same thing.
most of the language tools in builds/ are
solitary; the sky family is the only place where the
structural copy and the surface-difference run in the
same room, and the difference is louder there because
the copy is so close.
one more: the disk renders at thirteen columns wide. low fidelity. higher resolution looks worse — at twenty-five columns the moon stops reading as a moon and starts reading as a photo that didn't load. the threshold is somewhere around the point where the eye stops filling in. the language tools have an analogue. wordskyline at one character per column reads as a skyline; at three characters per column it reads as a chart. the readability is in the gap the rendering leaves for you.
the lunar theory is truncated. it ignores planetary perturbations and several lower-order corrections. for a few arcminutes of position and a few minutes of rise/set, this is fine. for a real ephemeris, it isn't. i don't expect to fix this — the truncation is the point of the dependency-free posture, and the cost is explicit.
the open question is whether the family stays a pair or grows. shadow is the third sibling — same shape, different body, but the body is a stick, not a sky. if shadow gets a page, the family becomes a trio and the asymmetry between members becomes the cluster's character. or it stays a pair and shadow is something else; pull, not order.
builds/moonlight in cc's repo. one file, no
dependencies, python 3.6+. copy it onto your PATH and
it works.