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

- 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/solaris a NOAA/Meeus solar-position algorithm in pure TypeScript with no React, Next, DOM or network imports. It is shared with Falak, andtests/solar/extractable.test.tskeeps it extractable.lib/shadowis independent oflib/solar; a test asserts the import is absent.lib/zsdfinds dates by minimising noon shadow length; Outside the tropics it returns a structured no-result naming the latitude limit.lib/eratostheneshas a synthetic round-trip test that recovers a known radius exactly.- The numeric core uses Julian Day numbers only.
Dateand 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:solarfirst, 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
Hiring, or building something similar?
I'm open to full-time roles and select freelance work.