Malaysia · God Mode
Research notes · v4 · 25 September 2026

A Digital Twin
of the Federation

Sixteen states. 160 districts. 32.4 million people. Every public signal about Malaysia that can be read in real time, drawn on one map, with the slow layers underneath. Built by Dr Non Arkaraprasertkul as a gift to depa (Digital Economy Promotion Agency of Thailand) and the ASEAN Smart Cities Network (ASCN) 2026.

Scale
0.6°–7.6° N · 99.4°–119.5° E
Coverage
16 states · 160 districts · 32.4 M people
Feeds
17 live · 3 baked
Stack
Static site · Node 20 relays · Cloudflare Pages
Languages
EN · BM · 中文

01Dr Non Arkaraprasertkul

Most important thing first: this is a gift.

Dr Non is a Thai urbanist who works on city systems — flood, mobility, air, fire, water. He has shipped dashboards for Bangkok floods (FloodDash), Chiang Mai (CNX), and Greater Kuching under the ASCN. He also keeps two personal projects — nsp-prototype (a podcast + soccer-life essay under his own name) and pirchchat (Burma monitoring).

The Malaysia project extends three boards he has already shipped. Where FloodDash ends at a basin, CNX ends at a city, and the Greater Kuching IOC ends at a state, this one ends at the federation.

"A good dashboard tells you what the room is doing right now. A great dashboard tells you what the room cannot tell you. This one does both, in three languages."

— Dr Non Arkaraprasertkul, project notes, September 2026

02This week, in data

What the dashboard has measured in the past seven days, with sources. Each row is live in the running app; the numbers below are a snapshot of the rolling week ending today, 25 September 2026.

512JPS river gauges reportingpublicinfobanjir.water.gov.my
5gauges at warning level or aboveJPS Public Infobanjir
1MET warning in forceapi.data.gov.my
452public-transport vehicles liveGTFS-Realtime · Prasarana + myBAS + KTMB
7,392vessels tracked around MalaysiaAxiom Overwatch
93fire hotspots in the last 48 hNASA FIRMS VIIRS
45worst PM2.5 µg/m³ (Kuching)Open-Meteo · CAMS model
4.0775USD / MYR referenceBank Negara Malaysia

What the news has been

The press pulse aggregates Google News in EN / BM / 中文 on a 10-minute interval. As of today, the top three regional stories are about the southwest monsoon withdrawal across the Malay Peninsula, the East Coast Rail Link (ECRL) reaching the Kota Bharu integrated terminal, and the second-quarter GDP release from Bank Negara (5.2% YoY, vs. 5.0% consensus).

If a FACEBOOK_PAGE_ID and FACEBOOK_PAGE_ACCESS_TOKEN are configured on the server, the same press pulse now pulls the latest posts from a designated Facebook Page through the Graph API v18.0 — see § 06. Without credentials, the page stays empty and the Google News panel remains the sole source of press.

03Mathematics

Five small maths. Nothing exotic. The dashboard earns its keep by not lying when the maths is uncertain.

3.1 Great-circle distance

Every reach-analysis pixel walks to the nearest mapped facility with the standard Haversine formula over WGS-84.

a = sin²(Δφ / 2)
  + cos(φ₁) · cos(φ₂) · sin²(Δλ / 2)
c = 2 · atan2(√a, √(1−a))
d = R · c                R = 6,371 km

Real journeys by road or river are longer. The headline always says "straight line".

3.2 Reach analysis

For every ~1 km² cell where Meta HRSL says n people live:

for each facility f ∈
  {hospitals, clinics, schools, police, fire}:
  km[c, f] = haversine(c, f)
  nearest[c] = argmin_f km[c, f]
  if km[c, nearest_hospital] > 20:
    people_>20km += n
total_>20km = Σ people_>20km

3.3 Freshness grading

Each feed carries an observation time, not a fetch time. Age against cadence decides status:

grade(f) =
  loading   if !f.fetched
  error     if f.error
  nokey     if error matches /no .*key/
  stale     if f.obs > now + 5m
  stale     if age > 4 · cadence
  delayed   if age > cadence
  live      otherwise

A 200 OK with yesterday's numbers is not "live".

3.4 Trilingual interpolation

Bahasa forecast strings are matched against a small phrase table in feeds.js and replaced with EN or 中文 equivalents. The original phrase always backs the rain-icon lookup, so the emoji never disagrees with the displayed text.

3.5 Air quality

PM2.5 in 21 state capitals is the Open-Meteo CAMS reanalysis at 0.4° — a model, not a measurement. Bands follow the US EPA cut-points (12 / 35 / 55 / 150 µg/m³).

3.6 Reach map (visual)

PENINSULAR MALAYSIA each cell ≈ 1 km² · Meta HRSL 30 m → 20 km
Red squares = populated cells that sit outside the 20 km straight-line radius of any mapped hospital. Peninsular Malaysia, illustrative.

04Data pipeline

Every feed runs through the same shape: fetch → normalise → redraw → grade → notify. Polling lives in core.js → poll(id, fn). When the tab is hidden, polling pauses; when it returns, every feed whose last fetch is older than its interval re-runs.

fetch(url, { headers, signal: timeout(10s) })
   ↓
JSON.parse        → feed-specific normalise()
   ↓
D.{feed}          → layer redraw (Leaflet)
   ↓
mark(id, ok, newestObservationTime)
   ↓
grade(f)          → status: live | delayed | stale | error | nokey | loading
   ↓
regrade() → re-render verdict, KPIs, tab, status pill

Every feed, its cadence, its freshness window

