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
ποΈ Hero - PLOT landing page

π¦οΈ 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. π