A ProcessWire module that tracks per-page resource usage and estimates the CO₂ emissions of every front-end request. Adds a Setup → PageCarbon page in the admin with live statistics, an hourly chart, a ranked page table, and real-world CO₂ analogies.
Author: Maxim Alex Version: 1.6.1 GitHub: mxmsmnv/PageCarbon
- Estimates CO₂ emissions per request using the Sustainable Web Design Model v4
- Rates each page A / B / C / D based on milligrams of CO₂
- Tracks response size, PHP execution time, and peak memory usage
- WireCache buffer — metrics accumulate in memory; batch INSERT to DB once per hour (zero per-request DB writes)
- Bot sampling — only 1-in-N bot requests are recorded; human requests always recorded in full
- 90-day raw retention — raw rows are pruned automatically; historical data is preserved forever in a compact hourly aggregate table
- Daily maintenance runs automatically: aggregates raw data into
page_carbon_hourly, then prunes old raw rows - Hourly CO₂ bar chart for the last 24 hours (Chart.js loaded on demand, SVG fallback if unavailable)
- All-time totals combine raw + aggregate tables seamlessly
- Real-world analogies — all-time CO₂ total translated into 12 everyday equivalents (car km, espressos, kettles, phone charges, Netflix hours, emails, trees, LED bulb hours, subway trips, songs streamed, flights, Google searches)
- Top 50 pages table: CO₂ avg, range, exec time, response size, hits, rating, last seen
- Manual controls: Flush buffer, Run maintenance, Clear all data
- Storage info panel: raw row count, table size, retention window, last maintenance timestamp
- DOCX export — one-click formatted report via pure-PHP
PageCarbonDocx(no Composer, no Node.js) - Frontend API —
getStats($page)andrenderBadge($page)for use in templates - Full AdminThemeUikit integration — native
uk-card,uk-grid,uk-table,uk-buttonthroughout; respects--pw-main-colorCSS variable including dark mode
- Copy the
PageCarbonfolder to/site/modules/ - In the ProcessWire admin: Modules → Refresh → Install
- Two tables are created automatically:
page_carbon— raw request rowspage_carbon_hourly— permanent hourly aggregates
- Go to Setup → PageCarbon, visit a few front-end pages, then press Flush buffer — data appears immediately
PageCarbon/
├── PageCarbon.module.php # Main module (extends Process)
├── PageCarbonConfig.php # Config inputfields
├── PageCarbonDocx.php # Zero-dependency DOCX report generator
├── CHANGELOG.md
└── README.md
Based on Sustainable Web Design Model v4 (Wholegrain Digital, 2024):
energy_kWh = (response_bytes × 0.00000000006) // 0.06 kWh/GB — network + server + device
+ (exec_ms × 0.000000003) // CPU penalty ≈ 3 W server
+ (peak_mem_MB × 0.0000004) // RAM penalty — DRAM idle
CO₂_mg = energy_kWh × carbon_intensity × 1000
Default carbon intensity: 436 gCO₂eq/kWh (world average). Adjust in module settings.
| Rating | CO₂ per request | Notes |
|---|---|---|
| 🟢 A | < 100 mg | Excellent |
| 🟡 B | 100–300 mg | Good |
| 🟠 C | 300–700 mg | Needs improvement |
| 🔴 D | ≥ 700 mg | Heavy |
Reference: the average web page produces ~500 mg CO₂ per view (Website Carbon Calculator, 2024).
The dashboard displays all-time CO₂ totals translated into 12 everyday equivalents. Coefficients used:
| Analogy | Coefficient | Source |
|---|---|---|
| 🚗 Driving by car | 120 g CO₂/km | EU average petrol car |
| ☕ Espressos brewed | 28 g CO₂/cup | LCA studies |
| 🫖 Kettles boiled | 32 g CO₂/litre | UK grid avg |
| 📱 Phone charges | 8.2 g CO₂/charge | IEA estimate |
| 🎬 Netflix HD streaming | 36 g CO₂/hour | Carbon Trust 2023 |
| 📧 Emails sent | 4 g CO₂/email | Mike Berners-Lee |
| 🌳 Trees needed (1 year) | 21 000 g CO₂/year | avg deciduous tree |
| 💡 LED bulb on | 0.012 g CO₂/hour | 10W, world avg grid |
| 🚇 Subway trips | 35 g CO₂/trip | IEA urban transit avg |
| 🎵 Songs streamed | 0.028 g CO₂/song | Spotify sustainability report |
| 255 000 g CO₂/flight | ICAO economy class | |
| 🔍 Google searches | 0.2 g CO₂/search | Google Environmental Report |
| Setting | Default | Description |
|---|---|---|
| Enabled | true | Pause collection without dropping tables |
| Grid carbon intensity | 436 | gCO₂eq/kWh for your region. See electricitymaps.com |
| Raw data retention | 90 days | Raw rows older than this are deleted during maintenance |
| Bot sampling rate | 10 | Record 1 of every N bot requests (1 = record all) |
| Exclude templates | search, sitemap |
Templates whose pages are skipped |
| Table | Content | Size estimate | Lifetime |
|---|---|---|---|
page_carbon |
Raw request rows | ~9 MB / 1k req/day / 30 days | Pruned after retention window |
page_carbon_hourly |
Hourly averages per page | ~15 MB / year at 10k req/day | Permanent |
Bot sampling (default 1/10) reduces raw table volume significantly on high-traffic or heavily crawled sites.
Click Export DOCX in the admin footer to download a formatted report. The report is generated entirely in PHP using PageCarbonDocx.php — a zero-dependency class that builds the .docx via ZipArchive (PHP built-in). No Composer, no Node.js required.
The report includes:
- Title block with site URL, date, and carbon intensity
- Last 24-hour summary table (requests, human/bot split, CO₂, rating, exec time, response size)
- All-time summary table (totals, collecting since, retention, intensity)
- Top 50 pages by CO₂ with range, time, size, and rating badge (new page)
- Rating scale reference table
- Header (site name + date, right-aligned) and footer (page X of Y)
Use in templates to display per-page CO₂ data.
Returns stats from the raw table for the given page (human requests only). Returns null if no data.
Keys: avg_co2_mg, min_co2_mg, max_co2_mg, avg_ms, avg_kb, hits, last_seen, rating (A/B/C/D), rating_color (#hex).
$pc = $modules->get('PageCarbon');
$stats = $pc->getStats($page);
if($stats) {
echo $stats['avg_co2_mg'] . ' mg CO₂ · Rating ' . $stats['rating'];
}Returns a ready-made HTML badge. Returns '' if no data.
Styles: full (default), compact, minimal.
$pc = $modules->get('PageCarbon');
echo $pc->renderBadge($page); // full — card with all stats
echo $pc->renderBadge($page, 'compact'); // single-line pill
echo $pc->renderBadge($page, 'minimal'); // inline label onlyThe following User-Agent fragments are treated as bots:
bot, crawl, spider, slurp, facebookexternalhit, semrush, ahrefsbot, mj12bot, dotbot, yandex, bingpreview, ia_archiver, archive.org, bytespider, gptbot, anthropic, claudebot, google-extended, petalbot, dataforseobot, seznambot
- ProcessWire ≥ 3.0.227
- PHP ≥ 8.1 with
ZipArchiveextension (enabled by default in most PHP builds) - MySQL / MariaDB
MIT