Drag to orbit, wheel / pinch to change distance, tap the image to trace one ray. Lengths are in Schwarzschild radii (rs = 2GM/c²), so the mass drops out.
The science
What is different here. This is not a picture of a black hole, it is a ray tracer: for every screen pixel the fragment shader integrates a light ray backwards from the camera through curved Schwarzschild spacetime until it falls into the horizon or escapes to the sky texture, crossing the thin disk on the way.
Light bending. A photon in the Schwarzschild metric obeys, with u = 1/r in its orbital plane,
d²u/dφ² + u = (3/2) rs u²
The shader integrates the equivalent Cartesian form x'' = −(3/2) rs h² x / r⁵, where h = |x × x'| is the impact parameter b (conserved) and |x'|² = 1 + rsh²/r³, with classical RK4 and a step proportional to r. The camera's local viewing angle ψ is mapped to the initial state exactly: ṙ = cosψ, tangential speed sinψ/√(1−rs/R).
Landmarks. Photon sphere r = 1.5 rs (unstable circular light orbit); shadow radius (critical impact parameter) bc = 3√3 GM/c² = 2.598 rs, seen by a static observer at R under the angle sinα = (bc/R)√(1−rs/R); innermost stable circular orbit (ISCO) 6GM/c² = 3 rs. Far from the hole the deflection is α ≈ 4GM/(bc²) = 2rs/b (Eddington 1919), with the next term 15πrs²/(16b²). A source directly behind the lens gives an Einstein ring of angular radius θE ≈ √(2rs/R) for a distant source.
Photon ring and image orders. Rays with b slightly above bc circle the hole before escaping. They draw an infinite nested sequence of ever thinner images of the whole sky and of the disk: the primary image, the secondary image (light from the far side of the disk bent over and under the hole, hugging the shadow) and the higher-order photon ring, each about eπ ≈ 23 times thinner. "Colour by image order" paints them differently. The Event Horizon Telescope images of M87* and Sgr A* resolve the shadow and the brightest ring, consistent with this geometry.
Disk. A geometrically thin, opaque-ish, optically thick disk in the equatorial plane, between the ISCO (3 rs) and 14 rs, with matter on circular geodesics, angular velocity Ω = √(GM/r³). Temperature uses the Shakura–Sunyaev profile T ∝ r−3/4 (1 − √(rin/r))1/4 (Newtonian form: no GR corrections to the emitted flux). Redshift factor for a static observer at R, emitter at r with photon angular momentum λ = Ly/E:
g = √(1 − 3rs/2r) / [ √(1 − rs/R) (1 − Ωλ) ]
This is exact for circular geodesic motion in Schwarzschild (gravitational shift, transverse Doppler and line-of-sight Doppler together). The observed colour temperature is gT and the bolometric intensity scales as g4, which is why the approaching side is brighter and bluer. The slider scales the exponent of g (0 = no Doppler/redshift effect). To keep the display readable, brightness uses (Tobs/T)2.4 instead of the true fourth power, so the Doppler asymmetry is visually weaker than physical; colours are an approximate blackbody fit, and the disk texture (a Blender-baked turbulence map) is decoration, not a magnetohydrodynamic simulation.
Clocks. A static observer's clock runs at dτ/dt = √(1 − rs/R) relative to a distant one; an observer in a circular geodesic orbit (R > 1.5 rs) at √(1 − 3rs/2R). Below the photon sphere there is no circular free-fall orbit.
Simplifications, stated honestly. Non-rotating (Schwarzschild) hole: a spinning (Kerr) hole shifts and flattens the shadow and is not modelled. The camera is a static observer (no aberration from its own motion). The disk is infinitely thin and has no atmosphere, scattering, corona or jets; light-travel time between image orders is ignored (the pattern is frozen when seen via different orders); starlight is not blue-shifted. Rays that exhaust the step budget (a thin sliver right at the shadow edge, wider on Low) are drawn black. Tone mapping and bloom are artistic.
How the checks work. (1) Bisecting capture/escape with the same RK4 integrator in double precision gives bc; (2) the exact deflection is evaluated independently by quadrature of Δφ = 2∫du/√(1/b² − u² + rsu³); (3) the Einstein ring is the root of θ = α(b) for a source at infinity behind the hole; (4) the shadow edge produced by the actual GPU shader is measured on a pixel row and converted back to b. Sources: Schwarzschild 1916; Darwin 1959; Luminet 1979 (first simulated image of a disk around a black hole); James et al. 2015 (the Interstellar renderer, DNGR); Event Horizon Telescope Collaboration 2019 and 2022; Gralla, Lupsasca & Marrone 2020 (photon ring). Not affiliated with any film or observatory.