Back to Blog
·6 min read

Building swisseph-wasm: Astronomical Calculations in the Browser

WebAssemblyJavaScript

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.