This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass 🌿
💡 The idea in one breath
Most weather apps keep you scrolling. PLOT does the opposite. 📵➡️🌱
You pick a place (or share your location 📍). Real weather loads 🌦️. TabPFN, an open tabular foundation model 🧠, reads your day like a row in a frost table and answers:
⏸️ Wait · 🚶 Walk · 🌿 Garden
Then it hands you three concrete steps and expects you to leave. The screen's job is under a minute. ⏱️
| 🚀 Live demo | plot-touch-grass.onrender.com |
| 💻 Source | github.com/PranjalManhgaye/plot-touch-grass |
🛠️ What I Built
| Field | Value |
|---|---|
| 🌿 Product | PLOT - outdoor action signal + checklist |
| 🧠 Brain |
TabPFN (tabpfn-client) |
| 📊 Training data | 30,684 real daily rows, 12 US cities, 2018-2024 |
| 🌦️ Weather | Open-Meteo ERA5 reanalysis + live forecast |
| ☁️ Hosting | Render free tier |
| ⚡ Stack | FastAPI, static UI, no chat layer |
👥 Who it's for: anyone with a yard, balcony, or park nearby who overthinks the forecast and under-leaves the house.
🎬 Demo
🎥 Screen recording: [https://www.youtube.com/watch?v=0zisIS7GLjg]
🔗 Try it yourself: plot-touch-grass.onrender.com
⏳ First load on Render free tier may take ~30s while the service wakes up.
📸 Screenshots
🌦️ Live weather - Open-Meteo fills the form
🎯 TabPFN signal - wait / walk / garden with confidence
⚙️ How it works (60 seconds)
flowchart LR
subgraph YOU["You"]
A["Pick city or use location"]
end
subgraph DATA["Open-Meteo"]
B["Today's temps, rain, wind, soil"]
C["92-day frost window"]
end
subgraph AI["TabPFN"]
D["Classify: wait, walk, garden"]
E["Class probabilities"]
end
subgraph OUT["Outside"]
F["3-step checklist"]
G["Phone down"]
end
A --> B
A --> C
B --> D
C --> D
D --> E
E --> F
F --> G
🗺️ User journey
sequenceDiagram
participant U as User
participant UI as PLOT UI
participant OM as Open-Meteo
participant T as TabPFN
participant Y as Your yard
U->>UI: Open site / pick Portland
UI->>OM: GET /api/weather
OM-->>UI: Real temps, frost lag, soil
U->>UI: Get outdoor signal
UI->>T: POST /api/check
T-->>UI: garden, 83% confidence
UI-->>U: Checklist (3 steps)
U->>Y: Goes outside
🏗️ Architecture
graph TB
subgraph Client["Browser"]
WEB["Static UI / web"]
end
subgraph RenderHost["Render - plot-touch-grass"]
API["FastAPI / backend/main.py"]
ENG["OutdoorEngine / TabPFNClassifier"]
CSV[("outdoor_history.csv - 30684 ERA5 rows")]
WX["weather.py - Open-Meteo + fallback"]
end
subgraph External["Open services"]
OM["Open-Meteo API"]
TP["Prior Labs TabPFN"]
end
WEB -->|/api/weather| API
WEB -->|/api/check| API
API --> WX
WX --> OM
API --> ENG
ENG --> CSV
ENG --> TP
📈 The data story (real, not synthetic)
This is the part I cared about most. No fake temperatures. ✅
flowchart TD
A["Open-Meteo Historical API - ERA5"] --> B["scripts/fetch_real_data.py"]
B --> C["12 cities x 7 years of daily rows"]
C --> D["Feature engineering"]
D --> D1["days_since_last_frost from min temp at 0C"]
D --> D2["soil_moisture, wind, daylight"]
D --> E["Labels via backend/labels.py"]
E --> F["outdoor_history.csv - 30684 rows"]
F --> G["TabPFN fit and predict"]
🌎 Cities in the training set
| City | Climate | Rows |
|---|---|---|
| 🌲 Portland, OR | Cool marine | ~2,557 |
| 🌧️ Seattle, WA | Marine west coast | ~2,557 |
| 🏙️ Chicago, IL | Continental | ~2,557 |
| ❄️ Minneapolis, MN | Upper Midwest | ~2,557 |
| ⛰️ Denver, CO | High plains | ~2,557 |
| 🗽 New York, NY | Humid subtropical | ~2,557 |
| 🦞 Boston, MA | Coastal temperate | ~2,557 |
| 🌳 Atlanta, GA | Warm humid | ~2,557 |
| 🤠 Austin, TX | Warm | ~2,557 |
| ☀️ Phoenix, AZ | Desert | ~2,557 |
| 🌴 Miami, FL | Tropical humid | ~2,557 |
| 🌊 Los Angeles, CA | Mediterranean | ~2,557 |
📊 Label distribution (on real weather)
| Action | Meaning | Share |
|---|---|---|
| 🚶 walk | Good to be outside, not a planting day | ~57% |
| ⏸️ wait | Frost risk or bad conditions, prep indoors | ~30% |
| 🌿 garden | Frost window + workable soil, hands in dirt | ~13% |
Labels come from transparent horticulture heuristics in backend/labels.py (frost-free days, precip, wind bounds). TabPFN learns patterns in the table, not chat prose. I'm upfront about that. 📝
🔨 How I Built It
1️⃣ Real training pipeline
python scripts/fetch_real_data.py
Pulls daily ERA5 weather from Open-Meteo for every city above. Regeneratable. Documented in data/DATA_SOURCES.md.
2️⃣ TabPFN as the classifier
from tabpfn_client import TabPFNClassifier
model = TabPFNClassifier()
model.fit(X_train, y_train) # real rows
proba = model.predict_proba(X_today) # wait / walk / garden
Open-weight tabular model. Swap it without rewriting the product. 🔁
3️⃣ Live weather endpoint
GET /api/weather?lat=&lon= returns today's conditions + frost lag.
- 🌐 Primary: Open-Meteo forecast with
past_days=92for frost history - 🛡️ Fallback: nearest city's real ERA5 row for today's calendar day (when API rate-limits on free hosting)
4️⃣ UI that stays out of the way
No chat bubble. No purple gradients. One brand (PLOT), one CTA, one result card. ✨
- 🏙️ City presets across 12 climates
- 📍 Use my location (browser geolocation)
- 🔄 Refresh live weather
- ✅ Checklist that ends at the door
5️⃣ Deployed on Render
render.yaml blueprint, health check at /api/healthz, TABPFN_TOKEN in env (never committed). 🔒
💻 Code
PranjalManhgaye
/
plot-touch-grass
PLOT - TabPFN outdoor go-signal for Hacktoberfest Touch Grass
PLOT
Leave the screen. Tend the ground.
PLOT is a Hacktoberfest Week 1 (Touch Grass) project. It uses Prior Labs TabPFN on real weather data (Open-Meteo ERA5) to classify today’s outdoor action as wait, walk, or garden — then gives a short checklist meant to end at your door.
Live demo
- App: https://plot-touch-grass.onrender.com
- Repo: https://github.com/PranjalManhgaye/plot-touch-grass
Pick a city or use your location → live weather loads → TabPFN predicts → go outside.
Real data (not synthetic)
What
Source
Training rows
30,684 daily observations, 12 US cities, 2018–2024
Weather
Open-Meteo ERA5 reanalysis
Frost lag
Computed from real daily min temps ≤ 0 °C
Labels
Transparent horticulture heuristics (
backend/labels.py)
Live inputs
Open-Meteo forecast + 120-day archive
See data/DATA_SOURCES.md for full provenance.
Quick start
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # set TABPFN_TOKEN
PYTHONPATH=. uvicorn backend.main:app --reload --host 127.0.0.1 --port…plot/
├── backend/
│ ├── engine.py # TabPFN + transparent baseline
│ ├── weather.py # Open-Meteo live + ERA5 fallback
│ ├── labels.py # Documented outdoor-action heuristics
│ └── main.py # FastAPI routes
├── data/
│ └── outdoor_history.csv # 30,684 real ERA5 rows
├── scripts/
│ └── fetch_real_data.py
└── web/ # Static UI
🔓 Why Does Open Innovation Matter?
Frost and planting decisions are tabular 📋. They belong in a spreadsheet, not a chat thread. 💬❌
| 🔒 Closed approach | 🌱 Open approach (PLOT) |
|---|---|
| Garden API locks your coordinates to a vendor | Open-Meteo: free ERA5, no key |
| Chat model guesses from prose | TabPFN: probabilities over features |
| Can't explain why without prompt engineering | garden: 83%, wait: 9%, walk: 8% |
| Per-token cost at scale | Tabular fit once, predict cheaply |
| Model swap = rewrite the app | Swap TabPFN version, keep the CSV |
🔐 Privacy: we don't store your location. Weather is fetched per request. Training table ships with the repo and is auditable.
🏕️ Offline path: the CSV + open model path means this could run on a laptop in the backcountry with cached rows. That's the Touch Grass future I want.
A closed chat API wouldn't know what to do with days_since_last_frost: 208. TabPFN was built for exactly that column. 🎯
🏆 What I'd tell judges
| 🌿 Theme fit | Screen is the shortest part; checklist ends at the door |
| 🧠 Real open AI | TabPFN on 30k+ ERA5 rows, not a wrapper |
| 📝 Honest labels | Heuristics documented, not pretending it's human survey data |
| 🚀 Shipped | Live URL, GitHub, Render deploy |
| 🚫 No chatbot cosplay | Tabular foundation model where it actually belongs |
🤖 My Agent Session
Built with TabPFN docs + Open-Meteo API. Happy to answer questions in the comments. 💬
🎁 Prize Categories
- 🧠 Best Use of TabPFN - core classifier on real frost/weather tables
- ☁️ Best Use of Render - production deploy at plot-touch-grass.onrender.com
🔗 Links
| Resource | URL |
|---|---|
| 🚀 Live app | plot-touch-grass.onrender.com |
| 💻 GitHub | PranjalManhgaye/plot-touch-grass |
| 🌦️ Open-Meteo | open-meteo.com |
| 🧠 TabPFN | docs.priorlabs.ai |
| ☁️ Render | render.com |
Thanks for reading. Now go touch some grass. 🌿👋


Top comments (11)
How often do you retrain the TabPFN model with new weather data to keep the predictions up‑to‑date?
TabPFN fits at startup on our ERA5 history CSV; today's weather row is fetched live from Open-Meteo per request. Full dataset refresh = re-run fetch_real_data.py + redeploy. No scheduled retrain yet; that's a solid v2 idea.
thanks
Nice Work!
Nice build.
Great Build!!
solid work!
Really thoughtful post! Documenting real-world engineering hurdles and actionable solutions like this brings immense value to the community.
thanks, it means a lot
Great project 👍
Impressive !
Great work! The project looks impressive. 👏