An offline sun & moon engine for Swift and TypeScript

Where the Sun and Moon are, when they rise and set, how the Moon is lit, and when the Earth's shadow crosses it. No network, no data files to ship, no API key — just geometry, in your app or on your boat. The sky below is live: it is this library running in your browser right now.

Key Features

Offline by construction

Zero network calls and zero runtime data files. Everything on this page is computed in your browser from pure geometry — the same code runs on a boat with no signal.

Twin ports, one behavior

TypeScript and Swift implementations validated against a shared corpus of JPL Horizons, USNO and Espenak fixtures. Neither port is the other's oracle.

Sun, Moon and eclipses

Positions, rise and set, all three twilights, transit, moon phase and illumination, and lunar eclipses with contact times and local visibility.

Held to a tolerance

Rise and set times agree with USNO to within a minute across 1950–2100, and eclipse peaks match the Espenak catalog to roughly 15 seconds.

Using Almanac

TypeScript

npm install @openwaters/almanac
import { sunEvents, nextLunarEclipse, lunarEclipseVisibility } from '@openwaters/almanac';

const observer = { latitudeDeg: 48.5, longitudeDeg: -123.0 };

// Sunrise, sunset, twilights and transit for the next 24 hours
const now = new Date();
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
for (const { kind, time } of sunEvents(now, tomorrow, observer)) {
  console.log(kind, time.toISOString());
}

// The next lunar eclipse, and whether you'll see it
const eclipse = nextLunarEclipse(now);
const visibility = lunarEclipseVisibility(eclipse, observer);
console.log(eclipse.kind, eclipse.peak, visibility.visibleAtPeak);

Swift

.package(url: "https://github.com/openwatersio/almanac.git", from: "0.1.0")
import Almanac
import Foundation

let observer = try Observer(latitudeDeg: 48.5, longitudeDeg: -123.0)

let now = Date()
let tomorrow = now.addingTimeInterval(24 * 60 * 60)
for event in try sunEvents(from: now, to: tomorrow, observer: observer) {
  print(event.kind, event.time)
}

let eclipse = try nextLunarEclipse(after: now)
let visibility = try lunarEclipseVisibility(eclipse, observer: observer)
print(eclipse.kind, eclipse.peak, visibility.visibleAtPeak)

Supported interval: 1950-01-01 through 2100-12-31. Where the library is going next is written down in the roadmap.

Free & Open Source

Almanac is MIT licensed, with its astronomy translated from Don Cross's Astronomy Engine. Use it, contribute to it, or fork it.