Release Carbon Badge 2.0.6 shows a lower bound when third-party sizes are hidden Changelog ↗
Open source · MIT · v2.0.6

Show the carbon cost of every page

Paste one tag into your footer. The badge measures what each page actually loaded in the visitor's browser and turns it into grams of CO₂e and a grade. No account, no cookies, no tracking.

npm i @cometweb/carbon-badge
Paste into WordPress Shopify Webflow Wix Any HTML
your-site.example
–loaded by this page
–CO₂e per page load
–grade

The real badge, measuring this page in your browser right now.

0
Cookies, storage or API calls

The default badge measures in the visitor's browser and sends nothing anywhere.

10.6 kB
Gzip, budget enforced in CI

10,585 bytes for the embed. A build over 10,700 fails.

380
Tests on every change
296 unit + 84 in 3 browsers
0
Guessed numbers

When a file hides its size, the badge shows a floor or N/D, never a grade.

Recorded, not mocked up

One tag, three looks, light or dark

Pick the style that fits your footer. The number stays the same: the badge measures once, after the page has finished loading.

127.0.0.1 · test.html · Carbon Badge 2.0.6

Recorded on 7 Nov 2026 in Chromium from the repository's own test.html, served locally with the production build. Real time, no speed-up. The labels above the badge were added for the recording.

Fail closed

It never guesses

Some files, such as third-party scripts, hide their size from the browser. Then the badge shows “at least” a number, or N/D, instead of a grade it cannot back up.

Graded badge: A+, less than 0.01 g CO2e per first load, observed-transfer SWDM v4 estimate

graded

Every resource reported its size. The badge shows grams, the method and a letter.

2 of 2 sizes visible · 36.1 kB

Lower-bound badge: at least 0.03 g CO2e, 8 of 9 resource sizes visible, no letter

at least

One image came from another origin without Timing-Allow-Origin. The visible part is shown as a floor, and the letter is withheld.

8 of 9 sizes visible · 218.1 kB visible · 0.032 g floor

N/D badge after a route change: not available, URL changed, reload this page

N/D

The URL changed after load, so the first-load timing no longer describes this view. The badge says so and offers a reload.

Also used for missing timing or an overflowed timing buffer

Real captures from Chromium on 7 Nov 2026. For the middle case the test page also loaded the repository's own screenshots plus one image from a second local origin.

SWD Model v4

Bytes in, grams out

The badge multiplies the data your page loaded by a fixed energy figure and the global average grid. Drag the slider to see how page weight changes the grade.

Try a page weight

Same formula and bands as the badge's estimate mode.

50 kB10 MB
Energy0.300 Wh
CO₂e0.148 g
ScoreA
CO₂e = data_GB × (E_operational + E_embodied) × 494
data_GB = bytes / 1,000,000,000

Energy per GB transferred

Segment Operational Embodied
Data centre 0.055 0.012
Network 0.059 0.013
User device 0.080 0.081
Total, kWh/GB 0.194 0.106

0.300 kWh/GB × 494 g/kWh = 148.2 g CO₂e per GB, or about 0.148 g per MB. Factors are pinned in model-factors.json and checked against the reference CO2.js implementation in the test suite. In estimate mode there is no green-hosting discount.

A+< 0.10 gunder 0.67 MB
A< 0.20 gunder 1.35 MB
B< 0.40 gunder 2.70 MB
C< 0.70 gunder 4.72 MB
D< 1.00 gunder 6.75 MB
F≥ 1.00 g6.75 MB or more

The letters are CometWeb's own fixed bands (carbon-badge-bands-v1), not the Sustainable Web Design Digital Carbon Rating. Megabyte equivalents assume the first-load transfer formula.

Three modes

Local by default, published when you choose

By default everything happens in the visitor’s browser. If you use CometWeb Insight, the badge can show a result you published there instead. It never starts a scan.

