A shaft of light through a slot meets a glass sphere and leaves as a waisted caustic horn

Light transport research · RadianceLab

Caustics without
a kernel

A volumetric caustic through glass, solved in closed form. No neural network, no clamp, no denoiser — and blitzing fast. The sheet is exact; the pair that renders the frame above is consistent, not unbiased, and the number is on this page.

Above: a shaft of light meets a glass sphere and leaves its underside as a waisted horn. That image is the pair: computed on the sheet, gathered by the beam it hands off to. In the equal-pass A/B further down, this caustic's waist is 7.7 px wide where the kernel estimator's is 70 at r = 0.70 and, with the beam at the pair's own radius, 16.1 px.

Per pass
7.05→1.17 ms
after the vsync fix; 0.76 ms of it is estimation · 217×140 film.
Cone → MIS trio
6291→1
worst-pixel ratio vs reference.
Hourglass solo → paired
0.976→1.0000
converged median against the beam at the same radius; by energy on the isolated caustic the pair reads 1.2 % above it.
Equal-pass error
2.1×
lower than the beam alone at 200 passes, on preset 51's fold.
Analytic primitives
21
kernel-free swept manifolds of 31 slots, plus six that carry a kernel (18, 19, 21, 28, 29, 30)

01 — The problem

A single grazing ray can ruin the whole frame

Volumetric caustics are the light that has already passed through glass before it scatters in fog. They are among the hardest paths in rendering: the light is focused to a thin, bright sheet, and almost no random path finds it.

The usual answer is photon mapping — fire photons, store them, and gather with a kernel of radius r. That works, but the radius is a lie you agree to: it blurs the caustic and biases the answer, and shrinking it trades the blur for noise. This page is about refusing the lie and keeping the speed.

An analytic estimator avoids the kernel entirely, but buys a new problem. Sweeping a photon into a surface and intersecting the camera ray with it introduces a Jacobian in the denominator — and when the camera ray grazes that surface tangentially, the Jacobian collapses toward zero and the estimator explodes.

medium sheet: ∠(x−p, n) = θ axis n tangency: J → 0 θ (fixed) φ t d a b light p eye x′ x a = ∂x/∂φ b = ∂x/∂t J = |(a×b)·d| = t·|cosθ ω·d − n·d| ω = (x−p)/t 09 · photon cone · sweep φ, t
SchematicThe photon cone: emission direction fixed in polar angle, swept in azimuth and distance. Where the camera ray runs tangent to the cone the co-area Jacobian vanishes — that arc is the firefly locus.

A photograph used to sit here, and it was pulled on 2026-09-08. It showed the photon-cone estimator alone on preset 41 at 400 passes and its caption called the bright pixels the tangency locus, at 1189.8× the scene mean. When every film on this site was regenerated from its own producer, that one did not come back: the estimator's own recorded config produces a frame with a twentieth of the shipped film's mean, and on this build the mixture that is supposed to fix it has the higher peak. The frames and the full retraction are kept in section 03 rather than deleted, but they are not evidence for this section's claim, so this section no longer shows them. That the cone is the family's worst primitive is measured elsewhere and stands: worst pixel against the reference, 6291 → 1 once it is mixed — the stat tile at the top of this page.

02 — The idea

Sweep the photon into a surface you can intersect

A photon arriving at a scattering event still has unused degrees of freedom. Rather than committing them randomly and storing the result, let them sweep.

Fix every dimension of a light path but two, and let those two vary. The scatter point traces out an analytic two-dimensional manifold in space — a sphere, a cone, a disk, a ruled plane. That manifold is a photon primitive, and a camera ray can be intersected with it exactly.

The kernel disappears. In its place is a co-area Jacobian relating the swept parameters to the measure the camera ray actually integrates:

J = | ( ∂x/∂a × ∂x/∂b ) · d |
One expression for every primitive in the family. The shape changes; the estimator does not.
medium (σ_s, σ_t) t · sampled connection r = |y − x| σ_s · phase · Tr(r) / r² light y x · scatter vertex in fog eye 01 · NEE long-ball · the oracle (no sweep)
medium light y t θ φ x x′ a b eye d sheet: |x − y| = t a = ∂x/∂θ b = ∂x/∂φ J = |(a×b)·d| = t²sinθ·|ω·d| ω = (x−y)/t, t fixed 07 · photon sphere · sweep θ, φ
SchematicsLeft: the reference — a light point connected to a scatter point, no sweep. Right: the photon sphere — sweep the emission direction at a fixed free-flight distance and the scatter point traces a sphere about the light; the camera ray meets it at isolated points, and the two tangents at that point are the Jacobian.

Because the manifold is exact, the estimate is unbiased with no radius parameter at all — the caustic is as sharp as the geometry, not as sharp as your kernel. What remains is the variance problem from section one, and it has an answer that costs nothing but bookkeeping.

03 — The family

One measure, twenty-one swept manifolds

Which two dimensions you sweep determines the shape. Twenty-one of them are built, and every one was divided against an independent reference before the next was started — the straight-line members against the next-event oracle to a median of 1.000, the dielectric members against the stochastic beam. The ratios are in the ledger; the one that lands off, the solo hourglass at 0.976, closes to 1.0000 paired. The roster below is the useful part of the space.

Twenty-one is the count that closes the picker: thirty-one slots, less the three baselines that are ordinary sampling, the six kernel estimators, and the one slot that is a rasterised gather rather than an estimator. Earlier versions of this page said nineteen, then twenty-two, and its siblings said twenty; none of those closed against the roster as it is filed today, and the discrepancy is what caused them all to be re-derived. The kernel class used to be two — the tracer beam and the tracer point. Section 04b is where three more arrived, and why they had to. The sixth is the hourglass pair, filed with them on 2026-09-08: analytic on its sheet, it carries the beam's radius everywhere else, and section 05 measures what that radius does to its energy.

#PrimitiveSweptCharacter
01NEE long-ballθ, φ, tThe oracle. Every primitive is validated against this
03UV light quadu, vNoisiest toward the light
05 / 06UVW cube / ballu, v, wNo grazing band at all — t² cancels 1/r². Cleanest of the family
07Photon sphereθ, φWorks with point lights; casts real shadow rays
08Photon diskcos θ, tSignature artifact: a radial firefly fan
09Photon coneφ, tWorst single primitive. Also the ior→1 limit of the hourglass
10 / 11UT / VT planeu,t / v,tGrazing bands perpendicular to one another
12MIS · three planesshared (s,u,v)259 → 1. Each member's singular band is where the others are healthy
13MIS · direction trioconditional6291 → 1. Sphere + disk + cone under one measure
26MIS · planes + trioshared (s,u,v), six densitiesBoth regimes in one estimator. Lowest per-pass variance at every emitter size; its masks reproduce 12 and 13 bit for bit
15Continuous-φ MISclosed-form envelope∫|a cos φ + b sin φ| dφ = 2√(a²+b²). Grazing band gone
18Tracer photon beamt — 1D kernelFirst estimator to show a glass caustic. Ground truth for 20. Also the t column of the lattice
28Photon ringφ — 1D kernelA circle of radius t sin θ about the axis. Singular where the camera ray is coplanar with it
29Photon arcθ — 1D kernelA half-circle of radius t about the vertex. Singular along the frame axis, and nowhere the ring is
30MIS · ring + arc + beamarclength on the camera rayWorst pixel 545.7 → 5.0, lower than the beam's own. Consistent, not unbiased: like every kernel member it blurs, and on top of that its weights do not sum to one at finite radius — so complement mode refuses it as a primary by name
20Photon hourglassφ, r about the axisThe novel one. Kernel-free refractive caustic; solo it reads 0.976, and its guard is a discard, not a clamp.
21Hourglass MISbranch measureConverged median 1.0000 against the beam at the same radius. The one to use — and consistent, not unbiased: it carries the beam's kernel at weight O(r) on the caustic and at one everywhere else, and its energy moves with that radius, +0.9 % at r = 0.15
27Photon ring-altφ, t about the mirror directionThe first glossy vertex. Five free dimensions instead of three — a rough surface imposes no reflection law, so the light point and the polar angle are sampled rather than solved
u v a b y x cam emissive quad sheet = quad + t·ω t·ω (sampled) camera ray medium 03 · UV quad · sweep u, v
medium axis t cos θ meridian half-plane (φ fixed) d b a x light y eye radial firefly fan φ̂·d → 0 : camera ray lies in the meridian half-plane point light · axis = world up a = ∂x/∂cosθ b = ∂x/∂t J = |(a×b)·d| = (t/sinθ)·|φ̂·d| φ̂ = half-plane normal 08 · photon disk · sweep cos θ, t
10 · UT plane · sweep u,t u t v=c ω fog emissive quad eye a b grazing band: d in span{u, ω} J = |(u × ω)·d| → 0 top view 11 · VT plane · sweep v,t v t u=c ω fog emissive quad eye a b grazing band: d in span{v, ω} J = |(v × ω)·d| → 0 top view 10 / 11 · UT and VT planes · sweep u,t and v,t — grazing bands at 90°
fog emissive quad y n x camera d a b ω u t φ φ = 0 · UT φ = π/4 φ = π/2 · VT w(φ) = π|(ê(φ)×ω)·d| / 2√(a²+b²) a = (û×ω)·d b = (ŵ×ω)·d |(ê(φ)×ω)·d| = |a cosφ + b sinφ| √(a²+b²) ∫₀^π = 2√(a²+b²) grazing 0 π/4 π/2 π φ 15 · continuous φ MIS · closed-form envelope removes the grazing band
medium eye R θ φ splat x x′ a b film ω_L light y J = |ω_cam·ω_L| = dual of 07's |ω·d| R = cam free flight · 1/R² stays (in We) 2 roots → 2 pixels, each splatted 17 · camera sphere · the dual: splat to film · swept: θ, φ about the eye
light y axis medium (fog) a = ∂x/∂t = ω b = ⊥ offset, |b| ≤ r K = 1/(2r·|ω×d|) glass ball (ior 1.5) x1 x2 x eye a b θ θ, φ traced t 2r 18 · tracer beam · traced chain + 1-D kernel (the reference for 20)
SchematicsSix members, one vocabulary: yellow is the photon side, blue the camera side, a red dot marks a swept or dielectric vertex, and the white ring is where the camera ray meets the sheet. The disk's radial fan and the two perpendicular plane bands are the singular loci that the mixtures below are built to cancel.
Screenshot — H2 · preset 35 · Primitive Explorer on · step strategies 3 → 7 → 8 → 9 → 10 → 11 → 14 → 16 → 15 → 12 → 13 · Jacobian resolution 80 · shoot the whole window
Primitive ExplorerThe renderer's own view of the same objects: each swept manifold ray-traced in world space and jet-coloured by its grazing Jacobian (blue face-on, red grazing), with the theory readout alongside. Nothing is hardcoded per primitive — the shape emerges from the two swept dimensions.

The design criterion that mattered: Jacobian flatness first, evaluation cost second, ease of connection third. A primitive that is cheap but singular everywhere is worth less than one that is expensive and well-conditioned — because in a mixture, the singular member is the one that poisons the estimate.

04 — Why a diverse set wins

The singular estimator down-weights itself

