DEV Community

SIKOUTRIS
SIKOUTRIS

Posted on • Originally published at carbon-badge.com

How I Built a Carbon Footprint Badge for Websites

How I Built a Carbon Footprint Badge for Websites

Last year, I stumbled on a concerning statistic: the average website produces 0.81g of CO2 per page view. That's not trivial when you multiply it by billions of daily page loads.

I decided to build something that would help developers visualize—and care about—their site's environmental impact. Enter: Carbon Badge.

The Idea

Website carbon footprint badges already exist, but I wanted something:

  1. Easy to embed on any website (single line of HTML)
  2. Automatically updated (no manual recalculation)
  3. Visually honest (green if you're good, red if you're not)
  4. Built on real data (not hand-wavy estimates)

The Data Layer: Google PageSpeed Insights API

Instead of inventing my own metrics, I decided to piggyback on Google's PageSpeed Insights API, which already measures page load performance and analyzes Core Web Vitals.

Turns out Google publishes research linking page load time to energy consumption: faster pages = less CPU work = less CO2.

const fetchPSIMetrics = async (targetUrl) => {
  const response = await fetch(
    `https://www.googleapis.com/pagespeedonline/v5/runPagespeed`,
    { url: targetUrl, key: PSI_API_KEY, strategy: 'mobile' }
  );
  const metrics = response.lighthouseResult.metrics;
  const loadTime = metrics.first_contentful_paint / 1000; // seconds
  return loadTime * CO2_PER_SECOND; // ~0.003g per second
};
Enter fullscreen mode Exit fullscreen mode

The Frontend Badge

Users get an embeddable widget with a single line of code:

<script src="https://carbon-badge.com/badge.js" data-url="https://yoursite.com"></script>
Enter fullscreen mode Exit fullscreen mode

This loads a tiny iframe that:

  1. Calls the backend API
  2. Fetches PSI metrics for your domain
  3. Calculates CO2 in real-time
  4. Renders a colored badge (green/yellow/red)

The Calculation Engine

I didn't just use raw load time. I weighted multiple performance factors:

function calculateCarbonFootprint(psiMetrics) {
  const weights = {
    loadTime: 0.4,        // Biggest factor
    firstContentfulPaint: 0.3,
    largestContentfulPaint: 0.2,
    totalBlockingTime: 0.1
  };
  const normalizedScore = Object.entries(weights).reduce((score, [metric, weight]) => {
    const normalized = Math.min(psiMetrics[metric] / 3000, 1);
    return score + (normalized * weight);
  }, 0);
  return 0.81 * normalizedScore; // 0.81g is average
}
Enter fullscreen mode Exit fullscreen mode

Handling the Cache Problem

PageSpeed Insights is slow—30-60 seconds per run. I couldn't make users wait.

Solution: Async background updates

app.get('/api/carbon', (req, res) => {
  const cached = cache.get(req.query.url);
  res.json(cached || { co2: 0.81, stale: true });
  // Background: refresh if older than 24h
  if (!cached || Date.now() - cached.updated > 86400000) {
    queue.add({ task: 'refresh_psi', url: req.query.url });
  }
});
Enter fullscreen mode Exit fullscreen mode

Users get instant results, and we refresh periodically in the background.

The Feedback Loop

Here's the clever part: when developers see their badge is red, many optimize to make it green. I've had users email saying: "Your badge motivated me to cut my page load time by 50%." That's the whole point.

What I Learned

  1. Use existing APIs. Don't reinvent metrics—build on proven data (Google PSI, Carbon Trust)
  2. Cache aggressively. External APIs are slow; cache + async updates is essential
  3. Visual feedback drives behavior. A badge is more motivating than an email report
  4. Be honest about limitations. PSI doesn't measure server hosting emissions; document caveats

Head to Carbon Badge, add your site's URL, and grab the embed code. Check back in a month—I bet you'll be optimizing.

Top comments (0)