Skip to content
AFM Studio
  • Pages

    AFM Studio
  • Work
  • Lab
  • About
  • CV
  • Contact
  • Projects

    AksaraA searchable library for Otorita IKN's knowledge, where the server checks every reader's access across three levels
  • Daily TaqwaTamper-resistant prayer attendance system for ~2,000 OIKN government employees at Masjid Negara IKN
  • Lantara v2A permit engine for Otorita IKN where administrators add new permit types from a screen, without a developer
  • eKiosk IKNTwo control rooms for Nusantara: kiosk content management and live bus tracking with arrival times that grade themselves
  • E-Monev IKNA monitoring platform for Nusantara's 23-year build, tracking every unit's quarterly progress against 24 national KPIs
  • JDIH OIKNOIKN's public legal document portal, with a controlled publishing workflow and AI summaries that never leave the server
  • Jejak PenanamanA check-in app for IKN tree-planting events that proves each participant was on site with a geofenced selfie
  • Dashboard SAKTIA nightly pipeline and dashboard that replaces OIKN's manual budget downloads for all six of its budget units
  • DoseRxA bedside dose calculator for 127 drugs that rounds each dose to tablets and syrups Indonesian pharmacies sell
  • CubiqA speedcubing trainer with competition-grade timing and a solver I wrote for each of the 8 major puzzles
  • ScimotionAn interactive science library of 86 articles, each explained next to animations the reader can control
  • WA Daily Scrum BotA WhatsApp bot that chases missing standup reports for a 14-person team and flags tasks stuck for days

↑ ↓ to move · Enter to open · Esc to close

Falak

A Hijri calendar, prayer time and qibla tool that shows the moon and sun numbers behind every answer

Screenshots
historical new moons matched against JPL
50
hosting, everything runs in the browser
$0
evenings where a bug flipped the date
28
points on the Indonesia visibility map
3,255
On this page

The problem

Every year in Indonesia, people wait to hear when Ramadan, Syawal and Dzulhijjah begin. Sometimes different organisations announce different dates. They use different calculation criteria (hisab) for whether the new crescent moon, the hilal, can be seen. The government's sidang isbat combines calculation with sighting reports, but the public only hears the verdict.

Existing Hijri date converters are either lookup tables with no astronomy behind them, or official tools that give an answer without showing why. I built Falak for Muslims who want to check the dates and prayer times themselves, for teachers of ilmu falak in madrasah and pesantren, and for developers who want an open implementation.

What I built

I compute the sun and moon myself

I implemented standard published models for the sun's and moon's positions directly, with no outside astronomy service. The engine needs no API key and works offline. Every number the app shows traces back to a formula in the repository. Each answer, such as whether the hilal is visible, when Maghrib falls or which way the qibla lies, comes with a panel showing the raw figures behind it: altitude, elongation, moon age and lag time.

Three methods side by side

I implemented three competing criteria for crescent visibility: Wujudul Hilal, MABIMS-2021 and Odeh. Each runs independently, and the Awal Bulan page's criterion history shows where they agree and where they split. The app doesn't announce dates; it shows what each method computes for a given evening and place. The Indonesia map checks 3,255 points across the country, and on some evenings the visibility line cuts through Sumatra and Java, so the same date starts a different Hijri day depending on where you stand.

I validated the engine before building any screens

My build plan had one rule: no calculation feature ships without validation. Before any interface work, I checked the engine against JPL's DE440 ephemeris, a research-grade reference, for 50 historical new moons. Every one matched within five minutes. The reference library runs only in tests, never in the app. The Kalender page compares each method with past Kemenag announcements, entered from cited records only.

The whole app moved into the browser

The first version had a Django backend. Every endpoint was a pure calculation, the database only cached results, and the engine used nothing beyond standard maths, so it didn't need a server. I ported the engine to TypeScript and published a static site on GitHub Pages. Hosting costs nothing, and a server outage can't break it. The 3,255-point map now spreads across up to eight background threads instead of running on one.

The original engine checks the port

Two copies of a religious-calendar engine will drift unless something stops them. I kept the Python engine as the reference and froze its answers for 2,083 test cases across every kind of calculation the app does. The build replays them through the TypeScript port and blocks the deploy on any mismatch. To prove the suite actually catches drift, I broke the port three ways on purpose, a flipped threshold, a wrong lunar coefficient, a tiny sidereal-time offset, and each one failed the suite.