Each primitive has a grazing locus. The observation that makes the family usable is that they are in different places.

Where one primitive's Jacobian collapses, another's is well-conditioned — and the balance heuristic weights each sample by how well every strategy could have generated it. The member that is about to produce a firefly is precisely the member that assigns itself a low weight. The singularity does not need to be clamped, guarded, or clipped away. It cancels.

emissive quad fog eye ω 03 {u,v} 10 {u,t} 11 {v,t} a b a b a b balance w_i at x w03 w10 w11 03 grazed at x 10+11 carry it 12 · MIS of 03+10+11 · each band is where the others are healthy · 259 → 1
shared p: pdfSel·pdfArea cancels → conditional-given-p measure w_i = p_i(s|p) / Σ_j p_j(s|p) · each p_i → 0 on its own tangency locus medium axis θ 9 cone · φ,t 7 sphere · θ,φ 8 disk · cosθ,t camera p ab ab ab x₇ x₉ x₈ 13 · sphere + disk + cone under one measure · 6291 → 1
SchematicsLeft: the three plane sheets share one quad, and their grazing bands are perpendicular, so the balance weights dim whichever member is about to fail. Right: sphere, disk and cone share one light origin, which is what lets the common selection and area densities cancel before any weight is computed.
A · single planeA single swept-plane estimator showing its grazing band
B · three-plane MISThree swept planes combined under MIS, with the grazing band removed
Mean radiance — A0.12640
Mean radiance — B0.12642
Agreement4 decimal places
Peak pixel — A13.7952
Peak pixel — B0.6637
Variance reduction20.8× lower peak

Both images are unbiased estimates of the same integral, at the same sample count, and their means agree to four decimals — they are computing the same answer. One of them is usable. That gap is the entire contribution of the mixture, and it costs nothing but bookkeeping: the weights are closed-form.

A · cone alonePhoton-cone estimator alone, showing bright firefly outliers at the tangency locus
B · direction trioThe same scene under the three-primitive direction MIS, with the outliers gone
Withdrawn 2026-09-08 — this pair no longer shows a contrastPreset 41, 400 passes, strategy 9 against strategy 13. The panes are what the two estimators render on the current build, and they are indistinguishable.
Frame mean — cone alone / trio0.03829 / 0.03840
Peak ÷ frame mean — cone alone858×
Peak ÷ frame mean — trio1138×
Reduction shown by this pairnone

What this readout used to say, and why it does not say it any more. It read 1189.81 against 13.38, an 88.9× reduction. When renders.py was written and every film on this site was regenerated from its own producer, ten of the eleven came back byte-identical or agreed in energy to 3e‑4 — and this one did not. The shipped fireflies film carried a frame mean of 0.21171 with 63 % of its energy in ten pixels; the film that the recorded config actually produces has a mean of 0.03829 and 1.2 % in its top ten, and at ten times the passes it still does. On this build the two estimators are indistinguishable on this preset, and the trio's peak is higher than the cone's. The numbers above are the two films measured (py docs/tools/filmstats.py stats, peak divided by frame mean); no film has been swapped, and the pair is left standing rather than deleted so that the retraction is where the claim was.

The claim itself is not withdrawn — its illustration is. The direction trio's variance reduction is measured on the roster, not on this frame: worst pixel against the reference, 6291 → 1, the number in the stat tile at the top of this page; and the plane pair two panes up, which regenerates byte-identical, shows the same mechanism at 20.8× on a scene where the grazing band is genuine. What preset 41 needs to illustrate it again is a scene whose tangency tail is real at 400 passes — a scene choice, not a new number, and it has not been made.

The three direction-swept primitives share a conditional-given-p measure, which lets the common selection and area factors cancel before any weight is computed. Without that cancellation the mixture would not be tractable at all.

Which mixture, at which light size — and the two predictions the rig overturned

The two mixtures are built for different regimes. The planes sample a point on the light, so they should improve as the lamp grows; the direction trio conditions on one light point, so it should lose ground as that point wanders. The variance study wrote the crossover down before anything was rendered: the planes overtake the trio once the emitter subtends about 35° at the ball. A second prediction sat beside it for the caustic: the vertex-seeded hourglass (22) overtakes the light-seeded one (20) at 35–41°.

Presets 58–62 are one rig built to settle both: a glass ball in fog under a downward quad whose edge steps 0.2, 0.5, 1.25, 3.2 and 8.0 at constant power — 2° to 77° at the ball — with every cell timed, eight independent pass-batches per cell, and the energy checked against the camera walk at every row. Neither crossover arrived. The trio beats the planes at every size: by 43× on equal time under the smallest lamp, narrowing to about 1.3× at 77°, where the cost slopes carry a 10–30 % timing uncertainty and the equal-time margin is inside it — the per-pass ratio of 2.2× at that row is not. The light-seeded hourglass beats the vertex one at every size, by 236× down to 1.9×. Both trends run the predicted way, so the crossovers exist — beyond the rig, near an emitter edge of 9–10 for the airlight pair and further still for the caustic, where the lamp would be nearly half the width of the fog box. The miss was on the side the study trusted most: the trio's variance floor is 1.10 per pass, not 3.8, and it rises 1.35× over a 1,600× increase in emitter area, not 2×. Both corrections are written into the study beside the predictions they replace.

variance/lightsize/fig_crossover.png
Equal-time variance against emitter sizeLeft, the airlight: three planes (12), the direction trio (13) and the cross-family mixture (26). Right, the caustic: the light-seeded hourglass (20) against the vertex hourglass (22), with the beam-paired 21 above them both at full transport and isolated to the caustic. The dashed line is where each crossover was predicted. Presets 58–62, 217×140 film, 640 and 2,048 equal passes per row; relative variance per pass times milliseconds per pass, so lower is better.
Emitter edgeAngle at the ball12 · planes13 · trio26 · both22 / 20, caustic
0.22.3°114.72.73.5236
0.55.7°41.53.03.5111
1.2514°43.42.23.723
3.235° — the predicted crossover17.32.93.43.8
8.077°4.73.63.01.9

Equal-time cost of one unit of noise for the three airlight estimators; the last column is the vertex hourglass's cost divided by the light-seeded one's. The cost slopes carry 10–30 % error bars (±0.35 on the trio and ±0.2 on the mixture at the last row), so the bottom row's equal-time verdicts are marginal and the per-pass ones are not. The three airlight estimators agree on energy to 0.1 % at every row, and all three read 2–3 % above the camera walk — every bit of that excess sits inside the glass ball's disc, where a swept estimator integrates fog along the unrefracted camera ray and the walk stops at the glass. Outside the disc they agree with the walk to 0.03 %. That is the one camera-leg convention the whole light-sweep family shares, and the camera-leg cap that complement mode switches on closes it.

The third column answers the question the two mixtures invite: why not both? Strategy 26 mixes the three planes with the direction trio under one measure. The obstacle looked like a change of variables — the trio's densities are conditional on a light point, the planes' live on the light's area — and turned out to be bookkeeping: multiplying the trio's densities by the light-point selection density puts all six on the same light-area measure, the area factor cancels exactly, and the six weights are closed-form with no root-find. It has the lowest per-pass variance of the three at every emitter size and, from the third row on, the cleanest tail; it costs 1.2–1.8× the trio per pass because it draws six members rather than three and pays a shadow ray for every crossing that lands, and under the widest lamp it edges ahead on equal time, inside the timing uncertainty. It captures both regimes per sample. Masking its members down to the planes alone or the trio alone reproduces 12 and 13 bit for bit; the check that the shared measure is right is the energy, which agrees with 12 and with 13 to 0.1 % across a 1,600× range of emitter area. Use it when the light size is unknown.

One bug came out of the rig, and it is on the page because the rig is what found it: inside the trio, the sphere and disk members cast no shadow ray while the cone did, so on preset 48 the trio read 1.25× the walk under a glass ball's shadow. Fixed the same day; the trio now reads 1.016, and the mixture inherits the fix.

And the rig's own oracle is the estimator to beat. The reference at every row was the camera walk's single-scatter next-event estimate: a free-flight-sampled scatter point and one shadow ray. Its variance was recorded and never tabulated, and it is lower than any swept member's at every emitter size — 0.35 to 0.67 per pass against the trio's 1.10 to 1.49, and 1.4 to 3.6× better on equal time. That is the honest boundary of this family. Sweeping a dimension of an area light's single scatter does not beat connecting to it; what sweeping buys is the mixture mechanism above, and the connections next-event estimation cannot make at all. Hold on to that sentence — the next section reproduces it exactly, on a different construction, and the section after that is where sweeping stops being optional.

Screenshot — H8 · presets 58 → 62 in order · Split A/B with strategy 13 on the left and 12 on the right · MinBounce 0 · 640 passes each · one frame per preset, framing the fog under the ball as the lamp grows

04b — The lattice's other half

Sweep one coordinate instead of two

A photon leaves a vertex with a direction and a distance: three numbers, (θ, φ, t), in a frame at that vertex. Fix one and sweep two and you get a surface — the cone, the disk, the sphere, kernel-free and shipped since the first week. Fix two and sweep one and you get a curve. There are exactly three of those, and until this week only one of them was built.

The construction is the user's. The ring and the arc, the shared measure that mixes them with the beam, and the observation that closes the lattice are all theirs; what was added here is the transcription into the renderer and the gates below.

three coordinates at a photon vertex: (θ, φ, t) in a frame (a, U, V) FIX ONE, SWEEP TWO a sheet. the camera ray crosses it at a point. no kernel. θ 08 disk · sweeps θ,t 09 cone · sweeps φ,t 07 sphere · sweeps θ,φ holds φ holds θ holds t each column is a complementary pair — the sheet holds what the curve sweeps FIX TWO, SWEEP ONE a curve. the camera ray misses it. one 1-D kernel. t r r r 28 ring · sweeps φ 29 arc · sweeps θ 18 beam · sweeps t holds θ,t holds φ,t holds θ,φ one expression: integrand × (1/2r) ÷ |(∂x/∂u) × d| — the beam's 1/sin θ is that, at unit speed
The latticeTop row, the two-dimensional members: a sheet, and the camera ray crosses it at a point, so nothing is blurred. Bottom row, the one-dimensional group: a curve, which a camera ray in three dimensions misses, so each carries a one-dimensional kernel of half-width r and the white ring marks closest approach rather than a crossing. Read the figure by column: each pair is complementary — the sheet above holds exactly the coordinate the curve below sweeps.

One expression, not three constructions

Every member of the bottom row is the same integrand times the same kernel 1/(2r), divided by one geometric term: |(∂x/∂u) × d|, where u is whichever coordinate is being swept and d is the camera direction. The tracer beam's familiar 1/sin θ is that expression for a unit-speed sweep. The beam was written first and looked like a special case; it is the t column of a lattice that now closes. The ring (strategy 28) sweeps φ, so its locus is a circle of radius t sin θ about the frame axis; the arc (29) sweeps θ, so its locus is a half-circle of radius t about the vertex itself. Write the volume element as t² dt dμ dφ and the point source's 1/t² cancels it, which is why the ring carries one factor of 1/t that the beam does not, and the arc carries that plus an explicit sine.

Ray versus circle is a quartic — and the primitive is a torus

