Fuego.Earth
2026

A fire-spread model is not useful just because it runs. Someone has to choose a place, supply the right terrain, fuels, and weather, wait for the computation, and understand what the result does and does not say.
Fuego.Earth is an attempt to make that whole chain usable from a browser. I built it with My Tran, Álvaro Galván, and Sofiia Bilyk at Minerva University. A user can place an ignition point on a map, choose a date and duration, run Cell2Fire or FARSITE, and replay the estimated spread hour by hour. The platform also produces ensemble and relative-risk maps for questions that a single perimeter cannot answer.
I worked mostly where the scientific code became a dependable product: simulator inputs, spatial and temporal correctness, probability runs, API behavior, caching, deployment, and the contracts that connect those pieces.
From one click to a perimeter
The visible interaction is simple. Pick an ignition point, choose the simulation window, and press run. Behind that click, Fuego.Earth has to collect and reconcile several kinds of evidence.
LANDFIRE provides fuel models and terrain for the selected area. Weather comes from sources such as HRRR and Open-Meteo. Other products add satellite observations, land cover, land-surface temperature, and roads. These sources were not designed to fit together. They arrive in different coordinate systems, resolutions, time zones, file formats, and definitions of missing data.
The first version let too much of that complexity leak into the fire engines. A fix for one data source could quietly change another path. A layer could look plausible on the map while being shifted by a few cells. Weather labels could be one interval away from the values actually used.
We eventually gave the system one internal contract. Extraction modules download raw data. Processing aligns every layer to a common grid with the same extent, resolution, coordinate system, and missing-data rules. The result is a Landscape. Products consume that object instead of knowing where each file came from.
That boundary mattered more than any one model. It made spatial alignment testable, let multiple products reuse the same prepared landscape, and kept the API thin. It also made provenance possible: an output can say which source, time window, and transformation produced it.
Two engines, one interface
Cell2Fire and FARSITE do not accept the same inputs or return the same outputs. One expects a particular set of ASCII grids, fuel tables, ignition cells, and weather rows. The other has its own landscape and weather formats. Their time steps and geometries differ too.
Fuego.Earth treats each engine as an adapter behind one fire-spread interface. The pipeline prepares model-specific files, runs the selected engine, and converts the result into a common GeoJSON time series. The web application only has to know that each step has a timestamp, a burned perimeter, area, weather, and a few summary statistics.
This sounds like ordinary software plumbing, but it is where many scientific applications become fragile. Early bugs included fuel classes missing from a lookup table, a drifting ignition marker, weather rows that overflowed long simulations, and timestamps that described the wrong interval. None of them made the interface crash in an obvious way. They produced believable, wrong results.
The lesson was that correctness has to include the glue around the model. A physically sophisticated engine cannot repair a raster shifted east or a weather series shifted in time.
One run is not a probability
A single simulation answers a conditional question: given this ignition, this weather, this landscape, and this model, where might the fire travel? It does not tell you how likely each place is to burn.
For the ensemble view, Fuego.Earth varies the uncertain conditions across many Cell2Fire runs. Each grid cell records the share of scenarios in which it burns. The interface calls that quantity chance of burning, which keeps it distinct from the separate Burn Probability Map. The latter is a relative-risk product that combines ranked drivers such as vegetation, slope, aspect, temperature, wind, humidity, and proximity to roads or settlements.
The ensemble feature forced a change in the product architecture. A request that starts one hundred fire simulations cannot safely sit inside one browser connection. It became an asynchronous job with progress, durable state, limits, and cached landscape data. The cache was not just a speed optimization. It had to be crash-safe, reject truncated terrain files, and keep results from one run from appearing in another.
Comparing the models with a real fire
We used historical fire perimeters as a check against the simulators. One stored benchmark follows Fire 5501 for 36 hours and compares each model with VEDA observations at 12, 24, and 36 hours.
At 36 hours, the observed perimeter covered 16.17 km². FARSITE produced 17.92 km², with an F1 score of 0.656 and an intersection-over-union score of 0.488. Cell2Fire produced 23.86 km², with an F1 score of 0.610 and an intersection-over-union score of 0.439.
That does not mean FARSITE is simply the better model. Cell2Fire had the stronger F1 score at 24 hours, and this is one fire under one set of inputs. The comparison was useful because it exposed where the models diverged over time. It also kept the product honest: a smooth animation is evidence that rendering works, not evidence that the fire forecast is right.
In the saved benchmark, the simulator stages completed in about 8.2 seconds for Cell2Fire and 2.1 seconds for FARSITE. Those numbers describe that run, not total time from a cold browser request. Downloading and preparing terrain can dominate the first request; a valid cache makes the next one much faster.
Turning research code into a public service
The production system is deliberately plain. A FastAPI service and a React/Mapbox application run on one virtual machine behind Caddy. Docker builds both fire engines at pinned revisions. GitHub Actions deploys from the main branch. SQLite stores jobs and usage counts so a restart does not erase work in progress.
The less visible work mattered most near launch: request timeouts, concurrency limits, stable error messages, path-traversal protection, strict CORS settings, and privacy rules for coordinates. Public analytics round locations to roughly one kilometre and avoid session recording. Exact ignition coordinates stay out of run logs.
This project changed how I think about applied modelling. The model is only one component of the claim a product makes. The claim also depends on where the inputs came from, how they were aligned, which version of the engine ran, what failed, and how uncertainty is shown to the person looking at the map.
If I continued the study, I would validate on many more fires and terrain types, publish calibration curves for the ensemble map, and make model disagreement a first-class part of the interface. The most trustworthy result may not be one crisp perimeter. It may be a map that shows where two reasonable models stop agreeing.