FeedEndpoint↻ok ≤
JPS river gaugespublicinfobanjir.water.gov.my120 s45 m
MET warningsapi.data.gov.my300 s15 m
MET forecastapi.data.gov.my30 m36 h
USGS + MET earthquakesearthquake.usgs.gov300 s60 m
GTFS-Realtime (Prasarana, myBAS, KTMB)developer.data.gov.my20 s5 m
Aircraft (adsb.lol / OpenSky)adsb.lol30 s5 m
AIS ships (Axiom Overwatch)axiomoverwatch.io300 s45 m
Airport arrivalsopensky-network.org1 h36 h
Open-Meteo air quality (CAMS)open-meteo.com600 s2 h
RainViewer radarrainviewer.com300 s30 m
NASA FIRMS fire hotspotsfirms.modaps.eosdis.nasa.gov30 m24 h
Blitzortung lightning (PocketWorld)pocketworld.org60 s5 m
Bank Negara ratesapikijangportal.bnm.gov.my30 m4 d
OpenDOSM CPIopen.dosm.gov.my6 h70 d
JAKIM e-Solat prayer timese-solat.gov.my1 h36 h
Google News (EN / BM / 中文)news.google.com10 m24 h
Facebook Page postsgraph.facebook.com——

Three feeds are baked (one-time, static): reach analysis, Census 2020 + HIES 2019, and the boundaries. The Facebook Page endpoint is enabled only when both FACEBOOK_PAGE_ID and FACEBOOK_PAGE_ACCESS_TOKEN are set on the server; otherwise it returns an empty array.

05Facebook integration

The press pulse now reaches into Facebook's Graph API v18.0 through /api/facebook. The endpoint returns the most recent 20 posts from a configured page: message, created_time, permalink_url, full_picture, plus reaction, comment, and share counts.

Configuration is two environment variables on the Cloudflare Pages project:

FACEBOOK_PAGE_ID            = 123456789
FACEBOOK_PAGE_ACCESS_TOKEN  = EAAxxxxxx...

The token is a long-lived Page access token, generated from a Facebook App with pages_read_engagement + pages_show_list scopes. Without it the endpoint returns { posts: [], note: "..." } and the dashboard continues to use Google News alone.

On the front-end, Facebook posts flow into the same rail tab as the news pulse. Source attribution shows Facebook · <page-name>. The cache is 5 minutes at the edge.

Example wire shape

GET /api/facebook

200 OK
{
  "source": "Facebook Graph API",
  "configured": true,
  "posts": [
    {
      "id": "123_456",
      "message": "Today at 14:32 MYT...",
      "time": "2026-09-25T06:32:00+0000",
      "url": "https://facebook.com/...",
      "image": "https://scontent...",
      "reactions": 432,
      "comments": 41,
      "shares": 18
    }
  ]
}

Failure mode

If the token has expired or the page is unpublished, the relay returns 502 with error set. The press tab hides that source and shows the remaining feeds.

06Safety & limitations

"This board is decision support, not an official warning."

The dashboard never claims it can establish that an area is safe. If Coverage is incomplete appears, the headline is honest about what it does not know. A red banner names the feed that has gone stale and the time since it last answered.

What the dashboard does not claim

  • Hospital locations are reference data. They do not confirm opening hours, available beds, or an accessible route.
  • Reach distances are straight line. Real journeys by road or river are longer.
  • Census 2020 and HIES 2019 are historical.
  • Air quality is a model, not a measurement.
  • Ship "declared destinations" are what the ship transmits, not where it actually goes.
  • Rain radar is 10-minute frames.
  • Lightning shows the last 15 minutes of strikes that crossed Malaysia's bounding box.
  • Facebook posts are public Page content; we do not fetch comments from posts older than 24 hours or DMs.

What it does claim

  • If a river gauge reads DANGER within the last 45 minutes, that is the operator-reported category for that station.
  • If a MET warning is in valid_to ≥ now, it is the current authoritative text from MET Malaysia.
  • If the verdict says Not enough data, the upstream is broken — not the dashboard.

07Credits

English

Built by Dr Non Arkaraprasertkul as a gift to depa (Digital Economy Promotion Agency of Thailand) and the ASEAN Smart Cities Network (ASCN) 2026. Fonts: Plus Jakarta Sans by Tokotype (Jakarta); Newsreader by Production Type; Noto Sans SC; IBM Plex Mono. Independent demonstration. Not an official product of the Government of Malaysia.

Bahasa

Dibina oleh Dr Non Arkaraprasertkul sebagai hadiah kepada depa (Agensi Galakan Ekonomi Digital Thailand) dan Rangkaian Bandar Pintar ASEAN (ASCN) 2026. Fon: Plus Jakarta Sans oleh Tokotype (Jakarta); Newsreader oleh Production Type; Noto Sans SC; IBM Plex Mono. Demonstrasi bebas. Bukan produk rasmi Kerajaan Malaysia.

中文

由 Dr Non Arkaraprasertkul 制作,作为赠予泰国 depa(数字经济促进局)及 东盟智慧城市网络 (ASCN) 2026 的礼物。字体:Plus Jakarta Sans(Tokotype,雅加达);Newsreader(Production Type);Noto Sans SC;IBM Plex Mono。独立演示项目,并非马来西亚政府官方产品。

Data attribution

License

Application code: MIT. Fonts: OFL (Plus Jakarta Sans, Newsreader, Noto Sans SC, IBM Plex Mono). Data layers: see the CC BY 4.0 / public domain notices above. The Malaysia state outline comes from DOSM's data-open repository. This repository is private.

— end —