The cloud mask, in three stages
| Test | Catches | |
|---|---|---|
| 01 | Onset | a return too strong, or an edge too sharp, for aerosol |
| 02 | Column | the cloud's body above its base, and the shadow it casts |
| 03 | Persistence | only what holds minute to minute is kept |
← Back Case study · Flagship Handed over · still running
A real-time atmospheric-intelligence platform that turns three Doppler LiDARs, NEA air quality and NASA fire detections into a detection score an operator can inspect, and reaches out the moment haze starts to move in over Singapore. I designed it, built it end to end, operated it from 2025 to 2026, and handed it over in full: the CCEH team runs it today.
00 / 04Context
Elevated aerosol layers (haze, transported smoke, dust) sit aloft for hours before they mix down to street level. By the time a ground monitor reads the spike, the event is already happening. The signals that see it coming live in Doppler-LiDAR backscatter and wind profiles, but those feeds are noisy, ragged, and arrive on different clocks across sites, and clouds routinely masquerade as aerosol.
The brief: make that early signal trustworthy and actionable. Fuse the sources onto one timeline, score the likelihood of an episode with a physical model an operator can interrogate, forecast when it reaches the surface, and push that judgement to the people who act on it, without drowning them in false alarms.
01 / 04System
One pipeline, read left to right: what comes in, what decides, what acts. Three Halo Photonics StreamLine XR Doppler LiDARs stare straight up from rooftops at NTU's Asian School of the Environment, Raffles Girls' School and Woodlands Health, each averaged to one backscatter profile a minute, with a six-beam wind scan solved by least squares.
Every feed lands on one ten-minute clock with its gaps carried, never interpolated away. One shared scorer reads the last two hours, and the same code runs live, in replay and behind the dashboard, so a past episode re-runs exactly as it fired.
LiDAR backscatter, vertical velocity and wind; NEA PM2.5, PSI and weather; NASA FIRMS fire detections; and a three-stage cloud mask computed from the LiDAR itself. Files are deduplicated per site in SQLite, and every upsert is idempotent.
Three sites on one ten-minute clock. The shared scorer rates each two-hour window and forecasts when an elevated layer will break through to the surface.
Alert cards in Microsoft Teams with every signal attached; plots and status in Teams and Telegram; mission control and the five-viewport Geo Data Hub for the people who look.
Every detected episode is recorded in PostgreSQL and can be replayed through the same scorer: how v2 was measured against the February 2026 episode.
02 / 04Detection
Detection is a physics-based score, not a black box. Every ten minutes the last two hours are rated 0 to 100 from six weighted checks, each one shown on the alert, so an operator can see exactly why a number moved.
Before anything is scored, hard gates ask for enough minutes of stare, an elevated layer several range gates deep, and a coherent wind. A window that passes them all is MEDIUM. It becomes HIGH when a second site corroborates or the score reaches 75. A diurnal boundary-layer model then estimates when the layer will reach the street.
Clouds are the hardest part of trusting LiDAR: a cloud base looks like a dense layer. A three-stage cloud mask runs beside the score and puts its verdict on the card.
Enough stare, an elevated layer, coherent wind
Weighted, summed to P(episode), 0 to 100
MEDIUM when every gate passes; HIGH on a second site or P ≥ 75
To Microsoft Teams, with the breakthrough ETA and the cloud mask's note
| Test | Catches | |
|---|---|---|
| 01 | Onset | a return too strong, or an edge too sharp, for aerosol |
| 02 | Column | the cloud's body above its base, and the shadow it casts |
| 03 | Persistence | only what holds minute to minute is kept |
The same layer means different things at 03:00 and 14:00. Overnight the boundary layer is shallow and the haze stays up; by mid-morning it grows into the layer and mixes it down. In the public extract's synthetic run, one identical layer scores 1.00 on the breakthrough term at 14:00 and 0.11 at 03:00.
03 / 04Delivery
A score nobody sees is worthless. The system is proactive: when a window crosses the line, the LiDAR Bot posts the alert to the dashboard and an alert card to Microsoft Teams, with every signal attached, before anyone has asked.
The same agents answer on demand. Teams carries the alerts, analysis, NEA readings and event log behind seventeen commands; Telegram receives the daily plots and status and answers plot requests.
04 / 04Result
Replayed against the 36-hour February 2026 reference episode over Raffles Girls' School, v2 issued ten alerts where the previous detector issued none. The north-east monsoon carried a layer in aloft; the first HIGH came at 02:30, and the layer mixed down at 08:15: 5 hours 45 minutes of warning.
Monitoring shifted from reading spikes after the fact to being told an episode is likely, with a forecast for when it reaches the surface.
The web app ships a public preview at lidar-dashboard-3dreams.vercel.app; the operational dashboard behind it is login-gated for the CCEH team. The signal behind a score is one click from the alert itself.
Ingestion, retrieval, scoring, backend, dashboard and agents: designed, built and operated by one person, then migrated in full to the CCEH team, who run it today.
Presented by CCEH's founding director at the Regional Action Forum NTU co-hosted with the World Health Organization in June 2026, to delegates from eleven Western Pacific countries. I built the platform and the film shown there; the film at the top of this page follows one evening of September 2026's haze through the platform.
In the open. The
3dreams-sg repository ↗
is a standard-library extract of the scorer's design: the shared scorer, the
breakthrough term and the cloud check, run over three synthetic windows with
python3 examples/synthetic_window.py and tested in CI.
The observational data, the credentials and the operational thresholds stay with CCEH. The repository shows how the decisions are structured, not the numbers that tune them.