Inverse proportion
Dissecting two circles that look inversely proportional
Two kissing circles keep trading size — one inflates exactly as the other deflates. The drawer is named "inverse proportion", yet measured, the equation where the product of the radii stays constant does not hold. What stays constant is the addition: the radii always sum to 81px. Keyframes sit only on the top-left circle's radius — 3 of them; the other radius is a subtraction from 81, and both centres are computed to recede from a fixed kiss point by their own radius, so the contact never moves in 90 frames. The whole build is dissected as one framework-free pure function.
- Published
- June 10, 2026
- Topics
- Conservation · Complement · Easing · SVG
Stare at the kiss point
In the loop above, the bottom-right circle inflates by exactly as much as the top-left circle deflates, and over 45 frames they trade sizes; the second half trades them back. Now stare at the single point where the two circles touch — however far the trade goes, that contact point never moves once in all 90 frames.
This motion is named "inverse proportion" — the textbook relation where one quantity grows as the other shrinks. In a true inverse proportion, multiplying the two radii (their product) would give the same number on every frame. Measure the original, though, and it does not. What stays constant is the addition instead: the two radii always add up to 81px. Keyframes sit only on the top-left circle's radius — 3 of them. The bottom-right radius is "81 minus the top-left", and carries not a single key.
Write one radius and a one-line subtraction
- 3 radius keys — 54px on frame 0, 27px on frame 45, 54px on frame 90
- one easing curve per segment, two in total
- bottom-right radius: zero keys — just 81 minus the top-left
- both centres: zero keys — each recedes from the fixed contact point by its own radius
The placement of the centres is the heart of the build. Fix a single contact point on the 45° diagonal; the top-left circle puts its centre its own radius away from that point, up-left along the axis, and the bottom-right circle does the same down-right. As the radii change the centres slide on their own, and the circles kiss at that point on every frame — no key ever aligns the contact. The point held still in the opening because it is built to hold still.
The product and sum equations part ways at the moment the circles pass at equal size. The crossing comes around frames 21–22, both radii at 40.5px — exactly half of 81. Keep a product unchanged while pushing two numbers closer together, and their sum always shrinks. So if the product were constant, the radius sum would have to sag to 76.6px at this moment. The measured sum sits at 80.931px near rest and 80.930px at the crossing — a 0.001px difference, no sag anywhere. Next to the constant-sum fit, the product model's miss is roughly 95× larger — so it was thrown out.
No key parks the bottom
At the bottom of the shrink, the motion seems to rest for a while. The first measurement agreed: "there is a hold key here". But that was a fake stillness, created by a habit of the measuring program — it recycled the previous frame's answer as the starting point for the next one. Break the habit, measure again, and the stillness dissolves into a single very shallow valley.
The settled build has no hold key. The motion seems to rest because the tail of the one easing curve each segment owns is long and gentle, easing slowly toward its target value. The outbound and return curves are similar but not identical — reuse a single curve for both, and the error against the measurement grows about 5×.
The curve's tail also carries a small expression: it slips just past its target before settling back, so the radius bottoms near frame 44.1, not 45, and peaks near frame 88.7. The valley and the peak measured independently from the original land on 44.0 and 88.7.
One pure function is all you need
The implementation is one pure function: pass a frame number, get back both centres and radii. Only the top-left radius is keyed; the other is a subtraction, and the centres are computed from the contact point — so contact drift is impossible by construction. Grow the radius keys from 3 to 5 — 54→27→54→27→54 — and the same function fits 2 round-trips of the trade into one loop. The keys, the curves, the 81 and the contact coordinates sit as plain constants in the JavaScript this page loads: the browser's view-source doubles as the spec sheet.