Skip to main content

Carbonwise, an AI carbon agent that grows a garden you can kill

Share:XLinkedInHN
Cover for Carbonwise, an AI carbon agent that grows a garden you can kill

The question a user actually asks

"I flew Bengaluru to Delhi last weekend and I ate mostly chicken this week. How bad was I."

That is the first prompt every Carbonwise user types into the chat box, more or less verbatim. Nobody opens the app and asks for a Sankey diagram of their annual Scope 3. They ask a specific, guilty, small question about a specific week. The whole product is shaped around answering that kind of question in one turn, and then giving the user a reason to come back tomorrow when they have not flown anywhere and they ate lentils.

Carbonwise is a Next.js app I built on and off across a few weekends. It is public on GitHub as kaushiksaravanan/carbonwise. There is a chat surface backed by an LLM, plus a small onboarding quiz that tunes the model's assumptions about you. Underneath all of that, there is a garden that reacts to how often you show up.

Why a garden and not a bar chart

The default reflex for anything with "carbon" in the name is a bar chart. Or worse, a stacked bar chart with a legend, split by category, calibrated in kilograms of CO2 equivalent. I built that first. It looked correct and it did nothing for behavior.

The problem with a carbon bar chart is that it makes you feel bad on a Tuesday and then it sits there being the same shape on Wednesday. There is no state. The chart on the day you drove to work is indistinguishable from the chart on the day you walked, unless you squint at the numbers. And the numbers are small. One walked commute is 3 kg. One flight is 300. The bar for the walked day is a pixel, if that.

So I replaced the chart with a garden. Every day you come in under your daily carbon budget, the garden grows: flowers accumulate, and every ten flowers become a new tree, bumping the level. Every day you go over, the garden's health ticks down a few points and the plants read as wilting. Plants have three stages (sprout, sapling, canopy) assigned by planting order, so the oldest ones are the most mature. The garden is a seeded SVG scene, not a Lottie file, so the layout is deterministic per user and the grow animation runs from a scoped CSS keyframe when the page mounts.

This works for two reasons that a bar chart cannot copy.

The first is that state persists visually. Your garden today looks different from your garden yesterday, even if your carbon number barely moved. The change is legible at a glance because it is a picture of a living thing, and living things read as different when they change posture.

The second is that neglect is a first-class outcome. A bar chart cannot show you what happens when you stop using the app. A garden can, and it does, and it is uncomfortable in the specific way that Tamagotchis were uncomfortable in 1997. The point of an environmental app is not to make you feel virtuous when you open it. It is to make the closed state feel like a loss.

I did not invent this pattern. Forest, the phone-lockdown app, has been doing it for a decade, and Duolingo's streak flame is the same shape of idea. What I wanted to see was whether the metaphor still worked when the underlying thing being tracked was not screen time or Spanish verbs but kilograms of an invisible gas.

Early answer, from a sample size of me and about fifteen friends who agreed to try it: yes, mostly. Two of them told me the wilt animation genuinely bothered them. That is the whole product.

Five questions, not fifty

The other early call I made was on onboarding.

Every carbon calculator on the internet is fifty fields long. You enter your annual mileage and your heating fuel, and by the time the form asks about dishwasher use per week you have already given up. I have filled out three of these. I have never come back to any of the tools that made me do it.

Carbonwise onboards you with five questions. They are lifestyle-shaped, not accounting-shaped. Something like "how do you usually get to work" with picture-labeled buttons underneath. You tap through the whole thing in under a minute.

The five answers do not produce a precise carbon number. They produce a rough persona that gets injected into the system prompt when you first chat with the model. When you later ask about the Bengaluru-Delhi flight, the model already knows you fly twice a year and eat meat four days a week, so it can locate the flight against that baseline without asking for your annual mileage first.

The reason I picked five over fifty is not that fifty is inaccurate. It is that fifty is unfinishable. Every field past the third is a place to bounce, and the precision from asking about dishwasher use is destroyed by everyone who never makes it to the dishwasher question.

Gemini can do a surprisingly good job of estimating a rough personal footprint from five lifestyle signals plus one specific week's activity, if you tell it that the answer needs to be honest about its own uncertainty. The model returns a range with a mid-point, not a single number pretending to be accurate to 0.01 kg. Users seem fine with that. Nobody has asked me to add more onboarding questions.

Gemini keys from cycle zero, via CipherStack

Carbonwise talks to Gemini. Gemini has a free tier that will keep a personal project alive for a long time, but the daily quota is easy to hit before dinner if a few users are chatting. I have written elsewhere about why I stopped putting one Gemini key in one project's .env file. See the CipherStack LRU rotation post for the state machine.

Carbonwise was the first side project I built after CipherStack was live, and I wired the vend call in from the very first commit. The Next.js server route that talks to Gemini does not read GEMINI_API_KEY from the environment. It hits https://cipherstack.kaushik.cv/api/v1/vend/gemini with a service token, gets back a key and a key_id, uses the key for exactly one Gemini call, then reports usage back to the vault with the token counts.

If the key comes back 429, Carbonwise reports the rate-limit error and CipherStack cools that key down for 60 seconds. The next request gets a different key from the pool of eight. In practice, Carbonwise has never failed a Gemini call because of quota, even in the week I demoed it to friends and they were all typing at once.

The nice thing about wiring it in from the start is that I never wrote the .env version. There was no migration. Every commit in the git history has the vend call.

Deployment: Vercel, with a proxy

The app is on Vercel. The only wrinkle is that the LLM route runs through a small proxy layer so the CipherStack service token never ships to the browser. The token lives in the Vercel environment. The client calls /api/chat. That route vends a key, calls gemini-2.0-flash via generateContent, and returns the model's reply as JSON. It reports usage to the vault after the response comes back.

The honest bundle-audit note

At some point across the build cycles, I ran a multi-cycle bundle audit on Carbonwise. First Load JS came down by 44 percent. I know that number because I wrote it down in the cycle notes.

What I do not remember is which single change moved the needle most. It was somewhere in the interaction between an unused dependency I removed and the Lottie player getting pulled out of the initial route. I did those in the same set of cycles and I did not measure them independently. If I had to guess, I would say the Lottie split, because Lottie players are heavy and the garden is only visible after auth. But I am not sure.

I am leaving this in the post because the alternative is inventing a clean before-and-after. The honest version of my memory is that I got the win and wrote the number down, then lost the attribution. That is a normal shape for a solo project.

What the garden does not yet feed back to the model

Here is the piece I have not shipped, and the piece I think about most.

The garden knows your health score, your tree and flower counts, your level, and the last day the garden was watered. None of this is in the prompt to Gemini.

When you ask about the flight, the model sees your five-question persona and your current question. It does not see that you have come in under budget every day for two weeks and your garden is level 4 with the earliest trees at the canopy stage. It also does not see that you overshot for four days and the health score dropped into the wilting range. The model's tone is the same either way.

The version I want to build treats the garden state as a chunk of context the model reads on every call. The prompt gets an extra line, something like "user is level 4, health 82, three canopy trees, mood thriving". The model can then choose whether to praise the streak or gently roast the wilt, in the same conversational voice.

I have not built that yet because the metaphor and the model are still two different systems in my head, and connecting them changes what the app is. Once the garden influences the words, it stops being a decoration and becomes part of the reasoning surface. That is the next thing I will ship.

See also

Cite as: Saravanan, K. (2026). Carbonwise, an AI carbon agent that grows a garden you can kill. Kaushik Saravanan. https://www.kaushik.cv/blog/carbonwise-living-digital-garden