Intersecting a camera ray with a circle of tube radius r is ray-versus-torus, which is what these primitives actually are, and the stationary-point condition squares to a quartic. Two things make the shipped solver simpler than the textbook one. The bracket is free and exact — every stationary point lies in [−k−ρ, −k+ρ] — and inside that bracket the quartic's own derivative is a cubic in closed form, whose real roots cut the interval into at most four monotone pieces, each sign change bisected. No companion matrix, no eigenvalues, and no threshold to guess.

The part that is easy to get wrong is that you must sum every stationary point, minima and saddles alike. Two or three roots occur on 57.01 % of hits when the camera ray runs near coplanar with the ring's circle — 51.15 % two-root and 5.86 % three-root over 165,429 measured hits — and those are precisely the configurations the primitive exists for. Keeping only the nearest root would have deleted the geometry the estimator was built to reach, and would have looked like a mild darkening rather than a bug.

The consequence that matters: the swept density stops cancelling

In the two-dimensional members the sampling density of the swept coordinate cancels against the emission profile, so the profile is divided out and never evaluated. Sweep one coordinate and it does not cancel, and that single fact sets the whole character of the group. The emission profile has to be evaluated at the crossing — which is why any of these is identically zero at a smooth dielectric (a two-dimensional delta integrated over one dimension is still a delta), and equally why an evaluated payload is exactly what would let a one-dimensional member carry a bounce off a rough surface. A delta in one angle is fine, and the family already ships it: the cone is the polar-delta ring.

A corollary that was run as written on 2026-09-08, and half of it fell. If the emission profile is axisymmetric about the frame axis, the ring's swept coordinate is the symmetry coordinate, and a μ-density shaped like the profile absorbs it entirely — f_e / q is a constant and the profile is never evaluated. That is why the frame axis is the light normal: a Lambertian emitter is axisymmetric about it by construction. Until 2026-09-08 this note cited a cos⁴ lobe reading 1.0087 ± 0.0184 as the verification; that run sampled μ uniformly and evaluated the profile at the crossing — the opposite experiment. Run with the absorbing sampler (run_lobe_absorb.py, μ ~ 5μ⁴, same seed and N) the ring reads 0.987 ± 0.017, 1.018 ± 0.008 and 0.983 ± 0.011 on the broadside, steep and along-axis cameras — unbiased, like the evaluated one — with 1.2×, 2.6× and 2.5× less variance. On the near-tangent camera it has 20× more: the absorbing density rarely visits the μ ≈ 0 rings the profile is dark on and pays them full weight, which is exactly where the ring's tangency family lives. The corollary is right about the profile and wrong about "pays nothing": where the singular family sits in the profile's dark, evaluating is the cheaper deal, and the shipped ring evaluates.

Three tangency families, and none of them overlap

The ring goes singular where the camera ray is coplanar with its circle. The arc goes singular where the ray runs along the frame axis. The beam goes singular nowhere — its parallelism needs two fixed directions to line up by coincidence, while a swept circle hunts the tangency. That disjointness is the argument for mixing them, and it is visible one derivation earlier than any film: at 800,000 samples per cell against a quadrature reference the ring alone reads 2.1323 ± 0.0516 on a near-coplanar camera and unity everywhere else — an excess of 1.13 against a bar of 0.05, 22 sigma, resolved. Only half of that pair is a measurement, and the half that is not is marked. The arc's along-axis cell reads 1.0286 ± 0.0269, an excess of 0.0286 against a bar of 0.0269 — 1.1 sigma, not resolved — and it was bolded here as a measured family on the strength of its centre alone. The arc's tangency family is indicated by this table and demonstrated by its worst-pixel column below, 41.7 times its own frame mean against the beam's 5.9, and by the swarm picture. Nothing downstream leans on the cell: the mixture's case rests on the ring's 545.7 and on the arc's tail, both far outside their noise.

Strategy 30 puts all three on one measure — arclength along the camera ray — and applies the balance heuristic from the section above. Two properties fall out. With one shared kernel radius the 2r cancels identically in the weights, so the shared-width instinct buys the densities for free. And the contribution goes as 1/sin ψ while the weight goes as sin ψ, so the product is bounded and at most one member can be grazing-singular at a time. No member has to hand its tangent in: the three tangents are just the spherical frame rebuilt at the crossing.

What the mixture buys, in one column

Preset 60, 217×140, kernel radius 0.15, converged at 3,200 passes. The oracle is the camera walk's own single-scatter next-event estimate — strategy 2 with min bounce 0 and max bounce 1, the panel's bounce-isolation fields set so that exactly one scatter is accumulated and nothing else can enter the comparison. Its support is the whole frame, 30,380 pixels of 30,380, so every ratio here is a whole-frame ratio.

cellratio to the oraclemedian relvar / passp90 relvar / passms / passrelvar × msworst pixel
18 · beam1.0208 ± 0.0009108.0210.71.073 ± 0.062116 ± 75.9
28 · ring1.1845 ± 0.0234106.4278.14.159 ± 0.182443 ± 19545.7
29 · arc1.0360 ± 0.0039318.3812.83.531 ± 0.3201124 ± 10241.7
30 · mixture1.0207 ± 0.000539.079.77.447 ± 0.357290 ± 145.0

Eight disjoint pass-batches of 400, differenced out of the cumulative films; ms/pass is a least-squares slope over a 640 / 1,920 / 3,200 / 4,480 ladder with the film writes discarded, run three times per cell, interleaved, with the run-to-run spread of the three slopes as its bar (4.4–9.1 %). Worst pixel is max over frame mean on the converged film. The last column propagates the cost bar only — a median relvar carries no standard error here — so those bars are floors on the uncertainty rather than the whole of it. Note that SE(ratio) and the relvar columns are different statistics and move in opposite directions as the kernel thins: the frame mean is carried by a heavy tail that shrinks with r. Producers: ra_gate.py var for the ratio, ra_cost.py measure for the cost, ra_equaltime.py for their product and ra_worst.py for the last column — each stores its json beside the others in docs/ring-arc/harness/ra_gate/.

The last column is the whole thesis in one number. The mixture's worst pixel is 109× better than the ring's, 8.3× better than the arc's — and lower than the plain beam's own. The balance weight down-weights each member exactly where that member is about to go singular, and no member is singular where another one is. Per pass it is 2.7× better than the best single member at r = 0.15 and 2.5× at r = 0.05 (39.0 against 106.4; 118.2 against 300.2) — at three draws per pass against that member's one: the ring draws its own (μ, t), the arc its own (φ, t), and the beam sweeps the frame's direction, so a pass of the mixture is three light paths. Per draw, 39.0 × 3 = 117 sits beside the beam's 108.0 — parity — and each draw costs 2.3× the beam's, which is why the equal-time column below, not this one, is the verdict. It is also the only cell whose ratio matches the beam's converged value to four digits with the smallest standard error in the column.

Consistent, not unbiased — and the number that says how much

The caveat travels with the estimator. At a finite radius the three members deposit on manifolds a radius apart, so the balance weights do not sum to one pointwise. Strategy 30 is CONSISTENT, NOT UNBIASED. It converges as r → 0 and nowhere else, and it must never be called unbiased. That is also why complement mode refuses it as a primary by name: comp mode's claim is that the two layers together are the whole transport, and a primary that owns a box and then mis-reports its total breaks that claim at every radius the mode is actually run at. Its two members, 28 and 29, are each a single blur-biased estimator of the same box exactly as the beam is, and are not refused.

Since 2026-09-09 the caveat has a mechanism. The kernel half-width is one control for the whole biased family — the Blur width group, shown for every strategy whose class-table kernel bit is set (18, 19, 21, 28, 29, 30) and for the surface map, hidden for the kernel-free rest — and it carries an opt-in progressive shrink, pp.blurShrink = β, under which pass i reads pp.beamRadius·i−β. Within a pass every member of 30 still shares one r, so the 2r cancellation above is untouched; the running mean over passes is then the progressive estimator, and “consistent as r → 0” is something the renderer does rather than something the spec says. Measured on this preset at r0 = 0.30, β = 1/3: the trio’s excess over the beam plateau falls from +0.0036 ± 0.0003 at a fixed radius to inside the protocol’s bar by 6,400 passes, and the ring’s from 0.34 to 0.0125 ± 0.0046, with the derivation’s β* = 1/(2p+d) = 1/3 for the tube consistent with the observed slopes. Default β = 0: every existing film is byte-identical. The numbers and their producer are in docs/ring-arc/SPEC.md §3.1 and docs/tools/blur_shrink.py.

The honest way to read a biased-by-design estimator is as a ladder, so here is the ladder — five kernel radii, 3,200 passes each, every rung carrying its own error bar from its own eight batches rather than one bar transplanted from a neighbour:

kernel radius18 beam28 ring29 arc30 mixture
0.3001.0200 ± 0.00051.3506 ± 0.01831.0564 ± 0.00291.0233 ± 0.0004
0.1501.0208 ± 0.00091.1845 ± 0.02341.0360 ± 0.00391.0207 ± 0.0005
0.0751.0200 ± 0.00171.0902 ± 0.01551.0245 ± 0.00211.0197 ± 0.0008
0.03751.0179 ± 0.00321.0556 ± 0.01011.0238 ± 0.00311.0199 ± 0.0015
0.0191.0209 ± 0.00331.0292 ± 0.00261.0214 ± 0.00401.0195 ± 0.0012

At r = 0.30, where the ring alone is 33 % high at eighteen sigma, the mixture is 0.33 % high — a hundredfold reduction, both terms measured, neither inferred. And the 0.33 % is itself resolved at 7.5 sigma, which is a change of verdict in the direction that costs us: with a per-rung bar the mixture is measurably biased at the widest kernel. That 7.5 replaces a shipped 5.1, and the correction runs the way that hurts. The 5.1 assumed the mixture and the beam were independent; batch k of every cell is the same block of pass indices, so they are not, and the measured batch correlation there is ρ = +0.57 — the sign the construction predicts, since 30 contains the beam as an MIS member. A positive correlation means quadrature over-states the differential bar, so the shipped label was conservative and the honest one makes the bias more resolved, not less. That is the caveat above showing up as a number instead of a warning, and it is the right number to publish beside the payoff, because the payoff is unharmed. Below r = 0.15 the mixture is indistinguishable from the beam at every rung.

The ring's excess, fitted rather than eyeballed over the four resolved rungs — 0.30, 0.15, 0.075, 0.0375, where the whole-frame excess clears twice its standard error — goes as r1.067 (+0.105 / −0.096, χ² 0.20 on 2 dof) — first order. How far second order lies depends on the yardstick, and the yardstick is now named. The headline is the profile-likelihood statistic — √(30.67 − 0.20) = 5.5 sigma, delta-chi-squared — chosen because it evaluates the fit at p = 2 instead of extrapolating to it. The linear reading, (2 − 1.0670)/0.1055 = 8.8 sigma — from the unrounded fit, which is the convention; dividing the rounded p and sigma gives 8.9, and that difference is arithmetic — is quoted for continuity and is no longer the one to cite: it runs a 68 % Monte-Carlo interval nine of its own widths into a tail it was never fitted on. Neither is a limit of the data — 0 of 20,000 refits reach p ≥ 2.

