The Energy Balance Model
The Earth's temperature is set by a single tug-of-war:
Energy in — sunlight the planet absorbs (the rest is reflected by clouds, ice and bright land — the albedo).
Energy out — heat the planet radiates back to space, which grows as it warms.
If more comes in than goes out, the planet warms; if more goes out than in, it cools. It settles at the equilibrium temperature where the two exactly balance. Add greenhouse gases and you throttle the outgoing heat, forcing a new, warmer balance.
This is the Energy Balance Model — the simplest climate model there is, and the root of every bigger one. Below you step the planet's temperature forward one year at a time and watch it find its balance, live in WebAssembly.
begin
using PlutoUI, WasmMakie
endOne equation
With temperature T (in °C) and an ocean heat capacity C:
$$C\,\frac{dT}{dt} = \underbrace{(1-\alpha)\frac{S}{4}}_{\text{absorbed sunlight}} \;-\; \underbrace{(A + B\,T)}_{\text{heat radiated out}} \;+\; \underbrace{a\,\ln(\mathrm{CO_2}/280)}_{\text{greenhouse forcing}}.$$
We take one-year Euler steps. With pre-industrial CO₂ (280 ppm) the planet balances at about 14 °C; raise CO₂ and watch it climb to a warmer equilibrium.
CO₂ concentration (ppm) =
starting temperature (°C) =
years to simulate =
let
# all constants derived/fixed here; the year loop below uses only locals, so the
# bonded values never enter the loop body (keeps it cleanly wasm-compilable)
absorbed = 239.4 # (1-albedo)*S/4, W/m^2
A = 214.6 # outgoing-radiation offset, calibrated so 280ppm -> 14C
B = 1.77 # how fast outgoing heat grows with temperature
C = 51.0 # heat capacity (W*yr/m^2/K)
forcing = 5.35 * log(Float64(co2ppm) / 280.0) # greenhouse forcing of this CO2 level
T = Float64(t0i)
Ts = Vector{Float64}(undef, nyears + 1)
ts = Vector{Float64}(undef, nyears + 1)
Ts[1] = T
ts[1] = 0.0
for y in 1:nyears
dT = (absorbed - (A + B * T) + forcing) / C
T = T + dT
Ts[y + 1] = T
ts[y + 1] = Float64(y)
end
fig = Figure(size = (600, 350))
ax = Axis(fig[1, 1])
lines!(ax, [0.0, Float64(nyears)], [14.0, 14.0]) # the pre-industrial baseline
lines!(ax, ts, Ts) # the planet finding its balance
fig
endebm_stats = let
# Structurally identical to the carbon-and-warming readout (a known-good pattern):
# forcing recomputed INSIDE the year loop, every reported number is a final-state
# value that depends on all three sliders (co2ppm, nyears, t0i). The earlier version
# mixed an all-bonds value (final T) with a co2ppm-only value (equilibrium) in one
# tuple, which left the other two bonds partially baked.
absorbed = 239.4
A = 214.6
B = 1.77
C = 51.0
co2 = Float64(co2ppm)
T = Float64(t0i)
for y in 1:nyears
forcing = 5.35 * log(co2 / 280.0)
T = T + (absorbed - (A + B * T) + forcing) / C
end
(floor(T * 10.0) / 10.0, floor((T - 14.0) * 10.0) / 10.0)
end;After 150 years at 420 ppm CO2 the planet has warmed to about 15.2 C – a warming of 1.2 C above the pre-industrial 14 C baseline. The balance point is set by the CO2 level alone, no matter what temperature you start from.
Why this tiny model matters
Every climate model, from this one line to the supercomputer simulations behind IPCC reports, rests on the same accounting: energy in minus energy out. The Energy Balance Model captures the headline result — more CO₂ means a warmer equilibrium — with arithmetic you can watch run.
It also introduces the idea of a forcing (a push on the balance, here from CO₂) and a response (the temperature change it causes). The ratio between them is the planet's climate sensitivity, the single most important number in climate science — and the subject of the next few lessons.
Appendix
Henri Drake's MIT lecture builds this with Plots.jl and an ODE integrator. WebAssembly can't run those in the browser, so we take one-year Euler steps by hand and draw with WasmMakie. The physics — absorbed sunlight, linearized outgoing radiation, logarithmic CO₂ forcing — is exactly the textbook Energy Balance Model.