How Canopy works, and where it stops
Built for OregonHacks 2026 (“help people reconnect with nature or support environmental health”). This page is the honesty layer — what’s real, what’s adapted from where, and what Canopy deliberately refuses to claim.
The problem
Most nature-and-wellbeing apps ask you to trust a vibe: log some outdoor time, get a green checkmark, feel good about it. Two things are usually missing. First, the app never tells you whether outdoor time is actually doing anything for you — it just assumes the general finding in the literature applies to your life. Second, “go outside” is generic advice; it doesn’t tell you where, specifically, near you.
Canopy is for someone willing to log a few taps a day who wants two honest things back: a real read on whether their own outdoor time tracks their own mood and energy, stated with the confidence their data actually supports — and real, mapped green space near them, not a generic suggestion.
Fitting the prompt
The prompt is an “or”: reconnect with nature, or support environmental health. Canopy does both halves rather than picking one. The daily log and mood/energy correlation is the personal-habit half — reconnecting with nature, made concrete and measurable instead of a vague wellness goal. The live Overpass query against real OpenStreetMap data is the environmental-health half — an actual, verifiable claim about the user’s real geography (“here is a real nature reserve 900m from you”), not invented content. The two halves are connected, not bolted together: a declining outdoor-time trend is what triggers the green-space nudge.
The statistics — adapted from Skin Diary
Every correlation on the signal panel is deterministic arithmetic: Spearman rank correlation, a closed-form Student’s t-test (hand-rolled incomplete beta function, not a library), Welch’s t-test for the outdoor-vs-none comparison, and Benjamini-Hochberg false-discovery-rate correction across every hypothesis tested in one diary. No LLM and no model touches this number — the same diary always produces the same finding, and the arithmetic is auditable in src/lib/stats/.
This is carried over from an earlier, more mature project, Skin Diary (~/Projects/skin-diary), which solved the same underlying problem — honest, sample-size-aware correlation reporting for a personal diary — for skin-tracking instead of nature-tracking. src/lib/stats/distributions.ts and src/lib/stats/correlation.ts are adapted close to verbatim (they’re general-purpose statistics with nothing skin-specific in them); src/lib/analysis/engine.ts keeps the same shape — build (factor, outcome, lag) pairs from real calendar dates, correct the whole family, gate on sample size — rescoped from Skin Diary’s 7-factor, 147-hypothesis search down to Canopy’s single factor (minutes outdoors) against two outcomes at two lags, plus two group comparisons: at most six hypotheses per diary. Skin Diary’s photo-brightness confound check (partialCorrelation) was dropped — Canopy has no equivalent confound to control for.
The honesty rule that matters most: nothing is reported above “not enough data” below 8 paired days, however clean the pattern looks over fewer. Every claim states its n next to it, and a tree-ring graphic literally draws fewer, fainter rings for a thinner sample — so a finding over 3 days cannot visually pass for one over 30.
The green-space query — adapted from Nirog
The “real green space near you” panel is a live network call to https://overpass-api.de/api/interpreter, OpenStreetMap’s Overpass API — real parks, gardens, forests, nature reserves and protected areas, queried by tag (leisure=park, landuse=forest, boundary=protected_area, and similar) within a radius of wherever you say you are. Nothing about it is simulated: the query, the response, and the distances are real OSM data, parsed and sorted client-side.
The query-building and parsing pattern — one union query over a handful of tag/value pairs, filtered by around:<radius>, requesting out center tags so ways and relations come back with a computed centroid, then normalising/deduping the elements — is adapted from the Nirog patient-app project’s healthmap_fetch.py (~/Projects/Nirog App/healthmap_fetch.py), which used the identical approach against Overpass to pull real PHC/CHC/hospital coordinates across India. Canopy re-targets the same technique at leisure/landuse/natural/boundary tags instead of amenity tags, and ports it from Python to TypeScript, running server-side in a Next.js API route.
If the live call fails — and the public, unauthenticated Overpass instance genuinely does 504 under load; that happened on the very first call made while building this — the app tries a mirror endpoint, and only after both fail does it fall back to a bundled snapshot. That snapshot is a real response captured live against Portland, OR, not fabricated data, and the UI always discloses which of the three happened rather than quietly presenting a fallback as fresh.
Honest limits
- This is not medical or ecological advice. Mood and energy self-ratings are coarse, and Canopy does not diagnose, treat, or claim any clinical effect.
- Correlation is not causation. If your mood tracks your outdoor time, dozens of confounds could explain it just as well — weather, day of week, what else was happening that day. Canopy reports association, states its sample size, and stops there.
- OpenStreetMap completeness varies enormously by region. A well-mapped city park shows up reliably; a rural area or a country with less OSM contributor activity may show far fewer features than actually exist on the ground. An empty result near you is a real absence-of-data finding, not proof there’s no green space nearby.
- The demo diary is synthetic, and says so everywhere it appears. It exists so the payoff view is visible on first load; every entry in it is generated with a documented planted signal (see
src/lib/demo/seed.ts) and never presented as real.
Reset
If you’ve started a real log and want to see the demo diary again: