There is a fundamental difference between visualizing data and translating it into a visual language. A chart shows temperature as a rising line. The data is readable but not experienced.
ATMOSPHERICA proposes something else: the datum is the form. Temperature does not appear on a Y axis — it determines the complete color range of the canvas. Wind does not appear as an arrow — its Bézier strokes cross the canvas in the exact geographic direction of the real wind, with length proportional to wind speed in m/s. Atmospheric pressure is not a number — it decides whether the composition organizes into architectural horizontal bands (anticyclone) or into tilted, unstable rectangles at the edges (low pressure system).
The result is a painting that feels different depending on the climate. A July heatwave in Seville produces something radically different from a windy autumn day or a cloudy January morning — without reading a single number.
Each climate variable has its own family of forms, its own fixed color, and its own behavior depending on the data value. This grammar is the heart of the project: the set of rules that makes each visual decision traceable to a real measurement.
One additional layer — rain — appears on top of everything when precipitation is detected. Diagonal strokes lean according to real wind direction; intensity and density scale with mm/h.
This is the central conceptual mechanism of v2. All six variables are normalized to [0, 1] using local historical ranges for Seville. The variable with the highest normalized value at that moment takes over the entire composition and deforms how everything else behaves.
Example: 1014 hPa occupies 60% of the 990–1030 hPa range. 20°C occupies 43% of the 0–46°C range. Pressure wins. The canvas fills with horizontal bands. Everything else is subordinated to the structural order of the anticyclone.
OpenWeatherMap API — temperature, pressure, wind speed and direction, humidity, clouds, precipitation. Air Pollution API — PM2.5, NO₂, O₃. Both called on every execution, credentials managed via environment variables.
Each variable is normalized using historical ranges calibrated for Seville. Wind direction is decomposed into Cartesian components (dx, dy) for vectorial stroke orientation. The mapper computes 15+ visual parameters and determines the dominant variable via argmax of normalized values.
Each execution appends to a local CSV that persists in the repository. The system accumulates one row per day — 3 daily API calls aggregate into a single daily record. After 7 days, rolling means are real. After 14 days, all lag features are complete.
Random Forest trained on 15 years of ERA5 reanalysis data (Copernicus). 32 features including rolling means, temporal lags, pressure gradients, and seasonal encoding. Predicts probability of extreme weather tomorrow. Mean AUC 0.836 across 5 time-series folds.
HTML5 Canvas with native JavaScript. Deterministic seeded RNG — the same day and city always produce exactly the same painting, in any browser, on any machine. The dominant variable deforms the entire visual system across 8 render passes.
GitHub Actions runs twice daily (08h and 20h Madrid time). Playwright exports PNG headlessly. Git commits the PNG, the history CSV, and the archive JSON. GitHub Pages serves the gallery automatically.
The Random Forest predicts the probability of an extreme weather event the next day. When the risk is significant, the painting carries visual signals of the future event embedded in today's composition — using the shape of today's dominant variable in the color of tomorrow's predicted event.
The signals activate by progressive risk thresholds. The legend of each painting shows exactly which layers are active and at what probability they trigger. The production page treats 6% as the visual activation threshold: below that value the artwork keeps the base climate grammar only; above it, tomorrow's risk starts to leave visible marks in today's image.
The Random Forest outputs the probability of an extreme event. The specific event colour is assigned when the predictor can infer a type (heat, cold, rain, or wind); if the probability is active but the type is ambiguous, the interface names it as a generic extreme-event signal instead of pretending the model knows more than it does.
Event colors are fixed: heat → red-orange · cold → steel blue · wind → green-teal · rain → slate blue. These colors never appear in the base grammar, so any tint in those hues is a signal — not climate description, but prediction bleeding through.
Developed as a central portfolio piece by a recently graduated software engineer moving toward Machine Learning Engineering. Seville, 2026.
The project demonstrates the ability to build real data pipelines with external APIs, design feature engineering systems for time series, train and evaluate classification models, and produce outputs that go beyond the typical Jupyter notebook — a complete, autonomous ML system running in production.