Mode
Data source
Network
Storage
estimatedefault, no setup
Resource Timing of the current page load, then the SWD v4 formula above.
No score requests. Only the script file loads.
None. No cookies, no localStorage.
snapshotset snapshot-id
A measurement published in CometWeb Insight, read with GET /api/public/carbon-badge/id/{public_id}.
One read near the viewport. Stale, revoked or mismatched snapshots show N/D.
None. Published results are always refetched.
apiexplicit mode="api"
A published result looked up by URL with GET /api/public/carbon-badge. Query and fragment are stripped.
Sends the page's origin and path. Do not use it on private pages.
Optional cache for non-published responses, 720 minutes by default.

"Published by CometWeb" appears only for a fresh, ready snapshot fetched over the network. It describes where the number came from, not domain ownership or certification.

Get started

Paste it once, see it on the next load

No account or API key. The script is pinned to one exact version, so the browser refuses a file that was changed.

STEP 1

Paste the snippet

Put it where the badge should appear, usually the footer.

<script type="module" src="https://cdn.jsdelivr.net/npm/ @cometweb/carbon-badge@2.0.6/dist/ cometweb-carbon-badge.esm.js" integrity="sha384-N55q73xI0VL1Hp6MQp5W SFoVwZUDUdfN5JsQvjhkzU9VLHguRtdTculN BP7uvm8l" crossorigin="anonymous"></script> <cometweb-carbon-badge theme="light" mode="estimate"> </cometweb-carbon-badge>
STEP 2

Match your footer

Use the dark theme on dark backgrounds and pick a smaller variant if you need one.

<cometweb-carbon-badge variant="compact" theme="dark" lang="pl"> </cometweb-carbon-badge>
STEP 3 · OPTIONAL

Or bundle it

The embed entry registers the element and loads network code only when needed.

$ npm i @cometweb/carbon-badge // in your app entry import '@cometweb/carbon-badge/embed';

The hash above matches the 2.0.6 file on jsDelivr and on cometweb.io, checked on 7 Nov 2026. WordPress, Shopify, Webflow and Wix steps and self-hosting are in the README.

Built to be checked

Small enough to read, tested in three engines

Open TypeScript under MIT with no dependencies. Every change and every release goes through the same checks: tests, types, size and a security audit.

296unit tests

Unit tests with a coverage gate

They cover the estimator, API parsing, caching, route changes and rendering, and they run with a pinned clock.

28× 3 browsers

Browser tests on real engines

Chromium, Firefox and WebKit run the built package, including axe checks, 200% zoom, RTL and a strict nonce-based CSP.

CO2.jsreference

Formula checked against a reference

A golden matrix generated with @tgwf/co2 0.19.0 pins the estimator's arithmetic. It proves calculation parity, not physical accuracy.

10,585of 10,700 B

A size budget that fails the build

The initial embed is measured as gzip in CI. The full SDK and the API entry have their own limits.

SRI+ provenance

Release artefacts you can verify

A release manifest records hashes for every file. CodeQL, dependency review, an SBOM and a package attestation run on GitHub.

0audit findings

Dependencies audited

CI fails on any moderate or higher advisory. Security reports go to a private email, not public issues.

1.0.010 Feb 2026 · first npm release
1.0.1030 Sep 2026 · fail-closed honesty
2.0.29 Oct 2026 · local by default
2.0.631 Oct 2026 · lower bound
What it won't claim

Honest about its limits

A footprint number is only useful if you know what it leaves out. These limits are part of the contract, not fine print.

01

An estimate, not a meter

No energy is measured. Bytes go through fixed averages, so two pages of the same weight always get the same grams.

02

One first load, not a visit

It covers what loaded before the page went quiet. Later lazy loads, return visits and whole sessions are outside the number.

03

A global grid average

494 g/kWh is a modelled global proxy with no dataset year. There is no regional grid and no confidence range.

04

Not a certificate

It is not an ESG report or a Digital Carbon Rating, and browser byte counts include a small fixed header allowance.

Show your visitors the number and how you got it

Read the code, check the formula, then paste one tag into your footer.