❍ Schwarzschild space-time
🎯 Visualization of space-time curvature
- orange → real motion: geodesic flow in 4D
- cyan → Geodesic-projection on embedded equatorial surface
- red → flat motion (Newtonian/Euclidian projected trail)
🧠 Original idea and code by M. Ryston (Department of Physics Education)
📌 Described in Interactive animations as a tool in teaching general relativity […]
📌 See also Spacetime Embedding Diagrams for Black Holes
👉 The closer to the sun, the greater the difference between red and orange!
🐍 A 3D VPython demo is available as well, see schwarzschild_space_time.py
Live experiment
Section titled “Live experiment”Theoretical background
Section titled “Theoretical background”Flamm’s paraboloid
Section titled “Flamm’s paraboloid”The Schwarzschild metric describes a gravitational field of a non-rotating spherical mass (and without electric charge), see Wikipedia:
where
and is Newton’s gravitational constant, the speed of light and is the mass of the non-rotating spherical object.
When we assume the time to be constant (), we get:
Now, according to Ryston’s article:
In order to visualize the curvature in the 𝑟 direction, we embed this surface into the three-dimensional Cartesian space (where 𝑟 and 𝜑 are identical to polar coordinates and the third, vertical Cartesian coordinate 𝑧 is used to visualize the actual curvature – see figure 1 below). As a result, we get an equation for the 𝑧 coordinate as a function of 𝑟: Of course, this equation 𝑟 and 𝑧 are in meters, which is not very convenient for visualizing large regions of space. For this reason, geometricized units where 𝑐 = 𝐺 = 1 are often used. Then we get the simpler form:

Figure 1: Flamm’s paraboloid: the exterior t=const equatorial plane of a Schwarzschild Black Hole — Wikipedia
This is the quintessential formula that is used in this visualization:
class SchwarzschildSurfaceDefinition extends SurfaceDefinition { static zAsFunctionOf = (r, M) => Math.sqrt(Math.max(0, 8 * M * r - 16 * M * M));
// ...
sample(u, v, target) { const eps = 0.01; const r = (this.rMin + eps) + u * (this.rMax - (this.rMin + eps)); const phi = v * 2 * Math.PI;
target.set( r * Math.cos(phi), SchwarzschildSurfaceDefinition.zAsFunctionOf(r, this.M), r * Math.sin(phi) ); }}Circular orbit conditions
Section titled “Circular orbit conditions”A real circular orbit implies:
So:
- No radiale drift
- centripetal balance exact
We only need one equation: radiale acceleration = 0
The following equation is used:
For a circular orbit we have , so all terms with vanish:
Factor :
In order to obtain , we note that in Schwarzschild we have the normalization:
Substitution of gives us:
Pulling out of the brackets we get:
The solution for is therefore:
As a consequence, for we obtain:
Important result
Section titled “Important result”A stable circular orbit only exists if !
For:
- → photon sphere (instable light orbit)
- → no stable circular orbit possible
In the code
Section titled “In the code”const tDot = 1 / Math.sqrt(1 - 3 * sun.mass / r);const rDot = 0;const phiDot = Math.sqrt(sun.mass) / (r ** 1.5 * Math.sqrt(1 - 3 * sun.mass / r));