Why first order rather than second is not settled, and the mechanism this page used to give for it has been withdrawn. The obvious explanation is that a whole-frame average is dominated by the pixels where the ray is near coplanar with the ring's circle, where in general position the numpy model measures 2.4. That explanation needs the near-coplanar band to be the low-exponent region — low is what drags a frame average down to first order — and fitting the band and the rest of the frame separately puts the band's exponent at or above the rest's under both fitting conventions: 1.314 (+0.172 / −0.158) against 1.225 (+0.206 / −0.204) over the rungs resolved within each subset (three of them — 0.30, 0.15, 0.075 — the same rule as the whole-frame fit but on wider bars, so one rung fewer), and 1.571 (+0.121 / −0.105) against 1.322 (+0.155 / −0.144) forced over all five. The mechanism failed its own test, so it is withdrawn rather than softened. What survives is the concentration, and it is measured: the 5 % of pixels with the largest excess carry 64.5 / 63.5 / 60.8 / 58.2 % of the frame's whole excess at the top four rungs, selected on four batches and scored on the other four so that the selection is not the noise being scored. The honest reason the split cannot decide it is that the mask is frozen at the r = 0.30 geometry while the real band narrows with r, so by 0.019 it holds 4.3 % of the excess instead of 60 %. A band that tracks r would be the experiment; it has not been run.

Three labels moved when the bars were measured instead of carried, and all three are recorded: the “unresolved” mark on r = 0.0375 is retracted (1.5 sigma against a transplanted bar, 3.6 against its own); the arc's suspicious non-monotone rung turns out to be 0.20 sigma — it was the bar all along; and the ring's bottom rung, once “exactly on the 2 sigma line”, reads 1.7 sigma once the batch correlation is measured rather than assumed away (ρ is negative for the ring, −0.44 at the top of the ladder, so quadrature was anti-conservative there). Unresolved rather than borderline. The paired ladder reads 17.8 / 6.9 / 4.5 / 3.5 / 1.7 for the ring and 13.8 / 3.7 / 1.6 / 1.4 / 0.1 for the arc, every other verdict unchanged, and the exponent does not move at all — p = 1.0670 on the independence-assuming bars and 1.0673 on the paired ones, 1.067 either way. (Until 2026-09-08 this sentence ended “and the exponent moves by 0.001”. That 0.001 was a grid artefact and not a measurement: ra_fit.py profiled p on a 0.0025-wide grid and reported its node 1.0675 as 1.068, while ra_p2.py used a grid ten times finer. Both scripts now share the fine grid, and harness/ra_gate/p2.json stores the whole derivation.) The caveat travels with it: eight batches give ρ a 68 % width of about ±0.45 and the paired standard error itself is good to about ±27 %, so neither 1.7 nor the 2.0 it replaces should be read to two digits — the point is that the rung is not resolved and that the shipped bar was the wrong estimator, not the wrong digit.

The whole column sits about 2 % above the oracle, and that offset is shared, not a property of the new members: the beam's row is flat in the radius at χ² 1.17 on 4 dof. It was tested on four fronts that could each have killed it. The sharp one is strategy 12, three-plane MIS, which has no kernel at all, finds its emitter by a different route, and renders byte-identically with the photon-lamp convention flag on and off — it cannot even read the flag the sweeps use, and it reads the same 1.0198 ± 0.0007. Change the lamp and both move together, to 1.0336 and 1.0338 on preset 62. The statistic moves 1.4 percentage points across the light-size series, fourteen times its own bar, so it has discriminating power. The offset is a convention residue, it is preset-dependent, and it is not claimed to be zero.

It loses to the plain beam on easy transport, and per draw it only matches it

Equal time, same rig: 290 ± 14 for the mixture against 116 ± 7 for the beam alone — a loss of 2.51 ± 0.19×. The mixture beats the two sweeps it contains — 1.52 ± 0.10× the ring, 3.87 ± 0.40× the arc, and a factor of ten on the arc's p90 tail — and it loses to the member that was already there. All three orderings survive their bars, which is why they are quoted with them. That is stated in the ledger and not buried. It is consistent with the family's thesis — these primitives are heavier than plain sampling, and they win on hard paths, not easy ones — but consistent is not confirmed: this group's hard case, a sharp emitter or an occluded lamp, has not been run, and until it is, the 2.51× loss is the whole of the measured verdict.

Single scatter in clear fog with nothing in the way is the easiest transport this renderer has. The beam is the one member of the group that is singular nowhere, it costs 1.073 ± 0.062 ms per pass against the sweeps' 3.5–7.4, and on a rig with no obstruction it is very hard to beat — the same way the camera walk's next-event estimate beats every swept member of the airlight family one section up. What the mixture buys is not speed on that rig. It is that it never has a worst pixel, which is the property that matters the moment the geometry stops being easy: the ring's worst pixel is 545× its own frame mean and the arc's is 42×, while the mixture's is 5.0 — under the beam's own 5.9. A pixel like the ring's is not fixed by more passes. The ring and the arc sit inside the camera walk's own cost class — the walk costs 3.410 ± 0.567 ms per pass here, and they are 1.22× and 1.04× that — while the mixture is 2.2× the walk; the beam is 3.3× cheaper than the cheapest sweep, and the mixture is the only one you can point anywhere. How well is that column known? To about 10 % within a session and about 1.6× between them. It was re-measured on 2026-09-08 as three interleaved ladders on one binary, and the bars above are the spread of those three. The other number that has been quoted for it — a slope over the same eight batches' stored wall clocks, 0.73 / 6.90 / 3.38 / 15.13 — is not a competing measurement and not merely “load-contaminated” in the abstract: three of its four fits want a negative process start (−4.5 s, −5.1 s) where a real one is +0.3 to +0.9 s, two of its ladders are not monotone in the pass count, and at a matched 3,200 passes its renders ran anywhere from 0.61× to 2.05× the speed of tonight's — the machine moved inside the single sequential run that produced it. It does not swap the ring and the arc: that claim was wrong and is withdrawn, and all three measurements order the four members identically. Every ladder point now lives in ra_gate/cost.json.

What it looks like

The frozen batch renders the whole one-dimensional group as geometry rather than as a film: twelve primitives per member, the sample pool frozen, 400 passes. Strategy 28 draws a swarm of nested circles seen edge-on — twelve rings coaxial about the lamp normal, stacking into horizontal arcs of increasing radius, brightest where the sweep is slowest. Strategy 29 draws twelve open hooks, half-circles about the vertex leaning at twelve different azimuths instead of stacking. Strategy 30 draws both of those plus straight streaks: rings, hooks and beams in one frame, and the trio is legible as a trio. No code was needed for any of it — the correlated-batch reconstruct path re-seeds after the camera prefix and calls the ordinary per-pixel sampler, which is strategy-agnostic by construction.

The converged frames make the same argument a second and more useful way. At 3,200 passes the ring's film carries a bright horizontal band of fireflies across the middle of the frame — the locus where the camera ray is coplanar with its circle, exactly where the derivation says it must be. The arc's film carries a bright patch low and centre, on its own axis locus. The mixture's film has neither. The band and the patch are gone and what is left is the smooth fog gradient the beam and the oracle render. That is the disjoint-tangency argument as a photograph instead of a table.

Screenshot — H9 · preset 60 · correlated 2 · freezePool 1 · primCount 12 · 400 passes · step strategy 28 → 29 → 30 · same frozen pool each time · shoot the viewport only
Three frozen batchesThe three members drawn from one identical sample pool, so the only thing that changes between frames is which curve the photon is swept into. This is the picture that makes the mixture's argument visible: rings, hooks and streaks in one frame, each dim exactly where another is bright.

What the gates checked, and what was deliberately left out

The derivation gate was re-run rather than inherited, at 800,000 samples per cell against a composite Gauss-Legendre reference the estimators share no code with, and then again against an adversarial independent estimator that shares none of the algebra either — no quartic, no root find, no Jacobian, no sin ψ, just a tube indicator integrated by deterministic quadrature. The two rows that gate quotes — A_broadside against the isotropic lobe and against the tilted one — land within 1.2 sigma of unity on all three members. Storing the adversary's whole twelve-row table on 2026-09-08 showed what quoting two rows had hidden: ten of the twelve sit within about 5 % of unity for all three members and two do not. B_steep/lobeT reads 11.65 / 9.99 / 10.22 and C_neartang/lobeA reads 1.58 / 1.49 / 1.72. The disagreement is common to all three members — they agree with each other to about 15 % — which is not the shape of a member's Jacobian being wrong, and it is the only thing this estimator was built to test; both cells are anomalous against the quadrature reference too, and B_steep/lobeT's reference is 4.549e-07, three to four orders below every other cell in the table. That reading is inferred: the settling experiment — shrink the tube, refine the quadrature, watch the two sides converge — has not been run, so those two cells are reported unresolved and the adversary's verdict stands on the other ten, not on all twelve. The 21 pinned films are byte-identical across the change, and the pre-change set was itself verified 21/21 against the last recorded run before a line was written, so the baseline is a measurement and not an assumption. The bias-ladder film and the eight-batch film are 20/20 byte-identical, so the error bars cost no centre.

Two panel behaviours are load-bearing here and worth naming. Every intersection the group makes runs through the estimator's shared options, so a direction-preserving surface is transparent to all three members: a passthrough wall lets the camera stand outside the fog box without any of them noticing, and unlike the three photon walks the group has no walk to relaunch, because it is a single segment from the light. And the group's shadow ray is applied to all three members rather than to the one that happens to need it, which is load-bearing rather than tidy: the supports must agree or the balance weights would be weighting a path class that one member cannot reach.

When a strategy's scene requirement is not met, the refusal is printed under the Strategy combo in orange — what this scene lacks and what would fix it — rather than left in the log, because the symptom of an unmet requirement is a black frame and nobody hovers a control that appears to be working. The one-dimensional group declares that it needs both scene lights and a medium. Complement mode's live ownership read-out sits beside it and prints the resolved partition from the same declaration the runtime uses; it is the read-out whose needs bitmask was corrected on the volume-light rig, where two strategies had declared scene lights while their bodies actually looked for an emissive volume, so preset 43 would have been refused for the wrong reason.

Not in version one, and said so plainly. Single scatter only — all three are the first segment out of the light, which is why the mixture's beam leg is written locally rather than calling the shipped multi-bounce walk. No surface bounce yet, though the evaluated payload above is exactly the property that would allow one. No flexible subset dial: 30 is the fixed trio, and a mask for it would be four lines nobody has needed. And Tr(t) assumes the medium is homogeneous between the vertex and the crossing, which is exact for every fog box these run on. The obvious optimisation — a closed-form quartic solve in place of fifty bisection iterations — was not attempted, because the bisection is the version that is provably right and the cost is not out of range. Which members that covers is a correction. On the re-measured column — three interleaved replicates, harness/ra_gate/cost.json — the ring and the arc are 1.22× and 1.04× the camera walk's own 3.410 ± 0.567 ms per pass, inside its class; the mixture is 2.2× it. Until 2026-09-08 this page put all three inside the walk's class, on a cost column that had no stored artifact behind it.

05 — The contribution

An analytic caustic through a glass sphere

