Inspiration

After a storm, a lake can change really quickly. Water that looked clear a few days ago can suddenly turn brown or green. What surprised me is that the data to see this already exists. Sentinel-2 satellites photograph lakes around the world every few days for free. But most of the tools around that data are made for scientists. You end up looking at spectral bands, cloud masks, scene IDs, and metadata instead of getting a simple answer to what is happening to the water. I also noticed that most water-quality maps only show the latest usable satellite image. If it has been cloudy, that image could already be days or even weeks old. That made me think about weather radar. Weather apps do not just show where rain was. They show what is happening now, animate how it changed, and give you an idea of what is coming next. I wanted to do the same thing for water. Satellites show now. Hydrology shows next.

What it does

Ripple is a weather-radar-style map for lake and river water quality across the United States, southern Canada, and Mexico. Water is colored from green to red based on a relative optical water-quality index, while land stays uncolored. The main feature is a radar-style timeline with 4 observed satellite composites and 7 forecast days. Instead of looking at one static satellite image, you can play the timeline and watch conditions change over time. Ripple also has a 7-day runoff outlook using forecast rainfall and runoff modeling, 250 automatically discovered water bodies, a leaderboard that ranks water bodies from cleaner to more polluted, detailed lake information, two 3D visualization modes, and point inspection anywhere on the map. I also built Ask Ripple, an AI assistant that can answer questions about Ripple's own data and control parts of the map. It can find water bodies, move the map, change the timeline, and open the 3D view. I intentionally made it refuse health or safety claims that the satellite data cannot actually support.

How we built it

Ripple has two main parts: a Python data pipeline and a React frontend. The pipeline uses Python, rasterio, and NumPy to search and process Sentinel-2 satellite imagery through the Earth Search STAC API. Across a full run, the pipeline reads thousands of remote satellite scenes and combines the newest usable pixels into rolling composites. I use NDCI to estimate bloom intensity and NDTI as a proxy for turbidity and suspended sediment. Instead of maintaining a hand-written list of lakes, Ripple discovers water bodies directly from the satellite water mask. Every large enough connected water region becomes something the app can measure and track. For the forecast, I use Open-Meteo rainfall and the SCS curve-number runoff method to estimate how much runoff could reach the water after rain. The frontend is built with Vite, React 19, TypeScript, Tailwind CSS, MapLibre GL, and three.js, and is deployed on Vercel. The pipeline outputs map tiles and one JSON manifest. There is no traditional database for the water-quality data. The generated output basically becomes the API.

Challenges we ran into

A lot of Ripple came from things going wrong. One of the first problems was satellite reflectance scaling. Following the metadata literally produced negative reflectance over clear water in Lake Superior, which physically did not make sense. I eventually traced the problem and added a physics check so bad scenes now stop the pipeline instead of silently producing bad data. I also originally tried using standard water indices like NDWI to detect lakes. On western Lake Erie, it detected only around 0.2% of obvious water because highly turbid water breaks the assumptions behind the index. I ended up switching to Sentinel-2's scene-classification layer. Another problem was that the Pacific Ocean became the highest-ranked "lake" in the app. A huge connected ocean fragment completely dominated the leaderboard, so I added ocean polygons to remove those areas before discovering water bodies. I also had two different bugs where the map was completely blank even though the build succeeded and the unit tests passed. After that, I stopped treating "the build passed" as proof that the product actually worked and added real rendered interaction checks.

Accomplishments that we're proud of

The part I am most proud of is validating the satellite signal against real measurements. I matched Ripple's turbidity index against 135 satellite-gauge pairs from 25 USGS turbidity gauges. The correlation was 0.29 for the actual values shown by the product, 0.46 when looking at water-only pixels, and reached 0.67 for cells that were at least half water. That showed me two important sources of error. Mixed land-and-water pixels hurt the signal significantly, especially near shorelines, but the larger issue was that different water bodies have different optical baselines. A single calibration does not transfer equally between a clear mountain creek and a sediment-heavy water body. A satellite cell can contain both shoreline and open water, which changes the measurement significantly. I'm also proud that Ripple creates a 7-day outlook for 250 water bodies entirely from free public data.

What we learned

The biggest thing I learned from Ripple is to let measurements overrule assumptions. I thought cloud percentage would tell me which satellite scene was best. It didn't. I thought NDWI would be an easy way to detect lakes. It wasn't. I thought ranking a lake by its worst pixel would make sense. It caused almost every major lake to tie at the top. Each time something failed, I measured why, changed the system, and turned the result into a rule in the pipeline.

What's next for Ripple

I want to add per-lake historical baselines, improve the resolution for smaller lakes and reservoirs, add uncertainty bands to the runoff forecast, and eventually expand Ripple beyond North America and covers Globally.

Links

Website: https://ripple-liart.vercel.app
Github: https://github.com/pfarell/ripple
Live Demo Video: https://drive.google.com/drive/folders/1fnMV90drFzwFsYoqfxIKbPfRhRmf08qp?usp=sharing

Built With

Share this project:

Updates

Submission history