Building swisseph-wasm: Astronomical Calculations in the Browser
WebAssembly opened a path to run Swiss Ephemeris — a battle-tested C library used in professional astronomy — directly in the browser without server round-trips.
The problem
Nepali calendar applications need sub-arcminute precision for tithi, nakshatra, and planetary positions. JavaScript date libraries were not built for astronomical math. Server-side calculation works but adds latency and infrastructure cost for client-heavy calendar tools.
Bridging C and JavaScript
The challenge was bridging the C memory model with JavaScript’s garbage-collected runtime. I used Emscripten to compile the core ephemeris functions and exposed a clean TypeScript API on top.
emcc swisseph/src/*.c -O3 -s WASM=1 \
-s EXPORTED_FUNCTIONS='["_calc_ut","_swe_julday"]' \
-s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]' \
-o swisseph.js
The TypeScript wrapper hides pointer management from consumers:
export function calculatePlanetPosition(
julianDay: number,
planet: Planet,
): PlanetPosition {
const result = module._calc_ut(julianDay, planet, FLAG_SWIEPH)
return {
longitude: result.longitude,
latitude: result.latitude,
distance: result.distance,
}
}
Memory management lessons
WASM modules do not participate in JS garbage collection. Every malloc needs a matching free, or you leak memory on every calculation. I wrapped allocations in scoped helpers:
function withBuffer<T>(size: number, fn: (ptr: number) => T): T {
const ptr = module._malloc(size)
try {
return fn(ptr)
} finally {
module._free(ptr)
}
}
Results
Today the library powers Nepali panchanga tools and other calendar applications that need professional-grade ephemeris data in the browser. Calculations that previously required a server round-trip now run in milliseconds client-side.
The project is open source at github.com/prolaxu/swisseph-wasm.