Refraction is where analytic connections stop. A path that bends at two dielectric interfaces has no closed-form connection — which is why caustics have been left to photons and kernels. This one is not.

For a spherical dielectric there is a symmetry that rescues it. Take the axis a = normalize(C − y) from the light through the sphere's centre. The entire refraction chain — entry, interior, exit — is rotationally symmetric about that axis. Rotating the whole chain is a rigid motion, so the exit rays sweep a surface of revolution: a waisted, hourglass-shaped caustic envelope that can be intersected in closed form.

medium a ψmax C glass ball φ waist ψ x1 x2 r ωout eye d a b x J = |((a×(x−y))×ωout)·d| a = ∂x/∂φ = a×(x−y) b = ∂x/∂r = ωout 20 · photon hourglass · sample ψ on the cap, sweep φ, r
SchematicThe meridian chain at φ = 0 — emitted at polar angle ψ, refracted in at x₁, out at x₂ — and its revolution about the axis a. The exit rays sweep a waisted surface; the camera ray meets it where the white ring sits, and the two tangents there are a × (x − y) and ω_out.
J = | ( ( a × (x − y) ) × ωout ) · d |
The co-area Jacobian of the swept exit surface. The fold dilation det3 cancels per branch,
so the MIS weights are closed-form and O(1) — no root-finding anywhere in the estimator.

Two properties make it a result rather than a trick. First, the density p = Σ pc·|J| / |det3| was derived four independent ways and then measured against the sampler, agreeing to 1.0007. Second, as the index of refraction approaches 1 the hourglass degenerates exactly onto the photon cone — strategy 09 — which pins the measure convention against an estimator that was already validated. That limit matches to nine digits.

A · kernel estimator, r = 0.70The same caustic estimated with a large photon-gather kernel, visibly blurred
B · analytic pair, r = 0.15Analytic volumetric caustic beneath a glass sphere in fog
Preset 48The acceptance scene: one glass ball, one point light, dark walls — built so the stochastic beam and the hourglass pair describe exactly the same transport and can be divided against each other. Both frames are 1000 passes over the same bounce range; the only difference is the estimator. B is strategy 21 at r = 0.15, its beam member included.

How much of the pair is still the kernel

The pair carries the beam's radius, so it has to be read the way strategy 30 is read one section up: as a ladder. Preset 48's caustic isolated to the pure transmit-transmit class (min bounce 2, max bounce 3), 217×140, eight disjoint batches of 400 passes per cell, and batch k of every cell is the same block of pass indices, so every ratio below carries its batch-paired bar. Producer: docs/hourglass/hg21_ladder.py, artifact hg21_ladder.json, run 2026-09-08. Energy is the currency: a kernel estimator on an open receiver blurs shape and conserves energy, so the energy column is the one to read.

kernel radius18 · beam alone, energy21 · pair, energy21 / 2021 / 18 at the same r21 / its own r = 0.0375 rung18 / its own r = 0.0375 rung
0.300.03477 ± 0.000040.03548 ± 0.000021.0247 ± 0.00041.0205 ± 0.00091.0185 ± 0.0004 · 44 σ0.9982 ± 0.0022
0.15 — every frame on this site0.03471 ± 0.000060.03514 ± 0.000021.0150 ± 0.00051.0124 ± 0.00151.0089 ± 0.0004 · 20 σ0.9966 ± 0.0022
0.0750.03472 ± 0.000080.03495 ± 0.000021.0092 ± 0.00051.0064 ± 0.00221.0032 ± 0.0003 · 12 σ0.9969 ± 0.0021
0.03750.03483 ± 0.000090.03483 ± 0.000021.0060 ± 0.00051.0001 ± 0.0026——

Solo strategy 20 on the same isolation: 0.03463 ± 0.00002, the row every 21 / 20 ratio divides by. Energies are whole-frame mean luminance; ± is the standard error over the eight batches, and the two right-hand columns divide batch by batch. Re-run 2026-09-08 after the hourglass crossing solver went to double precision: the 18 cells are byte-identical, solo 20 rose 0.16 %, and every rung moved by 0.0005 or less.

The beam alone is flat in its radius, as a kernel estimator should be: 0.9982 ± 0.0022 and 0.9966 ± 0.0022 at r = 0.30 and 0.15 against its own r = 0.0375 rung. The pair is not. Its energy rises with the radius it carries, first order in r — +0.9 % at the radius every frame on this site is rendered at, +1.9 % at twice that, both resolved on the paired bar beyond argument. That is the structure complement mode refuses strategy 30 for as a primary: at a finite radius the beam deposits within r of the sheet while the hourglass numerator is evaluated as though the deposit were on it, so the balance weights do not sum to one pointwise, and the total the pair reports for its box moves with r. It vanishes only as r → 0, which is the definition of consistent. Its shape does not move: the energy in its brightest 1 % of pixels is 0.133–0.136 at every rung against the solo hourglass's 0.136, and on the Gate frame its waist is 7.7 px at r = 0.15 and 7.5 at r = 0.05. The kernel shows in the energy, not the width.

What the pair buys is unchanged by this. The solo hourglass reads 0.25 % below the beam on this isolation (0.03463 against 0.03471), and a straight line through the four 21 / 20 rungs meets r = 0 at 1.004 — the guard's discards handed to the beam at weight one. Solo, the hourglass is kernel-free and discards. Paired, it discards nothing and carries a kernel. No shipped estimator of this caustic earns the word unbiased, and this site stopped using it on 2026-09-08.

fog axis a C glass φ r 20 waist 18 x1 x2 y ψ 19 eye a b x MIS ledger A_i = p_i·det3 w_i = A_i/ΣA A18 = pdfDir·T1T2·2·rad·sinθ A19 A20 = pBall·p_c·J det3 cancels per branch → closed-form weights, no root-find 18 tube · traced exit ray + 1-D box kernel 19 point · 3-D kernel 20 sheet · sample ψ•, sweep φ,r · J = |((a×(x−y))×ω_out)·d| tangents at x: a = ∂x/∂φ (rotation about the axis) b = ∂x/∂r = ω_out 21 · 18 + 19 + 20 · converged median 1.0000
SchematicOne deposit reached three ways — the beam's tube, the point's kernel, the hourglass's exact sheet — and the ledger that combines them. Because the fold dilation is a property of the chain rather than the estimator, it cancels per branch and the weights are closed-form.
Screenshot — A7 · Compare ▸ Split A/B · preset 48 · A strategy 20, B strategy 18 with Min bounce 2 · drag the divider · then preset 52, strategy 21, spectral
Split A/BThe kernel-blurred beam on one side of a draggable divider and the analytic hourglass on the other, same scene, same passes — and the dispersive prism fan under the paired estimator.

Dispersion comes free

Because the estimator is parameterised by the refractive index rather than by a stored photon, making it spectral costs one substitution: draw a hero wavelength, evaluate Cauchy's relation, and let the beam and the hourglass share the same channel so the two techniques stay correlated. The caustic separates into a spectrum on its own — a dispersive volumetric caustic, analytic on the sheet and consistent as a pair, for the price of one line.

A dispersive caustic separating into a spectrum
SpectralDispersive glass under the paired estimator. The fan is the wavelength-dependent caustic; nothing in the estimator changed except the index.
Three glass spheres and two aimed mirrors casting caustics through fog
RelaysThree glass spheres and two aimed mirrors in fog. The light is reflected to a virtual origin before refraction, so a straight virtual chain stands in for the bent real one — mirror relays without a second bounce integrator.

How far the sheet can be pushed, and where it refuses

Three extensions landed after the hourglass itself, and they share a method worth naming before the numbers: each one asks whether some new transport is still a rigid motion of the same swept surface. Where it is, the existing quadric solver is untouched and the extension is exact. Where it is not, the estimator does not approximate — it refuses, by name, in the log.

  • The refraction chain. Push the sheet through a chain of glass balls instead of one. The revolution about the axis from the light to the first centre is a rigid motion of the whole chain if and only if every centre lies on that line — so a collinear chain costs nothing in the solver and a ball off the axis is refused rather than approximated. Which also makes it a point-source result: with a finite emitter the axis moves every sample, and the measured mean perpendicular offset for a half-0.4 quad is 0.395, with fifteen samples in a hundred thousand inside tolerance. The spec did not carry that correction; the measurement did.
  • The post-ball mirror. Light, ball, fog, then a mirror, then the eye. A reflection is an isometry, so the camera ray is reflected in the plane and the same quadric is solved against it; a root survives only when the bent path is real at every leg. It composes with the light-side relay and with the chain, and it is the one flag that turns the rasterised gather off — the screen bound projects the sheet where it is, not where its mirror image is seen.
  • The glossy ring. Strategy 24's reflection cone taken off the delta: the family's first vertex on a rough surface. A glossy vertex imposes no reflection law, so where every other member solves for the light point and the polar angle, this one samples both — five free dimensions rather than three — and evaluates the BRDF at every crossing rather than once.
Chain — variance per pass against the stochastic beam60.4× / 116× · 35.5× / 48.5× on equal time
Chain — energy on the caustic mask1.0023 ± 0.0061 · 0.9966 ± 0.0068
Chain — the off-axis pair, same configfilm sum exactly 0.000000, one refusal line
Mirror — against the same scene from the eye's mirror image0.9979 ± 0.0017 by energy
Mirror — cost of the flag1.038 ms/pass (2.507 with, 1.469 without)
Glossy ring — against the camera walk on the same path class1.0005 ± 0.0088 at roughness 0.16
Glossy ring — variance per pass, three roughnesses2.22× / 3.13× / 2.61× · 6.6× / 8.3× / 6.8× on equal time
All three21 of 21 pinned films byte-identical with the flags off

Two of those rows say more than their numbers. The chain's refusal is the acceptance criterion for the whole approach: a scene the estimator cannot represent exactly renders as a black film and a named log line, not as a plausible picture that is quietly wrong. And the glossy ring's variance row falsified a written prediction — the design note said the ordinary camera walk would overtake the swept estimator as roughness rose, and it does not, at any roughness in range.

What the ring cost, and why it is on this page rather than in a footnote. Sampling the polar angle from a lobe-shaped density pins one coefficient of the shared cone quadric to exactly 1, at which point the leading term cancels in single precision and the estimator invents energy: measured at 81.5×, 108.8× and 8.7× the correct value at roughness 0.05, 0.09 and 0.16. It is the sharpest illustration on this site of why every member is divided against an independent reference before the next is started — the picture at roughness 0.16 looked entirely reasonable while carrying nearly nine times the energy it should have. And the defect was never the ring's alone. Strategies 9, 13, 23, 24 and 26 solve the same quadric, and on 2026-09-07 the shared solver was reformulated — ray-centred, both differences of squares factored, the roots paired — and moved to double for every one of them; 27 keeps the separate double crossing it already had. Five of the twenty-one pinned films moved at 217×140 and 150 passes, and they are exactly the five that reach a cone. The direction was then settled by an arbiter that shares no line with either build, rather than by the fix arguing for itself: the quadric re-derived in exact rationals and cross-checked against mpmath at 120 digits, the two agreeing to 4×10−39, run on the individual samples that moved. Six of the seven films move decisively onto it; the seventh moves by an ulp the other way with its maximum unchanged. The largest single change, −3.80 % on one preset-48 film, is carried by one sample at pixel (83, 52) whose exact crossing lands 0.00275 outside the emitter — no light path, true contribution zero, and the 55.42 the old arithmetic paid it was invented energy. It is not the whole of the −3.80 %, and saying so was sloppy: the film sum moves 1135.22 to 1092.10, a net of −43.12, so that single sample is 128 % of the net and the rest is paid back by pixels that rise — +9.72 at (102, 61) and +2.38 at (90, 56), the two places the Jacobian was too big rather than the root wrong. One number, two mechanisms, and they point opposite ways. One residual is open and is recorded rather than smoothed over: the solver builds its leading coefficient assuming the camera direction is exactly unit length, and on one near-tangency sample the shipped Jacobian is still 1.93× the arbiter's, against the old form's 6.27×.

