Inspiration
Most sustainability tools tell people about the carbon they already emitted. That timing is wrong. By the time someone reads a footprint report, the choices that produced the emissions have already been made.
We kept noticing the same pattern in our own lives. A friend would book a solo cab when the metro was a five-minute walk away. Another would pick express delivery on a single coffee even though three other orders to the same building were going out in the next hour. Nobody in those moments was against the better choice — they just didn't have the comparison in front of them at the moment it mattered.
So we framed the problem differently:
Users do not lack concern for sustainability. They lack sustainability intelligence at the exact moment decisions are made.
That single sentence became our north star. It rejected three product shapes we were tempted to build — a passive footprint dashboard, a generic "green" chatbot, and a moralizing nudge app — and pointed us toward something more useful: a real-time, multi-objective decision engine that ranks the actual options in front of you, balances sustainability against the cost/time/convenience tradeoffs that humans really make, and explains its recommendation in one sentence.
What it does
SustainRoute AI is a real-time decision intelligence layer that ranks the options in front of you on sustainability, cost, time, and convenience — and explains every recommendation in one sentence.
It is not a footprint tracker, not a chatbot, and not a generic eco dashboard. It is an optimization engine that intercepts everyday decisions before they become emissions and surfaces the best balanced alternative.
Three decision domains
Transport optimization. You enter a source, a destination, an urgency level, and a scoring profile. The platform evaluates up to eight transport modes (solo cab, pool cab, metro, bus, bike, walk, electric scooter, suburban train), drops modes that are impractical for the trip distance (e.g. walking past 4 km), normalizes the candidate set, and ranks every option with a per-dimension breakdown.
Food delivery optimization. You configure an order — restaurant category, distance, diet, packaging, delivery type, plastic cutlery flag — and the platform compares your selection against three sustainability-leaning alternatives: an eco-packaging swap, grouped delivery with compostable packaging, and a reusable container with an off-peak window. Each option is scored, ranked, and reasoned.
City impact simulation. You pick a city tier and an adoption scenario (low, medium, high) and see deterministic monthly and yearly projections — CO₂ avoided, fuel saved, packaging waste avoided, tree-equivalent — with a 12-month seasonal trend chart and a transport-shift breakdown.
How we built it
We designed the system as five sequential layers, each with a single responsibility and no upward dependencies:
Frontend → API → Sustainability Engine → Optimization Engine → Recommendation Engine → AI Explanation Layer
The math at the core
Every option is scored on four normalized dimensions on $[0, 100]$:
- $S_{\text{sus}}$ — sustainability (from heuristic dataset coefficients)
- $S_{\text{cost}}$ — cost, inverted so higher is better
- $S_{\text{time}}$ — time, inverted so higher is better
- $S_{\text{conv}}$ — convenience
Cost and time are normalized within the candidate set so the comparison stays contextual:
$$ S_{\text{cost}}^{(i)} = 100 \cdot \left(1 - \frac{c_i - \min_j c_j}{\max_j c_j - \min_j c_j}\right) $$
The final score for an option $i$ under a profile $p$ is a convex combination of the four dimensions:
$$ F_i^{(p)} = w_{\text{sus}}^{(p)} S_{\text{sus}}^{(i)} + w_{\text{cost}}^{(p)} S_{\text{cost}}^{(i)} + w_{\text{time}}^{(p)} S_{\text{time}}^{(i)} + w_{\text{conv}}^{(p)} S_{\text{conv}}^{(i)} $$
subject to
$$ \sum_{k \in {\text{sus},\text{cost},\text{time},\text{conv}}} w_k^{(p)} = 1, \quad w_k^{(p)} \geq 0. $$
The default balanced profile uses
$\left(w_{\text{sus}}, w_{\text{cost}}, w_{\text{time}}, w_{\text{conv}}\right) = (0.40, 0.30, 0.20, 0.10)$,
which mirrors the weighting documented in docs/scoring_logic.md. We
ship five profiles in total — balanced, eco_priority,
budget_priority, speed_priority, comfort_priority — and each one
satisfies the sum-to-one constraint.
The optimal recommendation is simply
$$ i^{*} = \arg\max_i F_i^{(p)}. $$
Because the function is linear and the weights are deterministic, every recommendation is reproducible from the inputs and explainable down to a per-dimension contribution.
Backend stack
We built the API as a thin orchestration layer in FastAPI (Python 3.10+, Pydantic v2). The deterministic intelligence lives in three engines:
sustainability_engine.py— emission/cost/time/convenience evaluation per mode or food/delivery configurationoptimization_engine.py— weighted blend, normalization, rankingrecommendation_engine.py— reasoning bullets, tradeoff summary, baseline vs recommended selection
A separate services/ai_service.py is the only file allowed to call OpenAI.
It receives the structured output of the recommendation engine and produces
one premium one-line explanation. If the API key is missing or the call
fails, it returns the deterministic tradeoff sentence and tags the response
ai_explanation_source: "fallback". The platform never breaks because of
the AI layer.
Endpoints:
POST /optimize-transportPOST /optimize-foodGET /city-impactGET /healthandGET /profiles
Frontend stack
Next.js 15 with the App Router, TypeScript, Tailwind CSS, shadcn-style primitives over Radix UI, Framer Motion for animations, and Recharts for visualization. We built six routes:
/— cinematic landing with hero, live optimization preview, and an architecture diagram/dashboard— KPIs, 12-week avoided-emissions trend, profile pie, live recommendation feed/transport— input panel, AI explanation panel, tradeoff matrix bar chart, ranked option cards with score rings and per-dimension bars/food— same pattern for food/delivery, comparing your selection against three sustainability-leaning alternatives/city-impact— animated city/scenario projections with a 12-month seasonal trend/analytics— operator view with acceptance trend, profile distribution, and a recommended-vs-baseline radar chart
Every score in the UI traces back to a number in a dataset, which is what makes the experience feel like infrastructure rather than a demo.
Datasets — the intelligence foundation
Seven versioned JSON files in datasets/:
transport_emissions.json— 8 modes withemission_per_km,cost_per_km,convenience_score,avg_speed,occupancy_factor,sustainability_rating, and a tier colorfood_impact.json— additive modifiers for packaging, sourcing, diet, delivery mode, and penalties; restaurant categories with base scoresdelivery_rules.json— urgency, consolidation, packaging, warehouse, and fleet rules plus five efficiency tierssustainability_weights.json— the five profiles abovecity_simulation.json— Tier 1 and Tier 2 cities, three adoption scenarios each, monthly + yearly projections, seasonal multiplierstransport_route_samples.json— six pre-computed scenarios for instant demo moderecommendation_templates.json— reusable explanation templates with{{variable}}placeholders and a fallback
Challenges we ran into
Designing a scoring function that survives edge cases. Our first attempt used global normalization and produced obviously wrong rankings when one outlier mode skewed the scale. Switching to per-set min-max normalization within the candidate options fixed it and is the single most important formula choice in the system.
Keeping AI from creeping into the decision path. Every brainstorm was tempted to ask the LLM "what's the best option?" Resisting that and forcing the AI service to receive only a structured analytical context — emission deltas, time deltas, scores, weights — was the architectural decision that made everything else simpler.
Making the AI layer truly optional. It was easy to write code that silently broke when
OPENAI_API_KEYwas missing. We refactoredai_service.pyso a missing key, an SDK import error, a network failure, or a timeout all return the deterministic sentence and tag the source asfallback. The frontend reflects this honestly with a visible badge — no fake AI.Filtering impractical modes. A naive system happily recommends walking 22 km. We added distance cutoffs (
walk ≤ 4 km,bike ≤ 12 km,electric_scooter ≤ 18 km) before scoring. Small fix, huge usability gain.Production-quality UI on a hackathon clock. We anchored the design to references we trust (Linear, Vercel, Stripe, Notion AI) and built a small library of premium primitives —
ScoreRing,ScoreBars,KpiTile,AIExplanation,ProfilePicker— early. Once those felt right, every page assembled itself in a few hours.Server vs client component boundaries. Next.js 15's App Router tripped us up on the dashboard route when we passed Lucide icon components across the RSC boundary. We resolved it by marking data-driven pages
"use client"and keepingmetadatain the root layout. Build went from broken to clean in a single commit.Dataset shape choices have consequences. We rewrote the food modifier model twice. The third version — additive integer modifiers on a base score per restaurant category, clamped to $[0, 100]$ — was simple to reason about, easy to extend, and produced explainable reasoning bullets without extra code. It's now the pattern we'll use for every future domain.
Accomplishments that we're proud of
A working end-to-end prototype where you can:
- enter a route and watch eight transport modes get ranked across four scoring dimensions with explainable per-dimension contributions
- configure a food order and see three eco-leaning alternatives score against it, with packaging and grouped-delivery tradeoffs surfaced
- toggle adoption scenarios for a city and watch monthly and yearly projections recompute with seasonal multipliers
- read every recommendation as one polished sentence — generated by the AI layer when available, deterministically when not
The optimization core is reproducible, the AI layer is non-blocking, and every coefficient lives in a versioned dataset. That's the version of "sustainability infrastructure" we wanted to build.
What we learned
A few things we didn't know going in.
Multi-objective optimization beats single-axis optimization for adoption. Maximizing only sustainability produced recommendations every tester ignored within minutes. Encoding cost, time, and convenience as first-class dimensions changed the project's identity from "eco app" to "decision infrastructure".
AI is most useful when it does the smallest possible job. Our first prototype let the LLM rank options. It became unexplainable, non-deterministic, and fragile under network failures. Confining the LLM to a one-sentence explanation layer made the rest of the system simpler, testable, and trustworthy.
Datasets are architecture. Once every coefficient lived in versioned JSON, swapping heuristics for verified APIs (Climatiq, Maps, etc.) became a config change. We started treating
datasets/as a first-class layer alongside backend and frontend.Min-max normalization makes ranking feel fair. Comparing a walk with a cab only makes sense when cost and time are normalized within the candidate set. That single change eliminated almost every "this recommendation is silly" complaint from QA.
Premium UI changes how people read sustainability content. The same recommendation phrased on a Linear-quality dark surface reads as advice from infrastructure; the same words on a green-leaf cartoon read as guilt from a poster. Tone is carried by typography and spacing as much as by language.
Practicality filters matter as much as the math. Recommending a 22 km walk is technically optimal on sustainability and cost, and catastrophic on usability. Adding small distance cutoffs (
walk ≤ 4 km,bike ≤ 12 km,electric_scooter ≤ 18 km) gave us the largest perceived-quality jump of any single change.Determinism is a feature. We can give the same inputs to the optimizer at any time of day, with or without the LLM, and get the same recommendation. That predictability is what makes the system feel like infrastructure rather than a demo.
The architecture has to survive the AI layer being absent. A missing API key, an SDK import error, a network failure, or a timeout all need to fall back to a deterministic explanation transparently. We even tag the response source so the UI can label it honestly. No fake AI.
What's next for SustainRoute AI
The system was built so that adding scope is mostly composition, not rewriting. The roadmap below reflects the order we'd actually pursue.
Near term
- Live data sources. Replace the heuristic geo helper with a Maps / Distance Matrix API, and replace the per-mode emission coefficients with a verified carbon API such as Climatiq. The optimization core does not change — only the inputs do.
- Real-time signals. Feed live traffic, weather, and dynamic pricing into the cost and time inputs of the sustainability engine so the recommendation reflects the actual moment, not a static estimate.
- Personalized weight profiles. Learn $w^{(p)}$ from each user's historical choices instead of picking a preset. Concretely:
$$ w^{(p)}{t+1} = (1 - \alpha) \cdot w^{(p)}_t + \alpha \cdot \hat{w}^{(p)}{t+1} $$
where $\hat{w}^{(p)}_{t+1}$ is fit from the user's recent acceptances and rejections. Profile presets remain as cold-start defaults.
- Streaks, goals, and contribution tracking. Surface how each individual decision contributes to a user's monthly goal and to the city projection, without becoming a guilt-driven tracker.
Medium term
- New decision domains. The same engine pattern fits e-commerce
checkout (consolidate vs split, fast vs eco shipping), energy routing
(which time-of-use slot to charge an EV), and smart-home decisions
(HVAC schedules). Each domain is a new
engineand a new router file. - Persistence and identity. When streaks, goals, history, and acceptance analytics become real, plug in a database (Postgres) and authentication. Today the system is intentionally stateless to keep the prototype focused on intelligence.
- Carbon attribution. Move from heuristic ratings to attributed carbon: $\text{CO}_2 = \sum_k a_k x_k$ where $x_k$ are activity levels and $a_k$ are verified emission factors. Keep the optimizer's blend formula, replace its sustainability input.
- Explainability dashboard. Every recommendation already carries per-dimension contributions. The next step is to visualize the contribution of each weight × score term:
$$ \text{contribution}_k^{(i,p)} = w_k^{(p)} \cdot S_k^{(i)} $$
so a user can see why a recommendation won, term by term.
- Multi-language and regional packs. Coefficients, currency, and copy localized per region. Datasets are already versioned, so this is a packaging problem, not an engineering one.
Long term
- Enterprise mode. The same engine can power corporate sustainability routing — travel and procurement decisions evaluated with custom weight profiles, integrated into existing booking and procurement flows via API. Audit trails fall out of the deterministic core for free.
- Smart-city integrations. Public-sector partners can plug in real ridership and emission data, and the platform projects city-scale outcomes from individual decisions in real time.
- Behavioral research APIs. Because every recommendation is reproducible and every acceptance is logged with the active profile and tradeoff, the system becomes a controlled environment for studying what kinds of nudges actually shift behavior.
- Open dataset standard. Publish
transport_emissions.json,food_impact.json, anddelivery_rules.jsonas a versioned schema others can implement. The optimization engine becomes the reference implementation.
The thread running through every item: keep the optimizer deterministic, keep the AI layer optional, and let the dataset layer carry the change.
Built With
- fastapi
- framer-motion
- json
- lucide
- nextjs
- openai
- pydantic
- python
- radix
- react
- recharts
- shadcn
- tailwindcss
- typescript
- uvicorn