factcircuit
FactCircuit: an auditable claim-and-evidence verification loop with immutable provenance, staged checks, bounded retrieval, and reproducible evaluation.
The smoke has a source. See active fire detections, PM2.5 air quality, and wind patterns across Thailand, Myanmar, Laos, and Cambodia — updated daily.
DataTypeScript · PythonGitHub @JoostKiensOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
An open-source civic tech application visualizing the cross-border origins of Thailand's seasonal PM2.5 crises, making the connection between agricultural burning in Myanmar and Laos and Thai air quality visually undeniable.
Taken from the repository README
Loading the full README.
Read the full README ↗Thailand's dry season air quality is severe and its causes are genuinely complex: fires, agriculture, industry, transport, geography, wind. This map puts the available data in one place so you can look at it yourself.
NASA VIIRS fire detections, Open-Meteo wind vectors, OpenAQ ground stations, and the CAMS atmospheric PM2.5 model, updated daily. A time scrubber lets you step back through a configurable 30/60/90/120-day window of historical data. Available in English and Thai. Free, no login, mobile-friendly.
| Layer | What it shows |
|---|---|
| PM2.5 heatmap | CAMS atmospheric model at 0.4° resolution, bilinearly interpolated and rendered via a Web Worker |
| PM2.5 stations | OpenAQ ground measurements, colored by US EPA AQI thresholds |
| Fire detections | NASA VIIRS active fire points, sized by Fire Radiative Power (FRP) |
| Wind particles | Animated flow field from Open-Meteo |
| Power plants | WRI coal, gas, and oil plants across Southeast and South Asia |
Click any element on the map to open the info panel:
All panels show the reverse-geocoded place name and country flag for the clicked location.
The bottom slider steps through the selected history window. Press Play (or hit Space) to animate through dates automatically at 800 ms per step.
The history window — 30, 60, 90, or 120 days — is configurable in Settings and persisted across sessions. New data is typically available by 06:30 Bangkok time each morning.
Map position, zoom, and selected date are reflected in the URL, so any view is fully shareable.
Click any monitoring station and hit "What is causing this?" The backend assembles a spatial context snapshot and streams an explanation from Gemini 3.1 Flash Lite.
The model sees more than just the PM2.5 number. It gets the 5-day daily trend, current wind speed and direction, and fires discovered via a 72-hour backward trajectory: the backend runs a 5-member backward trajectory ensemble, tracing the air mass from the station and four cardinal offsets, stepping back in 6-hour increments over 72 hours using stored wind grids. Fires within the combined footprint of all five paths are then scored by FRP, recency, and proximity to the trajectory corridor. A pre-computed daily per-station fire pressure score (fire count and total FRP within 75km of the station over 14 days) is used to distinguish sustained local fire activity from transported smoke passing through the area. Major cities, industrial zones, and power plants near the trajectory footprint are also evaluated as upwind emission sources, each scored by population or emission proxy and minimum distance to the ensemble trajectory footprint, and included only if they fall within the widening trajectory cone and their bearing from the station aligns with the wind direction. It also gets the distance-weighted mean PM2.5 from all peer stations within 75 km that have reported in the last 24 hours.
When a station reads at least twice (or less than 40% of) the distance-weighted mean of nearby stations, the trajectory, fire, and CAMS sections are dropped from the model's context entirely; the explanation focuses on the anomaly and its most likely causes rather than regional transport. When the reading is consistent with the regional picture, transport and fire evidence take the foreground.
| Source | What | Cadence | License |
|---|---|---|---|
| NASA FIRMS (VIIRS/NOAA-21) | Fire detections — location, FRP, confidence | Daily | NASA open data |
| OpenAQ v3 | PM2.5 ground stations | Daily | CC BY 4.0 |
| Open-Meteo | Wind grid + CAMS PM2.5 atmospheric model | Daily | CC BY 4.0 |
| WRI Global Power Plant Database | Coal, gas, oil, diesel power plants | Static | CC BY 4.0 |
| Mapbox | Base map | — | Mapbox TOS |
Fahsai's data (fires, PM2.5, weather, stations) is also queryable programmatically via fahsai-mcp-server, a Model Context Protocol server for AI agents.
Frontend: React 19 · TypeScript · Vite · Mapbox GL JS · Deck.gl · Zustand · TanStack Query v5 · Web Workers (PM2.5 canvas rendering) · react-i18next
Backend: Node.js 24 · Fastify · Supabase (PostgreSQL) · Upstash Redis · Google Gemini API
Deployment: Vercel (frontend) · Railway (backend + cron jobs)
Apache 2.0 + Commons Clause — non-commercial use.
See CLAUDE.md for local setup, environment variables, and dev commands. Database schema, API reference, and ingestion job docs are in docs/claude/.
The backend serves its API under /api/v1. With pnpm dev running, the interactive API docs are at http://localhost:3001/docs and the OpenAPI document at http://localhost:3001/docs/json.
The frontend components are in Storybook. Run pnpm --filter frontend storybook and open http://localhost:6006. Each component has a Docs page, and every story runs as a test, including an accessibility check, with pnpm --filter frontend test:storybook.
Issues welcome.