One boundary is also now measured rather than assumed. A general, non-collinear chain was costed with a standalone harness that links nothing from the renderer and implements the crossing exactly as the specification describes it: 2 475–3 251 ns per footprint pixel, 1.79 roots per pixel, a sheet covering 6.7–9.4 % of the frame. At the batch sizes this estimator runs, that is 188 ms per pass at the theoretical floor and 443–557 as implemented, against one-ball strategy 20's 13.1 — fourteen to forty-two times. The general chain is a no-go, and it is a no-go because somebody wrote the harness rather than because somebody estimated.

Screenshot — CHAIN: two collinear balls (preset 68) · integrator mode 9 · strategy 20 with Chain length = 2 · Min bounce 2 · 2000+ passes · clean view (G) · frame both balls and the waist between them; then load CHAIN: the same pair OFF-AXIS (preset 69) at the identical settings and shoot the black film beside the console line reading [PP][hgchain] REFUSED

The second half of that shot is the point. An estimator that renders nothing, loudly, on a scene it cannot represent is worth more than one that renders something on every scene. It is the same standard as the projection self-check on The camera is not a viewpoint, arrived at independently in a different part of the estimator: refuse rather than approximate, and say which line refused.

05b — The hourglass as a system

One member exists. The family is finite.

The refraction chain through a glass sphere has exactly three free parameters, and a camera ray consumes exactly two of them. That single fact enumerates every analytic hourglass there can be.

Write the chain as y → x₁ → x₂ → x: a light point, an entry point on the sphere, an exit point, a scatter point in fog. Given the emitted direction — polar angle ψ about the axis a = normalize(C − y) and azimuth φ — both refractions are deterministic, and the exit ray is one line. Add the exit distance r and the whole chain is a map (ψ, φ, r) → x. A camera ray is a 1-manifold in R³, so meeting it costs two parameters. Therefore:

sample one of { ψ, φ, r } · sweep the other two · intersect the camera ray
Every analytic member has this shape. The stochastic members (18, 19) sample more and pay with a kernel.
F20b · hourglass flavours sampled swept kernel member ψ φ r sheet 20 hourglass in engine revolution about a ior→1 ⇒ cone (9) 20-φ not built meridian sheet (ruled) ior→1 ⇒ disk (8) 20-r not built r shell at fixed r ior→1 ⇒ sphere (7) V2 fixed-apex Python only θᵢ+xₛ nxₛ cone about n at xₛ one interface, pdf_A·p_c 18 beam in engine tube: ray + 1-D kernel stochastic reference 19 point in engine point + 3-D kernel coverage, Θ(rad²) not drawn: mixed-L (traced light leg, sweep φ,r) · mixed-C (camera-seeded, sweep ψ,φ) — sketches only, see docs/hourglass/FLAVOURS.md the hourglass as a system: sample one of {ψ, φ, r}, sweep the other two
FlavoursRows are members, columns are the three chain parameters. A red dot is sampled, a yellow arc is swept, a grey square is a kernel. Only two rows are in the engine; the fixed-apex form that the derivation started from exists in Python only.

The member that ships samples cos ψ uniformly on the cap and sweeps the revolution φ and the exit distance r — "φ is never sampled; the revolution is consumed by the incidence." Its density is p = p_c · |J| / |det3| per branch, and its ior → 1 limit collapses onto the photon cone, which is how the measure was pinned.

What makes the family worth building rather than admiring is the same thing that made the three-plane and direction-trio mixtures work: every row has a different singular locus on the same transport. The revolution form folds at the caustic waist; the meridian form folds along its own half-plane; the exit-distance shell is singular where the camera ray grazes a closed surface; the fixed-apex cone is singular at its own tangency. Under a balance heuristic the member about to misbehave is the member that down-weights itself. And each missing member arrives with its acceptance test already written — its ior → 1 collapse lands on a primitive that is already validated (the meridian sheet onto the disk, the shell onto the photon sphere).

On the word "tridirectional". For the shipped member alone it overclaims: the dielectric vertex is derived from the light-side sample by Snell's law, not seeded, so the honest count is two subpaths joined at a derived vertex. It becomes literally accurate for the surface-seeded member — sample a coordinate of the entry point on the sphere and hang the light connection and the swept exit off it: three independently parameterised pieces — light, the two-segment dielectric chain, sensor — joined at two constraints. Used for the family, and defined on first use, it is a definition rather than a misuse.

05c — The other hourglass

Seed at the glass, not at the light

The shipped hourglass keeps its free angle on the light side and derives the entry point. Turn that around — sample the entry point on the glass and an incoming angle about its normal — and you get a simpler object with a different reach.

Sample x₁ on the ball and a polar angle θ_in about the normal n(x₁). Snell fixes θ_out; incident and refracted rays share one azimuth. Revolving that azimuth sweeps two cones apex-to-apex at x₁. For a sphere the normal passes through the centre, so the revolution is a symmetry of the ball and the whole chain — entry, chord, exit — rotates rigidly. The exit rays sweep a surface of revolution about C–x₁, and the camera ray meets it by the same revolved-quadric solve the first hourglass already uses.

medium air emissive quad C glass ball n = axis through C x1 (sampled) θin y (traced hit) trace · Le·Tr·T1 x2 ωout φ waist of the exit family (not a vertex) r eye d a b x p22 = pdfA(x1) · pθ · |J| / |det3| J = |((n×(x−x1))×ωout)·d| a = ∂x/∂φ b = ∂x/∂r 22a · entry-seeded · the camera meets the EXIT family after a 2nd refraction (hyperboloid) · light half traced
SchematicThe seeded vertex x₁ (red), its incident cone opening into air, the chord and exit family revolved about the axis through C, and the camera ray meeting the exit sheet in fog. At the crossing azimuth the incident ray is one ray — so it is traced back, and here it hits the emissive quad.

That last clause is the whole story of when this member applies. Count dimensions: x₁ (2) + θ_in (1) = 3 sampled, and the camera ray consumes the two swept ones. Nothing is left for the light side. With an area emitter the incident ray simply hits it; with fog before the glass it becomes a beam to connect along — a double-scatter caustic nothing renders analytically today. With a point light a single ray cannot hit, and the classic single-scatter caustic has no analytic vertex-seeded member at all. That class belongs to the light-seeded hourglass — which is exactly what its axis-revolution trick buys. The two are complementary, and the availability matrix by surface type and medium per side is written out in docs/hourglass/VERTEX-HOURGLASS.md.

Same chain code as strategy 20seed and axis differ, nothing else
Strategy 20 refactored through the shared helperbyte-identical — three pinned films (a1d4977f · fa75b2f1 · 6b070490) reproduced
22a / 20 on the shared class, presets 47 · 52energy 22a/20 = 1.0066 · 1.0195; 22a/18 = 0.9886 · 1.0046 (20/18 = 0.982 · 0.986 on the same support)

Two cones at the vertex — the form that was actually meant

The member above is not quite the sketch it came from. Seeded at the entry, it lets the camera meet the exit family only after a second refraction — so the sheet is a hyperboloid whose waist sits out in the fog, not at any vertex. The sketch had the waist at the sampled vertex, and that is a cleaner object: given θ_in, the interface law fixes θ_out and the two directions share an azimuth, so the admissible pairs are exactly two cones apex-to-apex at the vertex — same side and same angle for reflection, opposite sides and different angles for refraction. The camera meets the outgoing cone itself, and a cone's intersection and Jacobian are strategy 9's, verbatim, with the apex moved onto the glass.

23 · refraction · seed at the exit x₂ medium C n x2 (sampled) φ θout r θin x1′ emitter (traced) eye x ba waist = the vertex cone in the medium: J = t·|cosθout(ω·d) − n·d| 24 · reflection · seed on the outer surface medium C n x1 (sampled) θθ emitter eye x payload: Fresnel R(θ) (glass) or albedo (mirror) the specular-reflection caustic of a sphere the user's hourglass · two cones apex-to-apex AT the sampled vertex · the camera meets the outgoing cone directly contrast 22a (previous figure): seeded at the entry, the camera meets the exit family AFTER a second refraction — a hyperboloid whose waist is in the fog
SchematicLeft, refraction: the outgoing cone of an entry vertex would live inside the glass, so the seed goes on the exit — the cone opens into the fog, and the light half is the chord back through the ball, refracted out and traced. Right, reflection: both cones on the outside, same half-angle — the specular-reflection caustic of a sphere, which nothing renders analytically today.
23 · exit-seeded cone vs 20 / 22a / 1823/20 = 1.0001 · 1.0001 (48k vs 20's converged 12k, presets 47 · 52; equal-pass 0.985 is the tail, not bias) — 23/22a = 0.996 — 23/18 = 0.9932 · 0.9890, identical to 20/18 to four digits
24 · reflection cone vs reference24 / beam(reflection branch only) = 0.9982 on a single glass ball at 6k · 6k; 0.9996 with the top-20 px removed from each
Coverage at equal passes, 23 vs 22a vs 20@1500: 23 lights 61% (47) · 50% (52) of 20's pixels and 88% · 93% of 22a's, heaviest tail (max px 9.6 · 20 vs 20's 0.44 · 0.55); by 12k 23 = 22a at ~90% · 80% of 20. 24 fills 1.5× the beam's pixels

What the two cone members measured. Both converge to 20's energy — 23/20 reads 1.000 once its tail has settled (48k passes against 20's converged 12k) and inherits 20's ~1% residual against the beam to four digits — but standalone, the exit cone is not the more efficient member: at equal passes it lights 50–60% as many pixels as 20 with the heaviest tail of the three (its worst pixel 20× 20's), and its own 12k/48k ratio is 0.985. The reflection cone is the real gain: it is the first analytic member of the L Srefl V E class, matches the beam's reflection branch at 0.998 on a single glass ball, and fills 1.5× the beam's pixels. The variance study says why — the vertex members are light-trace-limited, and the exit cone also pays a chord back through the glass. Its remaining case is the {20, 22a, 23} mixture: three members with different singular loci (waist fold, sheet tangency, cone tangency), untested until the shared-measure ledger lands.

This is the member that earns the word "tridirectional". Light subpath, sensor subpath, and the two-segment dielectric chain seeded independently and joined at two constraints. Used for the family, defined on first use, it is a definition rather than a claim about connection edges.

05c — Surfaces

The reference denominator comes first

