Inspiration
Urban farming is underutilized; not because of lack of land or interest, but because of coordination and knowledge. Individual farmers operate in isolation: everyone grows the same popular crops, creating local oversupply in some areas and gaps in others. New farmers don't know what to grow, when to grow it, or how to manage their soil. Surplus has nowhere to go. The result? Wasted potential, wasted food, and a fragmented local food system.
We asked: what if we treated every urban farm as a node in a living network — and optimized the whole system like mycelium coordinates a forest?
What it does
MyCelium is an urban farming coordination platform that assigns crop production across a distributed network of farms using comparative advantage and integer linear programming. Each farmer is a node. Nodes contribute local data — plot size, soil conditions, tools, budget — and receive precise instruction bundles telling them exactly what to grow, how much, and when.
The Core Loop
- Farmer joins → enters plot variables (size, soil pH/moisture/temperature, tools, budget)
- Data enters the network → system builds a digital twin of every farm
- Optimization engine runs → assigns crops across all nodes to maximize collective output while respecting constraints
- Each farmer receives an instruction bundle → what to grow, quantities, timing, expected yield
- Farmers harvest and deliver to local hubs → community distribution points (schools, community centres)
- Hub Currency is earned → backed by real food production, spent to acquire food from the network
- Cycle repeats → the network improves with every new node and every harvest
Two Interfaces
Farmer App (Mobile-First)
- Landing → animated network visualization showing you as a potential node
- Setup → 4-step wizard: plot basics, soil conditions, climate, resources
- Suggestions → AI-ranked crop recommendations before you even join (no account required)
- Dashboard → current cycle progress, task list with due dates and tools, risk flags (frost, drought, overwatering), soil condition widgets, trend charts
- Update → log sensor readings (manual or OCR from soil meters), mark tasks complete
- Wallet → Hub Currency balance, delivery logging, transaction history, nearby hubs
Admin Dashboard (Desktop)
- Nodal Network Map → Google Maps integration showing every farm (colored by crop, sized by capacity) connected to nearest hubs via distance-constrained edges
- Charts → crop distribution, soil pH histogram, temperature variance, moisture-humidity correlation, supply vs. demand analysis
- Data Information → network-wide averages, plot/resource breakdowns, live weather API integration, optimization engine status, hub capacity tables
How we built it
Backend: Python + FastAPI
The nodal network (uniquely)
MyCelium’s nodal network has two kinds of nodes: farm nodes (growing plots—balcony, rooftop, backyard, community) and hub nodes (physical pickup points where food is dropped and collected). No node holds the “master plan.” Each farm node contributes one thing—its state: plot size, soil readings, tools, location, and (if any) current crop and cycle. Each hub node contributes demand: how much of each crop it needs per cycle. The intelligence lives in the edges and the aggregate. The system builds a single digital twin (the full graph of nodes and their state), computes which farms can reach which hubs (distance threshold), and which farms are locked (mid-cycle) vs. available for assignment. It then solves one global question: assign each available farm to exactly one crop so that total gap-filled yield is maximized and every critical hub’s demand is satisfiable by the farms that can reach it. The answer is a prescription per farm—one crop, one instruction bundle—and a supply map to hubs. So each node only ever sees its own slice: farmers get “grow this, here’s how”; hubs get “this much is coming from these farms.” The network is the thing that knows the full picture; the nodes are the thing that execute it. That’s why we call it nodal: the unit of agency is the node, but the unit of optimization is the graph.
The optimization engine is the intellectual core — a multi-stage pipeline:
Stage 1 — Suitability Scoring (scorer.py)
- Hard gates: plot size ≥ crop minimum, tool rank ≥ requirement, budget rank ≥ requirement
- Soft scoring across 12+ dimensions: pH, moisture, temperature, humidity, soil texture, drainage, NPK levels, salinity, sunlight, water quality — each scored 0–1 using
range_score()with linear decay outside optimal ranges - Unknown values default to 0.6 (slightly optimistic, penalizes data-poor farms)
- Output: N×M suitability matrix (farms × crops)
Stage 2 — Spatial Routing (router.py)
- Haversine great-circle distance between every farm-hub pair
- Reachability matrix: binary N×H (farm can reach hub within 5km)
- Farms deliver to nearest reachable hub
Stage 3 — Temporal Scheduling (scheduler.py)
- Farms classified as
locked(mid-cycle, cannot reassign) oravailable - Locked supply computed per crop and per hub
- Gap vector:
target[crop] - locked_supply[crop] - Rolling horizon: only available nodes re-optimized each epoch
Stage 4 — Integer Linear Programming (optimizer.py)
- Binary variables
x[i][c]— farm i grows crop c - Objective: maximize gap-weighted yield + inertia (stability) + farmer preference
- Constraints: exactly one crop per farm, critical hub coverage thresholds
- Solver:
scipy.optimize.milp()with branch-and-bound - Fallback: greedy assignment if ILP infeasible
- Multi-crop support: large farms split into zones (min 50 sqft each)
Stage 5 — Greedy Mid-Cycle Insert (optimizer.py)
- O(M) insertion when a new farm joins mid-cycle
- Score:
yield × (gap_weight + 1) × (1 + hub_urgency) - Considers: yield potential, network gaps, local hub shortfalls, farmer preferences
Stage 6 — Instruction Packaging (packager.py)
- Converts assignments into human-readable bundles with reasoning
- Per-crop task templates with tools, timing, and expected yield
Stage 7 — Network Reporting (reporter.py)
- Per-crop coverage (target vs supplied, gap %, surplus detection)
- Per-hub coverage (demand vs supply per crop)
- Network health: % of food targets met
- Unlocking-soon alerts, overproduction warnings
Transaction Engine (transaction_engine.py)
- Hub-as-bank model: deposits earn currency, withdrawals spend it
- Request lifecycle:
pending → options_ready → matched → confirmed - Dynamic pricing:
rate = base_rate × clamp(scarcity_ratio, 0.25, 4.0) - Hub scoring: fit score (demand gap or inventory abundance) × distance score
- Runs every 15 minutes + after every confirmation
API Surface: 40+ endpoints across 11 route modules — nodes, hubs, crops, config, coverage, optimization, requests, ledger, rates, suggestions, engine control.
Storage: JSON file-based (git-friendly, human-readable, zero-config). Farms, crops, hubs, config, assignments, readings, requests, ledger, hub inventory, current rates.
Frontend: React 18 + TypeScript + Vite
User App:
- React Router v6 with route guards (public, auth-required, onboarding-required)
- FarmContext (React Context API) for farm state + localStorage persistence
- Custom hooks:
useAsync,useTaskCompletion(localStorage-backed task states) - Component library: Button, Badge, Toggle, ProgressBar, BottomSheet, BundlePicker
- Sheets pattern: CropPickerSheet, LogDataSheet, AddPlotSheet, MenuSheet
- Responsive: mobile-first (375px baseline), desktop breakpoint at 768px
- Charts: Recharts area charts for soil reading trends
- Offline tolerance: forms draft to localStorage, sync on reconnect
Admin Dashboard:
- Google Maps via
@vis.gl/react-google-mapswith imperative marker/polyline layer - SVG icons: farms as colored circles (crop fill + status border), hubs as squares (priority color)
- Interactive: hover info windows, click detail panels, map-click farm creation
- Recharts: bar, line, scatter, histogram, stacked bar charts
- Live weather: Open-Meteo API integration for Toronto climate data
- KPI cards: farm count, network coverage %, active growing stats
Design System:
- Token-based: CSS custom properties for colors, typography, spacing, shadows, animation
- 4-layer nesting: canvas → sections → content → cards → interactive elements
- Fonts: Inter (body) + Space Grotesk (headings)
- Color palette: warm neutrals (#E8E5E0 base) + orange accent (#E8913A) + semantic colors
Data: Toronto-Area Seed Network
- 50 farms across Toronto (balconies, rooftops, backyards, community plots)
- 3 hubs: Greenwood Public School (critical, 500kg), North Community Centre (standard, 300kg), East Neighbourhood Hub (standard, 300kg)
- 10 crops: Tomato, Lettuce, Spinach, Herbs, Carrots, Kale, Peppers, Microgreens, Strawberries, Beans — each with optimal ranges, yield rates, and network target shares
- 1,500+ sensor readings (daily pH, moisture, temperature, humidity)
Infrastructure: Tailscale
Three-tier access model using Tailscale as the identity and networking layer:
| Role | Auth | Access |
|---|---|---|
| Farmer | Public Funnel URL + Supabase OAuth | Full farmer app |
| Hub Operator | Tailscale identity (zero login) | Hub dashboard only (ACL restricted) |
| Admin | Tailscale identity (full access) | All endpoints including /optimize |
Tailscale headers (Tailscale-User-Login, Tailscale-User-Name) replace traditional auth for internal users. Joining the tailnet IS the credential.
Challenges we ran into
- Optimization feasibility: Critical hub coverage constraints can make the ILP infeasible when too few farms are available. We implemented a graceful fallback chain: full ILP → relaxed constraints → greedy assignment.
- Multi-crop splitting: Dividing large farms into zones while keeping the optimizer tractable required a hybrid approach — ILP for primary assignment, then greedy slot-filling for additional crops.
- Dynamic pricing stability: Scarcity ratios can swing wildly. We clamped rates between 0.25× and 4× base to prevent economic instability.
- Real-time visualization: Keeping the nodal network map performant with 50+ markers, edges, and info windows required an imperative rendering layer rather than React-managed components.
- Mobile-first constraints: Building a data-heavy farming app for outdoor use on small screens meant careful attention to touch targets (44px min), outdoor readability (4.5:1 contrast), and offline tolerance.
Accomplishments that we're proud of
- The optimizer actually works. Comparative advantage emerges naturally from the math — the LP assigns crops where they'll thrive, not where they're popular. A 20-farm network solves in milliseconds.
- End-to-end data loop. A farmer can register, receive crop assignments with reasoning ("Suitability 87% for Spinach — your soil pH 6.5 is optimal, network gap 34% unfilled"), log conditions, complete tasks, deliver food, and earn currency.
- The network effect is real. Every node that joins improves the optimizer's model, diversifies supply, and fills gaps. We can demonstrate this live — a new farm joins, the network visibly rebalances.
- Zero-auth internal access. Tailscale identity headers mean hub operators and admins never see a login screen. Membership in the tailnet is the credential.
What we learned
Operations research > ML for this problem. Integer Linear Programming guarantees constraint satisfaction with zero training data. Comparative advantage emerges from the math, not from a model. ML layers can come later once historical yield data accumulates.
- The "node joins → network rebalances" moment is the pitch. Everything else supports that one visual.
- Hub Currency works because it's backed by real production. You can't mint it from nothing. You earn it by growing food and delivering it. This prevents the inflation and gaming problems that plague most alternative currency systems.
- Urban farming's real bottleneck is coordination, not motivation. People want to grow food. They just need to know what to grow, and they need somewhere for the surplus to go.
What's next for MyCelium
- ML yield estimator: Train on historical yield data to improve suitability predictions
- Demand forecasting: Predict hub demand from community patterns
- Sensor kit integration: Bluetooth soil sensors for automated data collection
- AR plot scanning: WebXR-based plot measurement for instant area calculation
- Mutual trades: Food-for-food exchanges with net currency settlement
- Multi-region expansion: Federated networks with cross-region food routing
Built With
- Backend: Python, FastAPI, SciPy (MILP optimizer), PuLP
- Frontend: React 18, TypeScript, Vite, Recharts, Google Maps API
- Infrastructure: Tailscale (networking + identity), Supabase (auth)
- Data: JSON file storage, Open-Meteo weather API
- Algorithms: Integer Linear Programming, Haversine distance, greedy insertion, dynamic pricing via scarcity ratios
Tech Stack
| Layer | Technology |
|---|---|
| Optimization Engine | Python — ILP via SciPy MILP (branch-and-bound) |
| API Server | FastAPI with 40+ endpoints |
| Frontend (User) | React 18 + TypeScript + Vite |
| Frontend (Admin) | React + Google Maps + Recharts |
| Networking | Tailscale (private overlay + public Funnel) |
| Auth | Tailscale identity headers + Supabase Google OAuth |
| Storage | JSON files (git-friendly, zero-config) |
| Weather | Open-Meteo API |
| Spatial Math | Haversine great-circle distance |
| Design System | CSS custom properties, Inter + Space Grotesk |

Log in or sign up for Devpost to join the conversation.