Result

Falak is live and free, with $0 hosting, comparing 3 hisab criteria across an Indonesia-wide 3,255-point visibility grid, entirely in the browser. It covers Hijri conversion, hilal visibility, prayer times with a printable monthly schedule, and the qibla with Rashdul Qibla, across five destinations plus a home instrument. The original feature set took about three days in July 2026; a browser migration and a full Indonesian-first redesign followed over the months after. A validation sweep in September caught a bug that could have started some months a day early under one criterion, found across 28 historical evenings and now closed with regression tests. The engine still matches all 50 historical new moons it's checked against JPL's ephemeris.

Under the hoodTechnical detail for engineers
  • Solar positions use a truncated VSOP87 model; lunar positions use ELP2000 as given by Meeus. The Python engine imports only math and datetime.
  • Validation checks 50 historical lunar conjunctions (Jan/Jun 1st, anchored on the current date) against Skyfield with the JPL DE440 ephemeris, each within a 5-minute tolerance. Skyfield lives only in requirements-dev.txt.
  • Five models: Location, ConjunctionEvent, PrayerTimeConvention, VisibilityResult, IsbatRecord. A cache miss always recomputes from the engine. IsbatRecord is seeded from cited Kemenag press releases, never back-computed from the engine, to avoid circularity.
  • The grid is 0.5° steps. It began as the Celery task precompute_visibility_grid, then moved to Web Workers (one per core, capped at eight) with live progress.
  • The 10 original Django REST endpoints kept their names and response shapes when ported to browser functions. The Django backend stays in the repo purely as the validated oracle CI checks the browser port against, still exercised on every push touching backend/**, but it isn't deployed. The rejected alternative, Fly.io with web, worker, Postgres and Redis, was priced at about $6–11 a month.
  • A second bug, found the same way: the "ijtimak before sunset" check compared an evening against the most recently-passed conjunction instead of the target month's own one, so an evening just before a month's real ijtimak could wrongly pass. A sweep of Hijri years 1400–1470H across Jakarta, Banda Aceh and Jayapura found 28 affected evenings, all Wujudul Hilal, each starting a month a day early, e.g. Ramadan 1439H in Jakarta on 16 May 2018 instead of 17 May 2018. Both engines now check the target month's own conjunction; none of the 2,083 golden vectors covered an affected month, so none needed to change, and regression tests were added to both suites.
  • DESIGN.md v2, from September 2026, replaced nine single-purpose, English-named pages with five Indonesian-first destinations, Salat, Kiblat, Awal Bulan, Kalender and Belajar, plus a redesigned home page. The old paths still redirect client-side, so previously shared links keep working.
  • The Merauke bug: one grid point's moon altitude differed by 0.00000016 degrees from the reference while 362 others matched, traced to the port storing instants as float milliseconds instead of integer microseconds like Python's datetime. Near the 3° MABIMS threshold, that error could have flipped a verdict.
  • CI runs the Python suite with a Postgres service, regenerates the golden-vector fixture, compares it numerically at 1e-9 tolerance (macOS libm and glibc differ in the last digit of sin/cos/atan2), compares verdicts, dates and booleans exactly, then runs the Vitest conformance suite.
  • Map colours were run through a CVD and contrast validator. The old "criterion met" teal measured ΔE 12.8 against slate, below the floor of 15. New steps clear 21.1 (light) and 25.3 (dark), and "met" is a filled square while "not met" is a small dot.
  • The coastline is extracted once at build time from Natural Earth via world-atlas and topojson-client into a committed 111 KB GeoJSON. Prayer times resolve the timezone with tz-lookup; an early bug showed them in raw UTC.
  • The social preview card is a committed 1200×630 image, regenerated by hand rather than built at request time, because GitHub Pages serves Next's convention-based OG image as application/octet-stream, which most link unfurlers reject.
  • 115 backend tests (110 directly verified passing; the rest need a live Postgres role and are covered by the project's own CI) and 113 frontend tests across 9 files gate the deploy. The engine is 2,354 lines of Python and 2,889 lines of TypeScript across 17 modules. 169 commits by one author, 5 July to 27 September 2026.

Screenshots

Beranda
Beranda (2)
Shalat
Jadwal Sebulan Shalat
Kiblat
Awal Bulan

Hiring, or building something similar?

I'm open to full-time roles and select freelance work.