Every primitive above was certified by one number: its median ratio against an independent estimator of the same integral, converging to 1.000. Surfaces had no such estimator to divide by. So the first surface code in the integrator is not an estimator — it is the thing estimators will be divided against.

At an opaque, non-emissive hit in the camera walk, make one Lambertian light connection using exactly the light set and sampling densities the medium estimators use, and stop. No continuation bounce. Mirrors and glass are untouched; the walk still terminates where it did. It is a reference, deliberately, and it is switched off by default so that every previously validated number remains bit-for-bit reproducible.

f = ρ/π · cos_s · L_e · cos_y / r² diffuse wall · ρ emissive quad · L_e camera camera ray r n_y cos_y n_s cos_s no continuation y x_s surface NEE · one Lambertian connection · the L D E reference denominator
Surface NEEOne connection from the surface vertex; the continuation is drawn and crossed out because it does not happen. f = ρ/π · cos_s · L_e · cos_y / r², with the emitter's own directional profile and the transmittance through any fog on the connection.
Off — six films vs the checkpoint binarybyte-identical
On — vs the GPU path tracer, one surface bounce, sky zeroedPP / GPU median 1.0005 (p25 0.963, p75 1.039, n = 40,842 diffuse px)

The comparison needed a small piece of plumbing that did not exist: a headless control for the environment light. The test scene is an open-fronted Cornell box, and sky light reaching its walls is a path class the integrator has no emitter for — so it had to be zero on both sides, or the ratio would have measured the sky rather than the estimator.

What the footprint idea actually requires

The obvious surface primitive — intersect the swept photon sheet with a wall, get a footprint, evaluate where the camera ray lands — cannot be an estimator. Count dimensions: three unknowns, four equations. A 2-manifold sheet meets a 2-manifold receiver in a curve, and the camera's hit point is almost surely not on it. That missing dimension is exactly what a photon-map radius has always been buying.

φ t 2D ∩ 2D = a curve — the gather does not exist lighteye x receiver S footprint x ∉ footprint φ t curve ∩ curve = a point J = det[Np, Nc, nS] lighteye Np Nc nS a b x θ t receiver S surface primitives · give the camera a sheet too
FootprintLeft: sheet ∩ receiver is a curve; the camera hit is off it, so no gather exists. Right: give the camera a sheet too, and curve ∩ curve is a point. The co-area factor is the triple product of the two sheet normals and the receiver normal — the surface analogue of |N·d|.

The repair is symmetric: give the camera a sheet as well. Two curves in a 2-manifold meet in isolated points, and the Jacobian is det[N_p, N_c, n_S]. Both incidence cosines land in the numerator, so grazing drives the weight to zero rather than infinity — the opposite of the medium primitives' failure mode. It also has a closed-form density in both directions, which is the admission ticket to the MIS network. That is the next estimator; the reference above is what it will be divided by.

05d — When does an estimator work well

The variance has exactly three sources

"Planes work when they face you" is true and useless. The systematic version is a closed-form Monte Carlo of every primitive's weight over the crossings a camera actually sees — 22 estimators, four parameter families, every number cited to a run.

Each analytic primitive samples some photon dimensions with a density p, sweeps two more into a sheet, and meets the camera ray at a crossing with weight

