How Bowcast works
The physics of a
rainbow forecast
A rainbow is geometry plus weather, and both are computable. Here is exactly how Bowcast turns a numerical weather forecast into an hourly probability that you will see one: the optics, the model, and the backtests, with nothing hand-waved. Written for someone who wants the equations.
01 · Optics
Why a rainbow is always 42 degrees
A rainbow is not a thing in a place. It is a direction: the set of angles at which raindrops throw sunlight back at you most brightly. Fix that direction and everything else follows.
A ray of sunlight enters a spherical drop, refracts, reflects once off the far inner wall, and refracts again on the way out. If it enters at angle of incidence \(i\), the total angle it is turned through is
\[ D(i) = 180^\circ + 2i - 4r, \qquad \sin i = n \sin r, \]
where \(r\) is the refraction angle inside the drop and \(n \approx 1.333\) is water's refractive index. Rays strike the drop at every \(i\) from 0 to 90 degrees, so they leave across a whole spread of deviations. The rainbow appears where that spread bunches up.
"Bunches up" is precise: intensity piles up where \(D\) is stationary, where a small change in entry angle barely changes the exit angle, so many rays leave nearly parallel. Setting \(dD/di = 0\) and using Snell's law gives the entry angle of that minimum-deviation ray,
\[ \frac{dD}{di} = 2 - 4\frac{dr}{di} = 0 \;\Longrightarrow\; \cos^2 i = \frac{n^2 - 1}{3}, \]
which for water is \(i \approx 59.4^\circ\), giving a minimum deviation \(D_{\min} \approx 138^\circ\). You look back along the incoming sunlight, so the bright ring sits at
\[ 180^\circ - 138^\circ = 42^\circ \]
from the point directly opposite the sun. This is a caustic, the same mathematics as the bright cusp of light in the bottom of a coffee cup: a fold in the ray map where brightness formally diverges. Descartes worked it out in 1637 by tracing rays by hand.
Color is the same calculation run per wavelength. \(n\) is slightly larger for violet (\(1.343\)) than red (\(1.331\)), so the caustic lands at \(40.6^\circ\) for violet and \(42.4^\circ\) for red. Red rides the outer rim; the whole bow is under two degrees thick. A second, dimmer bow at \(51^\circ\) comes from two internal reflections, with the colors reversed and Alexander's dark band between them. Bowcast scores the primary; the rest is presentation.
The consequence the forecast actually uses
Because the bright ring is \(42^\circ\) from the antisolar point, and the antisolar point sits exactly opposite the sun at elevation \(-h_\odot\), the top of the bow is at elevation \(42^\circ - h_\odot\). Raise the sun and the bow sinks. When \(h_\odot > 42^\circ\), the entire arc is below the horizon and there is nothing to see.
So the first thing Bowcast computes for every hour is the sun's elevation and azimuth from the NOAA solar position algorithm (no API, just the observer's latitude, longitude, and time). If the sun is down, or above \(42^\circ\), the hour scores exactly zero before any weather is even considered. Geometry is a hard gate.
02 · The model
From geometry to a number
Geometry says the bow is possible. Weather decides whether the ingredients are actually in that patch of antisolar sky at that moment. There are three necessary conditions, and they are necessary in the logical sense: miss any one and there is no bow. Bowcast scores each as a number in \([0,1]\) and multiplies them, because multiplication is logical AND, and a product with any zero factor is zero.
\[ \text{score} = 100 \cdot \underbrace{f_{\text{sun}}}_{\text{direct light}} \cdot \underbrace{f_{\text{rain}}}_{\text{drops present}} \cdot \underbrace{f_{\text{elev}}}_{\text{bow above ground}} \cdot\, f_{\text{conv}} \cdot f_{\text{align}} \cdot f_{\text{wind}} \cdot f_{\text{temp}} \cdot f_{\text{conf}} \]
The score is a 0–100 measure of how good the conditions are, not yet a probability (that comes next). The first three factors carry the geometry and the two essential ingredients; convection and alignment nudge the result up by up to about \(15\%\); and confidence, wind, and temperature are downweights that pull toward zero when the forecast is unsure, the wind is high, or the rain is near freezing. Every factor is anchored in one of three papers, cited where it applies.
\(f_{\text{sun}}\): is the disc actually shining on the drops
From the forecast's direct-beam sunshine duration, the fraction of the hour with real sun, raised to a deliberately concave power:
\[ f_{\text{sun}} = \left(\tfrac{\text{sunshine seconds}}{3600}\right)^{0.4}. \]
The exponent matters. A bow needs one lit moment, not a lit hour: ten minutes of breaks (a fraction of \(0.17\)) still returns \(0.49\), not \(0.17\). When sunshine duration is missing, a layered-cloud fallback stands in, \(1 - 0.9\,c_{\text{low}} - 0.5\,c_{\text{mid}} - 0.15\,c_{\text{high}}\), because low cloud occults the solar disc while high cirrus lets the beam through almost untouched.
\(f_{\text{rain}}\): liquid drops in the antisolar sky, just after the rain
Liu et al. (2023), Brier-validated on three years of observed rainbows in ZhaoSu, found that about 94% of rainbows appear in the hour right after rain ends: a receding curtain of drops in the antisolar sky with the sun breaking through behind you. So the rain term is a temporal maximum that leads with the clearing hour:
\[ f_{\text{rain}} = \max\big(1.0\,q_{-1},\; 0.9\,q_{0},\; 0.6\,q_{+1}\big), \]
where \(q_{-1}\) is last hour clearing out, \(q_0\) is this hour, \(q_{+1}\) is next hour approaching. Each \(q = \text{dropQuality}(\text{mm}) \cdot \text{typeWeight}\). Drop quality is a piecewise curve: nothing below \(0.05\) mm/h, a broad ideal plateau from \(0.5\) to \(4\) mm/h (plenty of large drops, sky not yet blackened), and a decline into torrential rain that darkens the backdrop. Type weight encodes Businger (2021): convective showers score \(1.0\), stratiform rain \(0.55\), drizzle only \(0.25\). Drizzle drops are small enough to leave the geometric-optics regime, where Airy diffraction broadens and overlaps the color fringes into a washed-out white cloudbow.
\(f_{\text{elev}}\), and the small refinements
Lower sun means a taller arc standing further above the horizon, so \(f_{\text{elev}} = 0.6 + 0.4(1 - h_\odot/42)\). Then three modifiers: a CAPE bonus up to \(\times 1.15\) for buoyant, cellular showers with sunlit gaps between them (Businger's ideal); an alignment term \(1 + 0.12\cos(\phi_{\text{wind}} - \phi_\odot)\), because wind blowing from the sun's direction advects fresh showers into the antisolar sky where the bow forms; and a confidence prior from the forecast's precipitation probability. A strong-wind taper (down to a floor of 0.1 by Beaufort 8, ~75 km/h, where Liu saw no bows) and a cold taper (down to 0.6 as rain nears freezing) round it off.
Hard gates → 0
- Not daylight, or sun outside \((0^\circ, 42^\circ]\): the geometry of §01.
- Frozen precipitation. Ice crystals are not spheres; they make 22° halos, not bows.
- Total cloud cover > 96%. Carlson et al. (2022): none of 7,094 photographed rainbows occurred above that cover, the split their regression tree found. No sun disc, no bow, even if a few minutes of sun register.
- No rain signal at all: no liquid nearby and no precipitation probability to grade.
03 · Uncertainty
From a score to an honest probability
The score answers "how good are the conditions," but a forecast is a distribution, not a fact. The headline number Bowcast shows is a probability, and it is estimated by Monte Carlo over the forecast's own uncertainty.
The ICON ensemble provides about 40 members: perturbed runs, each a physically self-consistent possible atmosphere. Bowcast runs the exact scoring gates on every member and reports the fraction that produce sunlit rain that hour:
\[ \hat{p}_i = \frac{1}{N}\sum_{m=1}^{N} w_{m,i}, \qquad w_{m,i} = \mathbb{1}[\text{rain}_m]\cdot s_{m,i}. \]
Two choices here are statistical, not cosmetic.
Per member, not multiplied marginals. The quantity we want is \(E[\mathbb{1}_{\text{rain}}\,\mathbb{1}_{\text{sun}}]\) over the joint distribution. Each member carries its own internal rain–sun correlation, so averaging the product within members estimates it directly. Multiplying the ensemble-mean \(P(\text{rain})\cdot P(\text{sun})\) instead is biased whenever the two correlate, and they correlate strongly: the same clearing shower brings both the drops and the sunbreak. \(E[XY] \neq E[X]\,E[Y]\) unless they are independent, and here they are the opposite of independent.
Occurrence, not intensity. The member weight \(w\) is the sun term gated on rain, not a product of rain rate and sun. Whether a bow occurs scales with the direct sun on the drops; how bright it is scales with rain rate, and brightness is the score's job, not the probability's. An early version multiplied a rain-intensity factor in and collapsed toward zero, badly under-reading broken-cloud regimes.
That under-reading was a real bug, and fixing it (v3.3) is a clean example of the model meeting the world. The sun term now uses each member's direct normal irradiance (the actual solar beam in W/m²), ramped around the WMO sunshine threshold, \(s = \mathrm{clamp}\!\big((\text{DNI} - 80)/240,\,0,\,1\big)\). Cloud-cover fraction, the old proxy, cannot see an 800 W/m² beam punching through a 60%-cloud trade-wind sky, so it undercounted exactly the places rainbows love. Swapping cloud inference for DNI lifted Honolulu's odds without touching the desert, which we can show:
Why bands, not decimals. With \(N \approx 40\), the estimator's standard error is \(\sqrt{p(1-p)/N} \approx 8\) percentage points at \(p = 0.5\). Reporting "37.2%" would be false precision. Bowcast shows five bands (strong, good, fair, slim, unlikely), and the probability thresholds sit below the score thresholds, because member agreement rarely clears 50–60% even on a textbook convective evening.
04 · Does it work
Backtests against real weather
A model you cannot check is decoration. So the identical production scoring runs over ERA5 reanalysis (the recorded past state of the atmosphere): no fitting, no cherry-picking, just the same scoring applied over history.
The London double rainbow, 8 September 2022
The famous double rainbow over Buckingham Palace appeared the evening the Queen died. Run the model over that whole week in London and ask which evening it would have flagged. Its daily-peak scores:
Climatology it was never taught
Run a full year of Honolulu and bin every rainbow-capable hour. Nobody told the model when Hawaiian rainbows happen; it reconstructs the textbook signature from physics alone.
Across the same runs, Honolulu produces rainbow-capable conditions on far more days than Phoenix (200 versus 29 in 2024, about \(7\!:\!1\)), the same order as the published \(64\%\)-versus-\(11\%\) climatology. The absolute numbers are not tuned to match; the ordering falls out of the physics.
05 · Honesty
What it deliberately does not do
- DNI is a point value at the grid cell, not ray-traced toward the specific antisolar rain shaft. It answers "is the sun out here," not "is the sun lighting that exact curtain."
- No drop-size distribution. Numerical weather gives rain rate, not droplet radii, so shower-versus-drizzle is proxied from weather codes. Drop size is what separates a vivid bow from a washed-out cloudbow, and we only approximate it.
- Secondary bows, supernumeraries, fogbows, and reflection bows are ignored. They are presentation, not occurrence, and the product is a probability that a bow is visible.
- Every threshold is literature-anchored but uncalibrated against ground truth at scale. That is what the "I saw a rainbow" button quietly collects: each tap is a labeled example for tuning the constants that are currently set by three papers and judgement.
See it run
Everything above is live. The optics, the model, and the Monte Carlo run in your browser every time the map draws a gauge.
Bowcast is built by Shams. The model is grounded in Carlson et al. 2022, Liu et al. 2023, and Businger 2021.