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

Zero Shadow Day

Finds the two days a year the sun stands overhead anywhere in Indonesia, with dates checked against BMKG

Screenshots
official BMKG dates matched exactly
75/76
Indonesian cities with built-in coordinates
86
network requests at runtime
0
On this page

The problem

Indonesia straddles the equator, so every place in the country gets two days a year when the sun passes straight overhead at noon. On those "zero shadow days", a vertical stick casts almost no shadow. BMKG, the national weather agency, publishes the dates every year, city by city. Almost nobody outside specialists knows it happens, why, or how to show it.

I built Zero Shadow Day for three groups. Residents can find their own city's two dates and see why they fall where they do. Teachers and students get a classroom activity: pair with a school a few hundred kilometres away and repeat Eratosthenes' 2,200-year-old measurement of the Earth. Anyone else gets quick facts on solar noon, sun height and shadow direction.

What I built

Everything comes from latitude, longitude and date

The app has no data feed, no API key, no database and no backend. Earlier projects of mine had stalled on data licensing, and a project with no data dependency has nothing to license and nothing to go stale. I wrote the sun-position engine, the shadow geometry, the date search, the Eratosthenes module and the 3D scene myself. There is no astronomy, date, timezone or 3D library.

I checked the maths before building any screen

The first milestone had no interface at all, only the sun-position maths checked against published astronomical tables. A common shortcut formula for the sun's position can be off by half a degree. Near the tropics, that turns into a date several days wrong that still looks plausible. I also wrote the shadow geometry with no shared code from the sun engine, and a test enforces that. An error then shows up as two calculations disagreeing.

A shadow never quite vanishes, so I report a window

The sun's disc is about half a degree wide, so a shadow shrinks to a minimum and never reaches nothing. The app always gives a time window, sized from the sun's apparent width on that date. A curious user with a stick outside could disprove a claim of one exact second. To find the dates, I search each year's candidate days for the shortest noon shadow. Near the tropics, where the two dates merge into one at the solstice, this works with no special case.

Where BMKG and I disagree, the tests say why

I checked the app against 76 dates from BMKG's official tables for 2025 and 2026, covering 38 provincial capitals. Seventy-five match exactly, all 76 fall within a day, and times agree to under five seconds. The one miss is Pekanbaru 2026: I name 22 March, BMKG names the 21st. The city is 0.53° from the equator, so the sun crosses overhead between two noons. Four Papua rows in BMKG's 2025 table appear to list only their first time. A test derives that from the physics.

The shadow is the readout

The main view is an animated 3D stick and shadow for any place and date. The shadow's length and direction are the numbers, not a picture beside them. I built the 3D scene by hand to keep the whole app at about 110 KB of JavaScript, under a 150 KB budget. A general 3D engine would have taken the whole budget on its own.

Result

Zero Shadow Day is live in Indonesian and English as a static site, with your coordinates staying in the browser and no network requests at all. It shows your two dates with the sun's highest moment to the second, draws shadow-tip curves and the figure-eight analemma, and sweeps the overhead sun across the islands through the year. Eratosthenes mode finds a measurement partner elsewhere in the archipelago and shows your error against the true circumference, because the gap is the lesson. Any view can be shared as a link, with dates exportable to a calendar file. I built the first version in two days in August 2026, then returned at the end of September for a further pass that redesigned the visuals.

Under the hoodTechnical detail for engineers
  • lib/solar is a NOAA/Meeus solar-position algorithm in pure TypeScript with no React, Next, DOM or network imports. It is shared with Falak, and tests/solar/extractable.test.ts keeps it extractable.
  • lib/shadow is independent of lib/solar; a test asserts the import is absent. lib/zsd finds dates by minimising noon shadow length; Outside the tropics it returns a structured no-result naming the latitude limit. lib/eratosthenes has a synthetic round-trip test that recovers a known radius exactly.
  • The numeric core uses Julian Day numbers only. Date and timezones live in one display module, lib/clock, since Indonesia has three zones and no DST.
  • Pekanbaru 2026: the app's rule picks the smaller noon-zenith residual, 0.111° on 22 March against 0.284° on 21 March. The four 2025 rows are Jayapura, Nabire, Wamena and Merauke. Their times are checked against two cities in the same zone and table, whose difference matches the change in the Equation of Time.
  • The 3D gnomon is a hand-rolled camera and perspective projection of about 450 lines, with the sky driven by solar altitude. No Three.js.
  • The PRD's flat "bleached concrete" palette (with a serif/sans/mono type trio) gave way to a day/night sky and a full-day hero animation, now set in Bricolage Grotesque, Plus Jakarta Sans and JetBrains Mono. Two rules stayed: vermilion only marks the zero-shadow day, and the shadow is the darkest tone on the page.
  • Accessibility pass: focus rings, error-to-field association, real headings, keyboard hints for canvas elements, toggle states and slider announcements. Contrast ratios are asserted in tests.
  • Performance pass: cached the per-year declination limits, pinned the variable font to the one weight used, and removed a city-data chunk shipped twice per route.
  • Next.js 14 static export on GitHub Pages, strict TypeScript, Tailwind, Vitest, pnpm, self-hosted fonts. CI runs pnpm test:solar first, then the full suite, typecheck and lint.
  • About 11,157 lines of TypeScript, 235 tests, 15 statically exported pages (7 routes in 2 locales, plus one root redirect), 91 commits between 13 August and 28 September 2026, with a long gap before a final pass in late September that added tests and redesigned the visuals.

Screenshots

Beranda
Beranda (2)
Beranda (3)
Dates
Dates (2)
Gnomon

Hiring, or building something similar?

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