Black Hole, in your browser
A real-time raymarcher that integrates the Schwarzschild geodesic ODE per pixel — straight through a WebGPU fragment shader. No textures baked, no precomputed light maps. Drag to orbit, scroll to zoom.
How it works
Light rays follow null geodesics around the black hole. In Schwarzschild coordinates, the equation for the inverse radius u = 1/r as a function of orbital angle φ reduces to a clean second-order ODE:
d²u/dφ² = (3/2) · r_s · u² − uFor each pixel, the fragment shader sets up an orbital plane from the camera position and ray direction, then RK4-integrates that ODE with an adaptive step size — coarser when the ray is far from the hole, denser near the photon sphere where bending is steep.
Whenever a step crosses the equatorial plane inside the disk band, the integrator switches to volumetric marching: sampling a tiled noise texture warped by Keplerian angular velocity ω ∝ r^(-3/2), with a Gaussian vertical profile and a radial falloff for the disk density. Beer's law accumulates opacity along the way.
Rays that cross the event horizon are swallowed. Rays that escape the universe radius (60 r_s) hit an HDR star map (lat/long sampled), giving the characteristic Einstein-ring distortion of the background as light bends around the hole.
WebGPU pipeline
The whole thing runs as a single fullscreen draw — a 3-vertex triangle covering the clip space, with all the heavy work in the fragment shader. Uniforms hold the camera frame in viewport form (pixel00 origin + ΔW, ΔH per-pixel basis vectors), so the shader reconstructs one ray per fragment without ever touching a matrix.
- Shader: WGSL, ~380 lines (source on github)
- Bindings: uniform buffer, stars texture, noise texture, two samplers
- Camera: orbit camera in TypeScript with latitude/longitude/distance
Why I built this
I've been working with CUDA kernels professionally for years but had never written a raymarcher from first principles. This project was an excuse to: (1) pick up WebGPU and WGSL properly, (2) work through general relativity to the point I could actually re-derive a ray-tracing ODE, and (3) build something that looks great as a portfolio piece — physics-correct enough to satisfy me, fast enough to ship as a webpage.