This is a submission for Weekend Challenge: Earth Day Edition
What I Built
I built Thermal Guilt: a gamified smart-energy dashboard that turns HVAC efficiency into a social + economic game.
The core mechanic is a “thermal ghost” for each household:
- If your usage is significantly above neighborhood behavior, your ghost trends red and your leaderboard rank drops.
- If you stay efficient, you earn
$COOLrewards on Solana Devnet.
My goal was to make energy behavior change feel immediate and engaging, not passive. Instead of just showing charts, the app combines:
- peer pressure (anonymized leaderboard),
- AI coaching (personalized suggestions),
- and crypto rewards (verifiable claim flow).
It’s playful on purpose, but technically serious underneath.
Demo
- API:
https://thermal-guilt-api.vercel.app/ - Video walkthrough:
- Live deployment: https://thermal-guilt-web.vercel.app/
Code
- GitHub repository: https://github.com/harishkotra/ThermalGuilt
- Suggested embed in DEV post:
<repo-embed username="YOUR_GH_USERNAME" repo="YOUR_REPO_NAME"></repo-embed>
How I Built It
I implemented Thermal Guilt as a monorepo with shared domain logic and provider adapters so each integration is replaceable without rewriting app logic.
Architecture
- Frontend: Next.js 14 App Router + Tailwind + Framer Motion + Recharts
- Backend: Node.js + Express + TypeScript
- Shared logic: score + ghost + reward rules in one package reused by UI and API
- AI: Backboard orchestration + Gemini analysis
- Auth: Auth0 user auth + M2M + scoped access model
- Blockchain: Solana Devnet + SPL token claim flow (wallet-signed)
Core implementation decisions
- Single-source scoring logic I put score and reward logic in a shared package to prevent drift between what users see and what payouts do.
export function scoreToTokenReward(score: number): number {
if (score >= 95) return 100;
if (score >= 85) return 50;
if (score >= 70) return 20;
if (score >= 50) return 5;
return 0;
}
- Wallet-signed on-chain claims (non-custodial UX) The backend prepares and partially signs claim transactions using treasury authority. The user signs in Phantom/Backpack, then backend relays the signed tx.
const txPayload = await apiPost("/api/solana/claim/transaction", { walletAddress, score });
const tx = Transaction.from(Buffer.from(txPayload.serializedTransaction, "base64"));
const signed = await wallet.signTransaction(tx);
await apiPost("/api/solana/claim/submit", {
signedTransaction: signed.serialize().toString("base64")
});
Provider adapter pattern
I isolated integrations into service adapters (auth0,backboard,gemini,solana,snowflake) so I can switch providers or fallback modes cleanly.Fast local onboarding
Snowflake is currently disabled by default for easy local execution. The app still runs fully with simulated energy data and real Solana claims on Devnet.Mint bootstrap automation
I added a one-shot script to create$COOLmint + treasury ATA + initial supply:
npm --workspace @thermal-guilt/api run solana:bootstrap-mint -- --supply 10000000 --decimals 6
This prints .env values to paste directly.
APIs implemented
/api/energy/current/api/energy/history/api/ai/chat/api/ai/ghost-analysis/api/leaderboard/*/api/solana/claim/transaction/api/solana/claim/submit
UX
- Animated thermal ghost card
- Neighborhood heatmap-style ghost map
- AI coach panel
- Wallet connect + token claim
- Leaderboard + score trend visuals
Prize Categories
I’m submitting this project to:
- Best Use of Backboard
- Best Use of Auth0 for Agents
- Best Use of Google Gemini
- Best Use of Solana
- Best Use of GitHub Copilot
(Snowflake integration is implemented in the architecture and service layer, and can be switched on with env configuration. )

Top comments (0)