Solver Zoo
work in progress—
Four inverse problems — what to look at, and how far each one goes
Every mode here asks the same shape of question backwards. Forward physics says "given the setup, predict what happens." These ask "given the outcome you want, find the setup" — find the shove that lands the box, the glass surface that casts the picture, the threads that reconstruct the photo. The modes are ranked in the rail, strongest first, and the two at the top are the ones worth your time.
What's new
Controls added by each batch below are tagged in the panel with a chip that names the batch — NEW · radiometry, NEW · lens, NEW · framing, NEW · showpiece, NEW · scoring — tinted to match the entry it belongs to here. The tint is a fast visual grouping; the word is the identity, so nothing depends on telling two hues apart. Separately, in descriptions green marks what genuinely works, amber an approximation to read with care, and pink a real limitation.
- Absolute radiometry — Optical Bench The film stopped being a normalized curve. A ray-tube (Jacobian) estimator gives irradiance in real W/m² per unit depth, with per-ray Fresnel losses; the Photo strip is scene-referred with exposure in stops, and a hover probe reads E at any height. The old energy "audit %" is gone — it was an algebraic identity that read 100% even on an all-zero film — replaced by unresolved %, which can actually fail.
- The freeform lens actually works — Optical Bench Sculpt the glass instead of moving prisms. The old solve was one-shot and paraxial; it now traces and corrects against the real Snell path and can sculpt both faces (1/2 toggle), which beats moving the prisms on 5 of 6 targets and is deterministic. (Its placed readout was briefly retired and is back — see the scoring entry below.) A prism upstream still costs the lens a lot.
- Honest framing — everywhere The rail is ranked, strongest first, with the Optical Bench moved last behind a divider and marked work in progress. Every mode carries a one-line note on what it does and how far it honestly goes. String Art's primitives are tiered Solved / Simulated / Patterned. This overlay became a guided tour with a "what's weaker, and why" section.
- The showpieces demo themselves — Caustic & Mechanics Inverse Caustic solves on entry instead of opening on a flat sheet of glass, and opens on a cross rather than a bright dot (a dot is what any lens does). In Mechanics the first Solve per scene fires itself so the predicted ghost path visibly pays off — with an Auto-demo On/Off toggle if you'd rather it didn't.
- The match % now shows its own baseline — Caustic & Optical Bench Correlation has no meaningful zero: a blank screen with no glass at all already scores 28–53% on the four presets at the default setup (up to 56% at other resolutions and light sources), and far more against a target that covers most of the frame — 96% on a mostly-white one. (Not a brightness effect: the score is exactly unchanged if you scale the target, since it is a cosine. It is coverage — the closer the target is to a uniform wash, the better a bare screen already matches it.) The readout now prints that blank baseline beside the match, plus skill — how far this surface gets from blank to exact. The Optical Bench now prints the same three, against the same lamp with every prism, mirror and lens deleted, and keeps placed beside it. Retiring placed was itself withdrawn — the evidence for it scored a fixed default-lamp baseline rather than the live-lamp one that ships, and against the real metric the two disagree on 22% of comparisons, not 3%. A previous attempt at this was withdrawn too: the mean-subtracted shape number shipped here for one commit and was contrast-blind, scoring a barely-visible caustic 94% against a real solve's 79%.
Start here
- Inverse Mechanics — the deepest one. Search for a shove that lands a rigid body on a target pose through contact: bounces, gates, catapults, a ball knocked into a slot. Each candidate is scored by a full forward rollout in a C++/WASM kernel, so the shot you fire reproduces the predicted ghost path exactly. Try Catapult or Twin Plinko — scenes where the answer has to route through a mechanism, not just fly.
- Inverse Caustic — the prettiest one, and a genuinely hard solve. Sculpt a glass surface so refracted light forms a target image: a 2-D optimal-transport problem on a height field, displayed in 3-D using a paraxial thin-element model. The part worth noticing is the Diffuse source — instead of pretending the blur isn't there, the solver designs through it, which wins 14 of the 16 cells in its gate. It is not a clean sweep: on the smoothest target at the lowest blur it ties or slightly loses to a naive solve, because there is barely any blur to fight.
Then
Inverse String Art — reconstruct an image from straight thread between pegs. The idea worth the visit is the hologram: peg heights are invisible head-on, which makes them a free second channel, so one sculpture shows a different picture to a tilted eye. At the default ring-then-star pair that reads 89% front, 61% from the tilted eye — the second image is real but clearly the weaker of the two, since it is encoded in whatever freedom the first one left behind. Its primitives are ranked in the panel: Line art, Stipple and TSP search for an arrangement; Hilbert, Spiral and Web do not, and are labelled as such.
What's weaker, and why
Ranking things honestly is more useful than claiming they're all equally good, so:
- The Optical Bench is the weakest mode, and it's a degrees-of-freedom problem, not a bug. Its forward model is the most rigorous physics in the app — real Snell refraction, total internal reflection, Fresnel transmission, and screen irradiance in absolute W/m² rather than a normalized curve. But the inverse side asks a search to shape a 320-bin light pattern with nine free parameters — the lamp's height, aim and spread, plus a position and rotation for each of the two prisms. Nine knobs for a three-hundred-dimensional target: there simply isn't enough freedom in the setup, so the solved pattern lands "near enough" and stops. The fix is more freedom, which is what the freeform lens gives you — add one, then Solve shape sculpts a whole surface instead of moving blocks, beating the prism search on five of six targets and doing it deterministically rather than as a stochastic search you press repeatedly.
- Three String Art primitives don't solve anything. Hilbert subdivides a space-filling curve more finely where the image wants ink, Spiral wobbles a groove by local tone, and Web is a static template. Those first two do adapt to the picture — but by a direct rule, not by searching for an arrangement that reconstructs it, which is what the modes above them do. They make a pleasant image and are grouped separately for that reason.
- Match percentages flatter, so both modes now show what nothing scores. Both score with the same raw correlation, and it has no meaningful zero: what a blank result scores is set by how much of the frame the target covers, not by whether anything worked. Measured — a caustic screen with no glass at all scores 28–53% on the presets and 96% against a mostly-white target; an optical bench with every element deleted, a flat flood carrying no pattern, still scored 93% under the old readout on the Wide target. Both now print that blank baseline beside the match, plus skill — how far the arrangement gets between them, so 0% means "no better than nothing there". On the bench that is unforgiving in the right way: against the Wide target an untouched bench already scores 97.5%, and a solved layout there lands at a median 50% skill over 12 solves, spread 0–76% — a range the old single number hid completely. (Those two don't chain arithmetically: the solver moves the lamp, so the baseline a solved layout is measured against is its own, not the 97.5%.) The bench keeps placed alongside, because on exactly that target skill cannot tell a layout that puts 96% of the light in the right place from one that puts 17% there. Three withdrawals got here. A mean-subtracted shape number shipped for one commit and was blind to contrast, scoring a barely-visible caustic 94% where the real solve scored 79%; and the bench's placed was retired and then restored, because the measurement that retired it scored a baseline the app does not use. Neither is the search objective.
- Two of the ~55 mechanics scenes don't reliably solve, for two different reasons. In the knock-an-object-off-a-ledge scene the search discovers that doing almost nothing scores acceptably and settles there. In the two-body collide-and-land scene the collision happens but the second body never comes to rest, so it scores 0 on settling. Both are left in and labelled best-effort rather than quietly removed.
Why the mechanics solver runs in WASM, not on a GPU
- Each rollout is sequential. Contacts are resolved in order (sequential-impulse / Gauss–Seidel): every iteration reads the result of the last, and the bottom of a stack changes the contact above it. Step N needs step N−1 — there's nothing to parallelize inside a rollout.
- The work is branchy and data-dependent. Collision narrowphase, the number of contacts, and which bodies touch all change every step. A GPU runs thousands of lanes in lockstep doing the same instruction; here the lanes diverge (one body in contact, its neighbour not; different branches), so most lanes stall. That's the opposite of GPU-shaped work.
- Determinism is the point. The search rolls out hundreds of candidates from one exact saved snapshot (copy/restore), so they're all comparable — and the shot you fire reproduces the predicted ghost path bit-for-bit.
What "depends on each other"
A moving gate's phase decides whether the box threads it; a contact impulse propagates through the whole constraint graph; the final pose is the end of a chain of collisions, each one conditioning the next. Those dependencies are exactly why it's a stateful, CPU-shaped solve.
Honest caveat: the outer search — trying many candidate launches — is embarrassingly parallel and would map fine to a GPU. The defensible WASM win is the per-rollout solver and branchy narrowphase inside each candidate, not the fan-out of the search itself.