npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@verifyhash/moon-phase

v0.1.0

Published

Zero-dependency lunar phase & illumination math (simplified Meeus method): phase angle, illuminated fraction, synodic age, canonical phase name, and Brown lunation number for any UTC instant.

Readme

moon-phase

TODO (owner): pick the final npm name/scope before publishing. The npm package name is not finalized by this project. Convention used here: package.json keeps the working slug moon-phase as a placeholder — replace it (and the npm install line below) with the real published name/scope before running npm publish.

A zero-dependency, pure-function Node library that turns any instant into the Moon's phase: its phase angle, the fraction of the disk that is lit, the synodic age in days, the canonical phase name, and the Brown lunation number.

No network, no external dependencies, no servers, no I/O. Every export is a pure function of a single Date and never mutates its argument. require() it and go.

const moon = require('./index.js');

const d = new Date('2024-01-25T17:54:00Z'); // a full moon
moon.phaseName(d);           // 'Full'
moon.illuminatedFraction(d); // ~1.0
moon.moonAge(d);             // ~14.75  (days since the last new moon)
moon.moonPhaseAngle(d);      // ~179.85 (Sun-Moon elongation, degrees)
moon.lunation(d);            // 1250    (Brown Lunation Number)

Who it's for

Developers who need the Moon's phase without pulling in a heavyweight ephemeris:

  • Weather / almanac apps — show tonight's phase and % illumination next to the forecast.
  • Astronomy & astrophotography planners — a dark-sky (new moon) or full-moon window finder, or a "how washed-out will the sky be" estimate.
  • Tide and fishing tools — spring/neap tides track the new/full syzygy, and the phase angle is a cheap proxy for it.
  • Calendar / UI widgets — a correct moon glyph for any date.

It is a geocentric phase model: it answers "what phase is the Moon in?", not "when does the Moon rise here?" or "where is it in my sky?". For observer-local rise/set/altitude you want a different tool (e.g. a full ephemeris).

API

All functions take a JS Date interpreted as a UTC instant (date and time). The time of day matters: the Moon moves ~12°/day, so one hour shifts the elongation by ~0.5°. A date-only value like new Date('2024-01-11') is treated as 00:00 UTC that day.

| Function | Returns | | --- | --- | | moonPhaseAngle(date) | Sun-Moon elongation in degrees, [0, 360). 0° = New, 90° = First Quarter, 180° = Full, 270° = Last Quarter. | | illuminatedFraction(date) | Fraction of the disk lit, [0, 1]. 0 at new, 1 at full, ~0.5 at the quarters. | | moonAge(date) | Days since the last new moon (synodic age), [0, ~29.53). | | phaseName(date) | One of the 8 canonical phases: New, Waxing Crescent, First Quarter, Waxing Gibbous, Full, Waning Gibbous, Last Quarter, Waning Crescent. | | lunation(date) | Brown Lunation Number (integer) of the lunation in progress. |

phaseName bins the elongation into eight 45°-wide sectors, each centred on its defining angle — so New covers 337.5°–360°–22.5°, First Quarter covers 67.5°–112.5°, and so on. The narrow "instant" phases (New, the two quarters, Full) are the sectors straddling their exact angles.

Algorithm & citation

The geometry follows Jean Meeus, Astronomical Algorithms (2nd ed., 1998):

  1. Evaluate the Moon's mean elongation D, the Sun's mean anomaly M, and the Moon's mean anomaly M′ from the standard polynomials in Julian centuries T from J2000.0 (Meeus ch. 47).
  2. Form the true Sun-Moon elongation by adding the leading periodic terms of the phase-angle series (Meeus ch. 48, eq. 48.4 — the published "low accuracy" method), giving the phase angle i and elongation 180° − i.
  3. Illuminated fraction k = (1 + cos i) / 2 (Meeus eq. 48.1).

The synodic month is taken as 29.530588853 days (Meeus ch. 47). The Brown lunation number is anchored to the new moon of 2000-01-06 18:14 UTC (Meeus lunation index k = 0), whose Brown Lunation Number is 953; Brown lunation 1 was the new moon of 1923-01-17.

Accuracy — honest limits

This is a simplified model. It keeps only the largest periodic terms and no planetary perturbations, so:

  • Phase angle / elongation: within about 0.5° of the full Meeus solution for dates within a couple of centuries of J2000.
  • Illuminated fraction: within about 1% — a few percent near a thin crescent, where a 0.5° angle error has the most leverage.
  • Synodic age (moonAge): derived from the true elongation via the mean synodic month, so it can drift up to roughly 0.5 day from a true almanac age, because the Moon's angular speed varies over its ~27.5-day anomalistic (perigee/apogee) cycle. Treat it as good to a fraction of a day, not to the minute.
  • No topocentric / parallax correction: this is the geocentric phase, which is what "the phase of the Moon" almost always means.

If you need to-the-minute event times (exact new/full moon instants) or a sky-position ephemeris, use a full-precision library instead. This one is for "what phase, how lit, how old, what's it called" — fast, dependency-free, and accurate enough for UI, planning, and forecasting overlays.

Tests

Run the full suite (a tiny built-in assert harness — no framework, no deps):

node test/moon.test.js

It exits non-zero on any failure. Fixtures use published new/full/quarter-moon instants (NASA GSFC phase tables / timeanddate.com), and the suite also checks that illumination increases monotonically across the waxing half-lunation, that the phase-name bins are ordered and complete over a lunation, that inputs are not mutated, and that invalid input throws. Stated tolerances: 1.5° on the phase angle at a documented event, 0.02 on the illuminated fraction at the quarters, and 0.15 day on the synodic age.

License

MIT.

Install

Placeholder name: the moon-phase below is the working slug, not a finalized npm name — see the owner TODO near the top of this README.

npm install moon-phase
const moon = require('moon-phase');

const d = new Date('2024-01-25T17:54:00Z'); // a full moon
moon.phaseName(d);           // 'Full'
moon.illuminatedFraction(d); // ~1.0
moon.lunation(d);            // 1250 (Brown Lunation Number)