Kerr spin, camera aberration, image-order tinting, an infalling beacon, and a live light curve - #6
Merged
Merged
Conversation
A small 2D-canvas chart that plots the disc's feeding boost against time since the star came apart, on log-log axes, with the t^(-5/3) fallback law drawn through the recorded peak. Off by default, one folder in the panel, and it works on the touch layout. It was asked for on the expectation that the fallback law would emerge from the simulation. It does not, and the feature is built to show that rather than to imply it. A realistic-mode star at the shipped clock decays like t^(-7.3), and the fitted index tracks the Disruption speed slider (about -12.5 at compression 4, -3.3 at 30) while barely moving with the seed or the placement radius. That is the app's own machinery talking: the plotted series is the absorbed-debris rate smeared by DISC_TUNING.boostDecayTau, fed by debris that DEBRIS_TUNING drag circularizes on a fixed timescale and maxAge truncates. So the reference line is anchored at the peak and never fitted, the caption prints the fitted index next to the law's, and the measurement is pinned by fallbackLaw.test.ts, documented in part 11 of docs/THEORY.md and listed in its "What we cheat on" table. The recorder is a fixed-capacity ring with no DOM and no WebGL, so all of its maths is unit tested directly. The frame loop only gains one recordFeeding call per fixed tick (an add, a compare, two typed-array writes about once in 120 ticks); the chart repaints on its own 10 Hz timer behind a dirty check, so no chart work happens inside frame(). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every other object in this app falls all the way in. That hides the one thing about a black hole that nothing else demonstrates: the horizon is a statement about clocks, not a surface. A probe released from rest reddens, dims by nine decades and stalls just outside the shadow for us, while on its own clock it crosses in a finite 28 r_s/c and notices nothing. Both readings are shown side by side, because either one alone is a lie. The probe is the only moving object here on exact Schwarzschild motion rather than Paczynski-Wiita. PW has no coordinate-time divergence at all and its matter crosses r_s in finite time, so reusing sim/gravity.ts would have made the headline effect impossible to show. The integrated state is the horizon gap rather than the radius, because an exponential decay stored as a difference of two order-one numbers stops being physics and starts being rounding at 1e-16. Two things the plan for this did not survive contact with: The coordinate-time ODE has a square-root fixed point at the release radius, where dr/dt is exactly zero, so a midpoint scheme on the radius alone parks the probe at r0 forever. The local speed is advanced alongside it, which leaves zero at a finite rate and reproduces the analytic short-time law exactly on the first step. The redshift is stated as sqrt(1 - r_s/r) times the recession Doppler and a test holds it to that, but it cannot be evaluated that way: the local speed reaches 1.0 in float64 at a gap of 1e-17 and the Doppler factor then collapses to zero, taking the last decade of the fade with it. The algebraically identical f / (E + sqrt(E^2 - f)) is what runs. The probe is drawn in the overlay pass rather than inside the raymarch, so it is not truly lensed. It is placed at the apparent image radius instead, floored at b_crit, which keeps it hugging the photon ring rather than sinking into the shadow where the horizon mask would swallow it. Honest about that in beacon.ts and in the new THEORY.md section. stepWorld's trailing clock arguments became a named SimClocks object rather than growing a sixth positional number nobody could read at the call site. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Spin is the largest realism gap this app had: real holes turn, and every consequence of that is visible. Rather than bolt a gravitomagnetic term onto the existing deflection, the shader and the CPU integrator now solve the exact Kerr metric in Cartesian Kerr-Schild form as a Hamiltonian system on (x, p). Kerr-Schild is the coordinate system that survives the horizon and its Cartesian layout matches the march the app already had, so the disc-crossing test, the body intersection and the uniforms are untouched. Everything the brief asks to follow from the spin does follow from it: the outer horizon, the frame dragging that makes the shadow lopsided, the prograde ISCO moving in so the disc's inner edge moves with it, and the photon sphere splitting into prograde and retrograde radii. Nothing is faked. The closed forms live in physics/kerr.ts and are pinned against the shipped Schwarzschild constants at a = 0, against the known extremal limits at a/M = 1, and against the integrator's own turning points, which is what stops the two from drifting. Spin 0 keeps the shipped image bit for bit. The a = 0 expressions are branched rather than unified because the Kerr forms are analytically identical there and not identical in floating point, and the old frame is a promise. That branch carries a comment saying exactly this, and a test asserts spin 0 equals the no-spin call. Camera flights now aberrate. The boost is applied once per pixel to the initial ray direction, so the shadow, the ring, the disc and the sky move together; aberrating only the sky sample would slide the star field while the shadow stayed put. The speed is not the derivative of the camera path, because the flights run on a compressed clock that is superluminal in these units. Each move publishes the physical speed of the trajectory it stands for instead: the circular-orbit speed for the orbit, free fall from infinity for the pass and the plunge. Both are already derived in THEORY.md. At rest the transformation is the identity by construction rather than by a threshold. Image-order tinting is a diagnostic overlay, off by default, that classifies the disc by accumulated winding angle rather than by counting plane crossings: a straight unlensed ray from a camera above the plane crosses the plane once, so a crossing counter would call it a first-order image. Two corrections to what was planned. The plan quoted b_crit, the ISCO and the photon radius at a/M = 0.998 from a rougher evaluation; the tooltip and the docs now carry the values the shipped closed forms actually produce, asserted by a test so they cannot go stale. The plan also asserted a ZAMO speed of 0.5 at the 0.998 ISCO; 0.5 is the extremal limit and the value at 0.998 is 0.565, so the test takes the limit instead. Debris now reads its absorb and kill radii from the environment rather than multiplying art constants by r_s. That is a correctness fix, not polish: at high spin the old kill radius of 1.05 r_s sits outside a disc that reaches 0.62 r_s, so debris would vanish before it could be credited to the feed. Kerr is a one-hole solution, so a second hole forces the spin to 0 in three places that have to agree: the shader marches one center, the CPU integrator throws, and the slider disables itself with a note. THEORY.md owns the consequence, that a merger currently leaves a non-spinning remnant. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three implementations came back needs-work. The findings, and what they cost: A blocker in the beacon. `apparentImageRadius` returns an impact parameter, which is a distance in the sky plane, and main.ts placed the sprite that far along the probe's own direction in three dimensions. That only agrees when the probe happens to sit side on to the camera; once it swings behind the hole the image landed inside the shadow, the one place an escaping photon cannot come from. Placement now projects into the sky plane first, so the offset from the hole's centre is the impact parameter in every geometry, and the image is lifted toward the camera so the horizon mask cannot eat it. The test that should have caught it asserted the scalar against b_crit rather than measuring where the image lands, so it passed throughout. The replacement measures the sky plane offset from six directions including directly behind the hole, which is the case that was broken. The Kerr preset claimed an inner disc edge of 0.62 r_s while shipping spin 0.95, which puts it at 0.97. That is the ISCO for 0.998. Three doc comments in kerr.ts carried stale values that the shipped closed forms contradict, including the file a reader goes to for exactly those numbers. The light curve recorded forever. Nothing cleared `recording` after a flare, so the ring filled with post-flare zeros and then decimated the event itself to make room for them: the longer a scene sat idle, the coarser its record of the disruption became. Two claims were true only at one slider position. The light curve's decay index moves with the disruption clock, so the tooltip and the cheat sheet now give the range and point at the live fit the caption already prints. The "spin was set to 0" note fired while `settings.spin` kept the user's value, so the slider disagreed with the note; it now sets what it says it sets. Also: `setSpinAvailable(boolean)` was a boolean parameter in a public interface, which the project standard forbids, and the beacon preset inherited whatever spin was left over, demonstrating a Schwarzschild solution against a dragged horizon. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Five features, built by a fanned-out workflow of 11 agents (5 design, 3
implementation in isolated worktrees, 3 adversarial verification), then
integrated, corrected and re-verified here.
401 tests, up from 51. Typecheck clean, build clean, both new presets
driven in a real browser.
What landed
a/Min [0, 0.998]. The outer horizon, theasymmetric shadow, the prograde ISCO and the split photon radii all follow
from the spin rather than being faked, and
a = 0reproduces the shippedSchwarzschild image. At 0.95 the readout gives horizon 0.66 r_s, ISCO 0.97
r_s, photon rings 0.69 and 1.98 r_s, which match the closed forms by hand.
transformed out of the moving camera's rest frame before the march, with
the matching Doppler shift on what comes back. Exactly zero when the camera
is at rest, so ordinary orbiting is untouched.
how many times the ray wound around the hole.
with a readout contrasting the distant observer's clock against the
probe's own, which crosses in finite time.
t^(-5/3) law drawn beside it.
The most valuable thing the workflow produced was a negative result
The light curve was commissioned to "show the t^(-5/3) law emerging from the
simulation". It does not, and the agent measured that rather than shipping
the claim: the recorded decay is far steeper, and its index tracks the
disruption clock (about -12 at the slowest setting, -3 at the fastest). The
cause is in our own machinery, not the debris: the plotted series is the
absorbed-particle rate smeared by the disc's boost decay time, fed by debris
that a drag term circularises on a fixed timescale instead of letting it
return on its own orbits.
So the reference line is anchored and never fitted, the caption prints the
live fit next to the law, and both the tooltip and THEORY.md say plainly
that the app does not reproduce it and why. Making the simulation actually
produce the law is a real follow-up, and a good one.
What verification caught after the fact
All three implementations reported passing tests and a clean build, and all
three still came back
needs-work. The headline was a blocker: the beacon'sfrozen image was placed at an impact parameter along a three-dimensional
direction, so whenever the probe swung behind the hole its image was drawn
inside the shadow. The test meant to catch that compared a scalar against
b_crit instead of measuring where the image landed, so it passed the whole
time. Both are fixed, and the replacement test measures the sky plane offset
from six directions.
Also fixed: a preset quoting an inner-edge radius for a spin it does not
ship, three stale constants in
kerr.tsdoc comments, a recorder that neverstopped and slowly ate its own resolution, a transient note that claimed to
set a value it never set, and a boolean parameter in a public interface.
🤖 Generated with Claude Code