W = f / ( p · J ), J = |(∂x/∂α × ∂x/∂β) · d|
and W = 0 when the sheet misses the ray. The per-pixel relative variance then comes from three places, and every finding in the study is one of them.
  • Miss mass. With crossing probability P, the zero point-mass alone contributes (1 − P)/P. Measured as the whole story for the UV quad (rel std = 1.05–1.16 × √(1/P − 1) on 90% of pixels) and as the exponential wall of the photon sphere in dense fog.
  • How J vanishes. Constant across a pixel: a grazing band between pixels, no in-pixel tail. Linearly with the sampled coordinate (the planes): an integrable tail, mean converges everywhere. As the square root — a fold, two quadric roots merging (sphere, cone, and every hourglass): W ~ ε^{−1/2}, tail index 2, and E[W²] is log-divergent. The sample variance of a fold member never converges (the sphere's grows 5.2 → 20.1 from 10³ to 1.6·10⁷ samples) and must never be read as a convergence gauge. A Cauchy fan (the disk): bounded, but silent on two exact rays.
  • Whether p tracks f. Phase function, transmittance and emitter cosine are common multipliers — unless the phase peak sits on a member's singular locus. The same g = 0.8 divides the sphere's variance by 23 and multiplies the disk's by 11 on identical Jacobian histograms.
dirsweep_fig_var_vs_N.png
dirsweep_fig_phase_point.png
Study figuresLeft: the sample variance of the sphere and cone estimators keeps growing with N — the fold's log-divergent second moment, measured. Right: the same crossings under three phase functions; the sphere's fold sits at the phase minimum and the disk's fan at its maximum, so one g helps one member and hurts the other.

The hourglass verdict, by light size

The efficiency question between the two seeds has an answer, and it is not the Jacobian. The vertex members are light-trace-limited: at the baseline waist their light half succeeds on 1–2% of roots, and that is the whole 30–90× gap in coverage below. Their Jacobian term is actually 2–3× smaller than 20's, with no axial pathology.

membercoveragecv²relvarwins when
20 light-seeded0.7388.211point lights and emitters under ~10° — nothing else exists there; worst at the paraxial focus (331), best just under the ball (4.2)
22a entry-seeded0.0112.5331emitters over ~35°: overtakes 20 at edge 2.4 (27.6 vs 65); flat in ior
23 exit cone0.0042.5978only low ior (≤ 1.2) with rays perpendicular to the axis; its back-trace through the ball loses coverage as ior rises
24 reflection cone0.003–0.0061.7–10450–2140reflection caustics from emitters of edge ≥ ~1 — 1.3–50× the reflected beam; loses below edge 0.3
18 beam, r = 0.010.1100.149.3as good as 20 at the waist; degrades linearly with the bundle radius; kernel-biased at the axial caustic

What the numbers say to do next. Not MIS between the seeds — their densities live in different measures and the vertex members rarely have a root where 20 does. Importance-sample the light half instead: aim θ at the emitter and the 1–2% success rate is the entire gap. The medium, meanwhile, is irrelevant to the whole hourglass family (coverage 0.738 at every σt).

fig_hg_lightsize.png
fig_hg_height.png
Hourglass sweepsLeft: relative variance against emitter size for the four members — the crossover from 20 to 22a is where the emitter subtends ~35°. Right: camera height through the caustic; 20's spike at the paraxial focus is the axial line caustic sending every revolved sheet's ρ → 0 at once.

Six of the fifteen rules, each with its measurement in docs/variance/README.md: a single swept plane degrades like sin(γ)^{−3} in its angle to the camera; the three-plane mixture caps the worst pixel at 1.5 rel std in every viewing direction; never use the random-orientation plane when the continuous mixture exists (same samples, 1.6–2.2× better); the cone has an exact on/off criterion for bounded variance; the medium density matters only to members that sample a distance from it; and epsilon guards are irrelevant to variance — acceptance geometry and mixtures are what keep it finite.

The study also records where its own Monte Carlo means did not match quadrature — a −0.7% cone clamp bias, the beam's kernel bias at the axial caustic, and a fold-tailed running mean sitting 31% high at 4·10⁵ samples before settling — so nobody has to rediscover them.

06 — What the estimator actually looks like

Freeze the batch and the maths becomes a photograph

Every estimator on this page samples a fresh primitive per camera sample, so the geometry averages away before you can see it. Reuse one batch across the whole frame instead and the individual swept sheets stay on screen — each weighted by its own co-area Jacobian, which is exactly the quantity the rest of this page is about.

This is photon mapping's trick applied to analytic primitives, and it cost one line. The integrator already drew every photon-side random number after the camera ray was built, so correlating is a split of the random-number stream rather than a refactor: seed the photon side from the primitive index instead of the pixel, and every pixel that lands on index k rebuilds the same primitive bit for bit. Not one strategy block changed.

correlated/fig-cone-9.png
Sixteen photon cones, one apexEach cone is one sample of the same estimator: a photon leaves the light at a fixed polar angle, and the azimuth and distance are swept into a surface. The camera ray meets that surface in closed form. Every cone shares the light as its apex, and the searing point where they all meet is not a bug in the render — it is the Jacobian going to zero at the tangency locus, which is the thing the mixture later has to fix.

Each family fails in its own shape

The variance study classifies how each primitive's Jacobian vanishes — constant across a pixel, linearly, as a square-root fold, or as a bounded fan. That classification turns out to be literally a prediction about what the frozen batch looks like, because the Jacobian sits in the denominator.

correlated/fig-sphere-7.png
correlated/fig-hourglass-20.png
Fold membersLeft, the photon sphere: a shell of constant flight distance about the light, and two quadric roots merge at its silhouette, so every bubble carries an incandescent rim. Right, the hourglass: sixteen caustic horns, and every one of them piles its brightest region onto the same axial line, because at the paraxial focus the revolution's Jacobian collapses for all of them at once.

The clearest argument for mixing anything

The balance heuristic changes a weight formula and draws no random numbers. So the two frames below are the same frozen batch, the same sixteen primitives in the same places, with only the edge weights different. Nothing about the geometry moved.

correlated/fig-mis-13-flat.png
correlated/fig-mis-13-balance.png
Flat weights, then balancedLeft: the three primitives averaged with equal weight — the rims are still there, because each member is still fully trusted on the locus where it is worst. Right: the balance heuristic, where a member's weight falls exactly where its own density spikes. The rims go out. This is the whole mixture argument in two frames that differ by one formula.
Film maximum · cone alone49,000
Film maximum · three primitives, flat weights4,562
Film maximum · three primitives, balance heuristic368.7

Two predictions that did not survive

The design document predicted a picture for each family before any of them was rendered. Most were right. Two were not, and they are worth more than the ones that were.

  • The disk's pinwheel is unreachable. Its blades are half-planes that contain the light's axis, so the pinwheel is what you would see looking straight down that axis — and every scene in this renderer has a level camera. From the side it is a fan of vertical blades, and level with the light it degenerates to two half-frames.
  • The quad's tangency is one scanline, not scattered slivers. The estimator translates the light quad without rotating it, so a ceiling quad stays horizontal at every distance and a level camera sees every member near edge-on at the same height. The whole estimator's variance lives on one row at the camera's eye level — which is also why its screen footprint is so small.

The trap, fired deliberately. The normalisation is one over the number of primitives drawn, never the number that crossed the ray. The wrong version was implemented behind a debug flag first, purely to watch it fail: it reads 2.1× to 6.8× too bright here, and it converges — 4.385, 4.447, 4.451 at rising pass counts on one preset. A quietly wrong image that looks more correct the longer you render it is the worst failure mode available, so it is now a thing this project has seen rather than a thing it was warned about.

06b — The speed

Blitzing fast, no neural network, no kernel on the sheet

Those three do not usually share a sentence about a volumetric caustic. Neural methods buy speed with a network; kernel methods buy it with blur; exact methods have been slow. The hourglass solves its sheet in closed form at the speed below. The word it does not get is unbiased: solo, its guard discards; paired, it carries the beam's kernel at weight O(r), and section 05's ladder says what that costs. The numbers below are what make the rest of the sentence true.

Per pass — before7.05 ms
Per pass — shipped1.17 ms
Film for these rows217×140 · preset 48 · strategy 20 · CPU, i9-13980HX, 32 threads; the GPU only displays the film
Of which estimation0.76 ms
Rasterized gather, bit-identical to tracingquad 16.7× · beam 5.8× · hourglass 1.1× — it crosses half the frame, so there is nothing to skip

The first row is worth a sentence, because it is the kind of number this project publishes rather than hides. Eighty-eight percent of a photon-primitive pass was the renderer waiting for a vertical blank: the window hardcoded a swap interval of one and drew one accumulation pass per frame, so every pass paid a display refresh against 0.76 ms of actual work. It was found by one measurement — a twenty-fold change in film size moved the per-pass cost by seven percent, so nothing inside the pass could own it — and fixed with a passes-per-frame budget. Thirty-three films, byte-identical before and after. The estimator had been fast all along; the display was hiding it. How reproducible is a millisecond on this site? Measured, and only once: the one cost column that has been run three times, interleaved, on a single binary holds to 4.4–9.1 % within a session, and moved by about 1.6× between two sessions a day apart on this laptop (docs/ring-arc/harness/ra_gate/cost.json). Every wall-clock number on this site carries that precision, this one included. The ratios it is spent on — 7.05 against 1.17, 16.7× on the rasterised gather — are taken inside one session, which is the half of it that reproduces.

The frame at the top of this page is the measurement. The Gate, preset 54, 363×505, strategy 21, 16,000 passes. Shot as a pixel-registered A/B at 6,000 passes against the stochastic beam at r = 0.70: the analytic side's waist peaks 5.3× higher and its full width at half maximum is 7.7 px against 70, while the two frames agree on total energy to 4 %. Same transport, nine times sharper, at the same pass count — against a beam given nearly five times the pair's radius. A 1D kernel's width scales with its radius, so on 2026-09-08 the same frame was rendered with the beam at the pair's own radius, 16.1 px across the waist (14.1–18.2 over the band): at matched radius the pair's waist is the tighter by a factor of 2.0, and the remaining 4.5 was the competitor's knob. The pair at a third of its radius, r = 0.05, stays at 7.5 px, so the waist is the sheet's and not the kernel's; its energy is where the kernel shows, and section 05 has that ladder. Both extra films are measured by the same producer over the same band and stored beside the four numbers below. Two of those four numbers are a correction. The readout published 4.4× and 57 px until 2026-09-08; those came from a hand reading whose row was never recorded, and the films measure neither at any row. docs/tools/waist.py is now the producer: it takes the peak and the full width at half maximum of every row in the seventeen-row waist band of the same two films this page inlines, and reports the band median with its row-to-row spread — peak ratio 5.3× (4.7–6.2 across the band), width 70 px (68.5–73.1) against 7.7 px (6.1–10.4), energy ratio 0.96003. The wall-clock of that frame is the one number this section does not yet print: it is what the recording below is for, and it goes in the caption the day it is shot.

A · stochastic beam, r = 0.70The same caustic gathered with a wide kernel: a soft lozenge with no waist
B · analytic pair, r = 0.15The same caustic computed analytically: a needle-thin waist
Preset 54, equal passesThe beam's 70-px smear and the hourglass pair's 7.7-px pinch, same scene, same camera, same number of passes. Neither frame has a denoiser, a neural network or a clamp on it; B carries a kernel too, at r = 0.15, and with the beam at the pair's own radius A's waist is 16.1 px.

What the photon-surface line of work reported, and what this adds

The idea of sweeping a photon's unused dimensions into an analytic manifold is not mine, and it is earlier: Bitterli and Jarosz's photon planes (2017) and Deng et al.'s photon surfaces (2019) proved the variance argument. They proved it at offline cost. The planes paper ran on sixteen Xeon threads, compared estimators at ten minutes each, and reports absolute times of hundreds to thousands of seconds per image, for a 2.4–40× variance reduction over photon beams. The surfaces paper publishes no wall-clock and no hardware at all; its results are equal-time variance ratios, and it shows individual spinning estimators with variance up to 1.8 million times path tracing before MIS tames them. It also names, in its own limitations, the regime it lacks a robust solution for: a medium interaction immediately after a surface event.

This project does not implement either paper's primitive. Its swept planes are a different split of the same dimension count, and the published multi-scatter construction is structurally inexpressible in the class table this family is built on — the transport page says exactly where. What it shares with that work is the idea. What it adds is an implementation fast enough to show what the idea is worth: 1.17 ms a pass on a laptop's CPU cores with no GPU compute in the loop, a rasterized gather that is byte-identical to tracing, a MIS network whose weights are closed-form with no root-find, and a dielectric member — the hourglass — that lands the specular case of the regime the surfaces paper left open, and, paired with the beam, converging to 1.0000 against the beam at the same radius — consistent, with its radius-dependence measured at +0.9 %. No speed ratio against the published work is printed here, because the scenes, resolutions and hardware differ; the comparison that is fair is the one above, against the kernel estimator in the same renderer, and it is 9× in width at equal passes against the beam at r = 0.70 and a factor of 2.0 at the pair's own radius.

video · The Gate, converging — the recording
Preset 54 (The Gate), integrator mode 9 (key 0). Compare ▸ Split A/B (PP estimator): side A strategy 18, beamRadius 0.70, Min bounce 2; side B strategy 21, beamRadius 0.15, Min bounce 0, hgGuard on, misPoint on. Load preset 54 fresh: its focal length leaks into presets loaded after it, not before. PP Estimator ▸ Passes / frame = 0 (Auto). Whatever film size the window gives; the pass counter is the measurement, so write the film size into the caption beside the clock. Do NOT press G: keep View ▸ Integrators HUD on so its SPP row is the on-screen pass counter, and keep the divider at mid-frame through the ball. Start a wall-clock in the capture tool's overlay at pass 0; the app has no ms-per-pass readout, so burn in "1.17 ms / pass measured on a 217×140 film" and a second caption reading "no neural network · side B is the pair at r = 0.15 · consistent, not unbiased" from the capture tool. Record from pass 0 to pass 6,000 in one unbroken take, real time, no speed-up, no cut; then hold 5 s on the finished frame and drag the divider once across the waist. Note the wall-clock at pass 6,000 and write it into this caption.
Watch for Both sides start as noise. Watch the waist: side B collapses to a 7.7-px pinch while side A settles into a 70-px smear at the same pass count, and the shaft above the ball converges to the same brightness on both sides — same energy, different sharpness. Watch the pass counter climb and the clock beside it: that pair is the speed claim, measured on camera.

06 — How it was proven

Two independent estimators, or it didn't happen

Every primitive in the family had to divide against something it shares no code with, and land on 1.000. Every row that claims it has the run behind it; the ones that land elsewhere — the solo hourglass at 0.976, the beam at 1.048, the reflection cone at 0.998 — say so in the ledger, and the pair closes the first to 1.0000.

  • Ratio against the oracle. Each estimator renders the same configuration as the NEE reference until the per-pixel median ratio converges to 1.000. Median, not mean — a single firefly cannot hide a systematic error.
  • Byte-identical regression. The strongest test available, and the one that caught the most. A refactor that should not change the image must produce a bit-identical file; an extra random draw shifting the stream, or a gate that quietly became more permissive, shows up nowhere else.
  • Degenerate limits as measure pins. ior→1 collapses the hourglass onto the cone; σ→0 collapses a stochastic surface onto its mean. Limits where a new estimator becomes an old one are free, exact tests.
  • Four-way derivation, then measurement. The MIS density was derived independently four times before being compared against the sampler's empirical histogram. Agreement to 1.0007 — the derivation and the code were checked against each other, not against expectation.

The habit that mattered most was negative. Reasoning about why an image is dark does not work. One investigation ran for several rounds on careful inference, reached a confident wrong conclusion, and recorded it in a commit message as settled. A single counter in the actual code overturned it in one run: 27657 rays, 15131 hits, 63% correctly blocked. The bug was elsewhere. Instrument first.

07 — What it does not do

The honest boundary

The scope is stated as precisely as the results, because a boundary you can name is what makes a result usable. These six are the boundary, and the first one is the thesis of the whole family.

  • Not a better sampler for easy paths. A swept primitive is more machinery than a light sample and a shadow ray, and on transport that was easy to sample to begin with, the machinery does not pay. Single scatter from an area light is the measured case: the camera walk's next-event estimate beats every swept member at every emitter size, 2.2 to 3.1× per pass. The family earns its cost on the paths that have no connection to make: the caustic through glass, where a connection would have to bend; the emitter too sharp to hit by sampling; the light source with more dimensions than a point sample can cover. There it lands what path sampling finds only by chance and what kernels can only blur. The hourglass is the proof; the sharp emitter and the volumetric light are the next two cases to measure.
  • Medium-only. Surfaces occlude and emit, but do not bounce. Walls look dark next to a full path tracer — that is the declared scope, and it is why every comparison here is against a medium-only reference that describes exactly the same transport.
  • Straight-line primitives cannot refract. Strategies 0–17 and the cross-family mixture at 26 are exact connections; none of them can bend at a dielectric interface. A glass ball under those estimators casts a correct shadow and no caustic. Neither can the one-dimensional group at 28–30, and there the failure is sharper than dimness: a smooth dielectric's emission profile is a two-dimensional delta, integrating a delta over one swept coordinate leaves a delta, and those three read identically zero. Structural, and the reason the hourglass exists. Strategy 27 is the one member that turns at a surface without being a dielectric — it reflects at a rough one, and still does not refract.
  • The hourglass needs rotational symmetry, and now it is tested. It is derived for spherical dielectrics: a glass box or a lens has no axis about which the refraction chain is invariant, and the derivation does not survive. The chain extension is the sharpest statement of that boundary, because it makes the condition checkable at run time — several balls are still one rigid motion if and only if every centre lies on the light-to-first-centre line, and the estimator tests that and refuses when it fails. It is also why the chain is a point-source result: a finite emitter moves the axis every sample. A fixed-apex form for the general case is derived and numerically validated; its light-side closure is measure-zero, and a standalone harness has since costed the general chain at fourteen to forty-two times one-ball strategy 20, which settles it as a no-go rather than an open problem.
  • Solo, it is 2.4% low; paired, it is 1.0000. The pairing with the stochastic beam, which carries internal-reflection chains at weight one, closes the gap: 0.976 → 1.0000. The attribution of the solo shortfall is not settled. The hourglass cannot express internal-reflection chains, which is the obvious explanation — but the grazing guard discards samples rather than clamping them, and the variance study measures it rejecting 2.4% of this strategy's energy at the waist in the same configuration. The two candidate explanations are numerically the same size and have not been separated.
  • Paired, it is consistent, not unbiased. The pair's energy on the isolated caustic moves with the beam radius it carries — +0.9 % at r = 0.15, +1.9 % at r = 0.30, first order in r and resolved at 20 and 44 sigma on the paired bar — while the beam alone is flat in r to 0.2 %. Its waist does not move: 7.7 px at r = 0.15, 7.5 at r = 0.05 on the Gate. That is the same structure complement mode refuses strategy 30 for; the difference is that the pair's box is the one no other estimator can own, so it ships as the production primary with the number beside it rather than without it.