How would Einstein have felt to fly through his 3-Sphere with these miraculous machines we have made

The 3-sphere is the shape of space in Einstein's first universe, 1917. This is the answer to a question about dimension, and a flight through the thing the question said could not be flown through.

The question, as it was put

Asked by the project's author, who has been asking it since a book on 3-D engine design at seventeen.

A point shows all its detail to the eye at once. So does a line, and so does a map: pan and zoom as far as you like, everything is there on the plate. But a perspective frustum on a three-dimensional object reveals only a fragment. Look at the side of a mug and only that side is seen; the far side could carry any pattern at all. From below it might be solid; from above the cavity appears and the sides recede. The only way to know the mug is to turn it in time and remember, and even that is not the whole of it: the mug is atoms, and the space between the atoms. We call ourselves three-dimensional creatures in a three-dimensional world, and we are, but the eye is stolidly a two-dimensional camera. A true three-dimensional viewer would see all of a solid at once.

So the fourth dimension is hard to think about, because each slice we can see holds so much less of it. The rendered hypercube falls into this trap: the animation is cute and it is not the object. And the hypersphere, the natural extension of point, circle, sphere, the shape it is speculated our universe really is, the one that wears π the way every circle does, cannot be pictured at all. What is a sphere pushed into another addressable dimension, twice? There seems not even to be a name for it. Whichever way the system is traversed, rendering four dimensions to the two the eye receives loses all coherence of what the object represents. Is it simply unknowable? Or is it?

Three answers, because it is three questions

Seeing it all at once; knowing it; flying through it. One is impossible, and not for the reason supposed; one is finished; one is a render.

Every creature sees one dimension fewer than it lives in

That is the wall, and it does not belong to the fourth dimension. A Flatlander, with a one-dimensional retina, sees a square as a line segment and can never see its interior; we look down on Flatland and see the whole square, inside and out, at once. A four-dimensional creature, with a three-dimensional retina, sees the mug the way we see a drawing on paper: both sides, the cavity, the base, every point of the glaze, all at once and without turning it. So a true three-dimensional viewer is not impossible in principle. It is not us. And that creature cannot see its own world all at once either, and is perplexed by the 4-sphere in exactly the way we are by the 3-sphere. The wall is not in front of the hypersphere; it is the shape of every eye, met at precisely the rung where it should be.

Two limits are tangled in the mug, and only one is about dimension

The atoms are a resolution problem: a four-dimensional creature would see the whole mug, at its own resolution, and be as ignorant of the lattice as we are. Set that aside and the mug's geometry is fully recoverable by exactly the method the question proposes. Turn it and remember: projections from every angle are complete information, which is the theorem a CT scanner runs on. The question was righter than it thought.

It has a name, and the construction in the question was correct

Mathematicians call it the 3-sphere, counting the dimension of the surface, because the surface is where one would live. Two things about the sequence: it does not begin with a point, since the zeroth sphere is a pair of points, the boundary of a segment; and the bowl construction in the question needs one more step, not a new idea. Two discs glued rim to rim make a sphere. Two solid balls glued skin to skin, every point of one surface to the matching point of the other, make the 3-sphere. The gluing cannot be performed in our space without crushing, but the object it defines is exact, and living in it is simple to say: walk out of ball A through its skin and arrive in ball B at the matching point; walk on, leave B, and arrive back in A at the antipode of the point of departure; walk on and arrive home, never having turned. Dante described precisely this around 1320: nine spheres of the heavens about the Earth, nine circles of angels about the point of light, the two systems sharing their outermost shell. The construction is seven centuries old, and it was made without the mathematics.

The ouroboros is not a metaphor; it is the definition

A circle is a line whose two ends are joined at a single point at infinity. A sphere is the plane plus one point, which is why a flat map of the Earth loses exactly the pole. The 3-sphere is ordinary space plus one point: go far enough in any direction whatever and arrive at the same place. So a complete map of it is already in hand. It is the space we are sitting in, read with the understanding that all of infinity is one point. Angles on that map are right, sizes far out are wrong, as Greenland is on Mercator, and it loses exactly one point. The question asked for an infinite map that could be panned and zoomed. That is it.

π is the signature of Pythagorean distance

A sphere is the set of points at one distance; distance is a quadratic; the integral of a quadratic exponential is √π. A cube is the set of points within one distance measured by the largest coordinate instead, and there is no π anywhere in it. That is why π is all over physics: it appears wherever distance is quadratic, and the interval of relativity is quadratic too, so the question was right about the root. The chart carries the consequence. The volume of the unit ball rises to a peak in five dimensions and then falls to zero for ever; in a thousand dimensions it is 10−886, and almost all of that lies within a hair of the skin. Every number on the chart is exact, and not one past three can be pictured. That is the whole of the argument that knowing and seeing are different things, and that knowing is the one we have.

V<sub>n</sub> = &pi;<sup>n/2</sup> / &Gamma;(n/2 + 1): the volume of the unit n-ball, orange, and the surface of the unit (n&minus;1)-sphere, blue. The volume peaks at n = 5 (5.264), the surface at n = 7 (33.07).
nball-volume-by-dimension.png. Vn = πn/2 / Γ(n/2 + 1): the volume of the unit n-ball, orange, and the surface of the unit (n−1)-sphere, blue. The volume peaks at n = 5 (5.264), the surface at n = 7 (33.07).

Inside: the flight

The 3-sphere's surface is three-dimensional. A creature living in it lives in a three-dimensional world with one rule changed: straight lines come back.

That is what makes it renderable. A camera inside it is an ordinary camera; the renderer is a game engine with a single line altered, the ray. In four coordinates a straight line from p in the direction d is p cos t + d sin t rather than p + d t, and after t = 2π it is home. A ball of angular radius ρ about a centre c is entered where ⟨ray, c⟩ first reaches cos ρ, which along a great circle is R cos(t − φ) = cos ρ with R and φ from two dot products: a closed form, no marching. The objects are the 120 vertices of the 600-cell, the four-dimensional icosahedron, each an 8-degree ball; the camera flies once round a great circle chosen to pass no closer than 17.3 degrees to any of them; a headlamp at the eye is the only light. Three things are true in there, and the film shows all three.

There is no horizon and no sky. Every direction ends on something, at most 2π away, and a ray that misses every ball comes back to where it started. So the farthest thing in every direction is the back of one's own head, and the observer is drawn as a 3-degree ball just behind the eye: that is the grey grid behind everything. It is lit by the observer's own lamp after a trip round, at ninety times the brightness of anything a quarter of the way out, because light from a point reconverges not only at the antipode but at its source. The first render had a white sky, which is the one thing a closed universe cannot have; it was this, and the head was darkened and gridded so that it reads as what it is.

The antipode fills the view. Every path from a point reconverges at the point opposite, so an object there is enormous and, under a headlamp whose irradiance goes as 1/sin²d, brilliantly lit. And an object a quarter of the way round and one three-quarters round look identical in size and brightness in a still. Only motion separates them: the near ones sweep outward past the camera, the antipodal ones drift inward toward the centre, because the parallax rate changes sign at the equator. Watch a ball straight ahead: if it grows and sweeps out, it is near; if it grows and drifts in, it is at the far side of the universe.

hypersphere-flight-24fps.mp4 · 1280×720, 480 frames at 24 fps, 17 MB. One full circuit, twenty seconds, home from behind and never having turned. Reinhard exposure, headlamp only, one bounce.
Frame 0: standing still. The grid's warp toward the centre is the antipode.
hypersphere-inside-frame0.png. Frame 0: standing still. The grid's warp toward the centre is the antipode.
Frame 240: halfway round.
hypersphere-inside-frame240.png. Frame 240: halfway round.

Stated limits. Direct lighting only, no interreflection, and a headlamp at the eye is the only source; the 600-cell is a choice of furniture, not physics; the colours label the balls by an angle about one fixed plane and mean nothing else; the exposure is a fixed Reinhard curve, noted in the caption. What is exact: the geodesics, the intersections, the 1/sin²d law and the fact that every ray terminates, which the renderer asserts on every frame.

Einstein's universe, honestly

The title asks a question that cannot be answered. Here is what can be.

Einstein's first cosmology, the 1917 paper that founded the subject, took space to be a 3-sphere: finite, so that the field equations would not need conditions at an infinity he distrusted, and without an edge, because a sphere has none. When Hubble's expansion made the static model untenable he gave up the stillness before the shape: his 1931 model was an expanding and recontracting 3-sphere, and only the 1932 model with de Sitter went flat. He held both, which is exactly where the measurements now stand. Space is flat to within a fraction of a percent. If it is a 3-sphere after all, its radius of curvature is at least some two hundred billion light years against an observable radius of forty-six, so we would be seeing at most about seven percent of the way round; and the ouroboros has been looked for directly, since a space that closed within sight would show the same circle of the microwave sky twice, from two directions. WMAP and Planck were searched for matched circles and none were found. So speculated, in the question, is the right word.

How he would have felt, flying through it, is not knowable, and the page will not pretend. What is knowable is this. The space in the film is the spatial section of his 1917 universe with the matter taken out and the light traced by the rule he wrote down, that light follows geodesics. Its two strange properties, that every path from a point refocuses at the antipode and that an observer's own light returns to them, are consequences he could compute and never see; the remark that in his universe one would see the back of one's own head is nearly as old as the model. He is also the physicist who at sixteen tried to picture riding beside a beam of light, and built a theory out of what the picture would not let him have. A machine that rides the beam round his universe and shows what arrives is, at the least, his kind of instrument. The one honest speculation is that he would have wanted to check the film against the calculation, and that the check passes: the head is lit at 1/sin²(6°), the antipodal balls subtend arcsin(sin 8° / sin d), and the flight arrives home.

The scripts

numpy only; ffmpeg for the encodes; the caption font is hawkcolor.py from the black-hole page, which sits beside them.

glome.py (12 kB) · The 3-sphere from inside: closed-form great-circle rays against balls at the 600-cell's vertices, a headlamp, the observer's own head; one still or the whole flight. download
#!/usr/bin/env python3
"""Fly through the 3-sphere: an honest render from INSIDE (2026-09-10).

The 3-sphere is the set of unit vectors in R^4. Its 'surface' is three-dimensional, so a creature living in
it lives in a 3-D world whose straight lines are great circles. That is what makes it renderable at all:
the camera is an ordinary 3-D camera, and only the rays are different.

A ray from p in unit tangent direction d (both in R^4, d orthogonal to p) is
    g(t) = p cos t + d sin t,   t in [0, 2 pi),
and after 2 pi it is back where it started. A ball of angular radius rho about centre c is the set
{q : <q,c> >= cos rho}. Along the ray,
    <g(t), c> = A cos t + B sin t = R cos(t - phi),   A = <p,c>, B = <d,c>, R = hypot(A,B), phi = atan2(B,A)
so the ray ENTERS the ball at t = phi - arccos(cos rho / R) (mod 2 pi) when R >= cos rho, and never
otherwise. Closed form; no marching. Every ray that misses every ball comes back to the observer at
t = 2 pi: there is no sky and no infinity.

Three things are true from inside, and the film shows all three:
  1. no horizon -- every direction ends on something, at most 2 pi away;
  2. antipodal focusing -- all geodesics from a point reconverge at its antipode, so an object near the
     antipode looks enormous, and under a headlamp is lit with irradiance 1/sin^2(d), which peaks there;
  3. the backdrop of every view is the back of your own head, lit by your own lamp after a trip round.
The objects are the 120 vertices of the 600-cell (the 4-D icosahedron), each a small ball. The camera
flies once round a great circle chosen to keep clear of them. Direct lighting only, one bounce, a headlamp
at the eye; exposure is a fixed Reinhard curve (noted in the caption, as for the Hawking renders).

  glome.py still <out.png> [frame_index]        one frame
  glome.py film  <out.mp4> [frames]             the flight, 24 fps, one full circuit
  glome.py path                                 report the chosen circle and its clearance
"""
import itertools
import os
import pathlib
import subprocess
import sys
import time

import numpy as np

sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))   # hawkcolor.py is beside this file
from hawkcolor import draw_text, text_width  # the 5x7 caption font from the Hawking films

FF = os.environ.get("FFMPEG", "ffmpeg")
# run from a shell with the project's ffmpeg build and mingw64/bin on PATH, as every tool in tests/ does
W, H = 1280, 720
FOV_DEG = 100.0
RHO = np.radians(8.0)          # each vertex ball, angular radius
RHO_SELF = np.radians(3.0)     # the observer's own head
FPS = 24
TWO_PI = 2 * np.pi


def cell600():
    """The 120 unit vectors of the 600-cell."""
    phi = (1 + 5 ** 0.5) / 2
    V = []
    for i in range(4):
        for s in (1.0, -1.0):
            v = [0.0] * 4
            v[i] = s
            V.append(v)
    for signs in itertools.product((0.5, -0.5), repeat=4):
        V.append(list(signs))
    base = [phi / 2, 0.5, 1 / (2 * phi)]
    for perm in itertools.permutations(range(4)):
        inv = sum(1 for i in range(4) for j in range(i + 1, 4) if perm[i] > perm[j])
        if inv % 2:
            continue
        for signs in itertools.product((1.0, -1.0), repeat=3):
            vals = [signs[0] * base[0], signs[1] * base[1], signs[2] * base[2], 0.0]
            v = [0.0] * 4
            for k in range(4):
                v[perm[k]] = vals[k]
            V.append(v)
    V = np.array(V)
    assert V.shape == (120, 4), V.shape
    assert np.allclose(np.linalg.norm(V, axis=1), 1.0)
    assert len({tuple(np.round(v, 9)) for v in V}) == 120, "duplicate vertices"
    d = np.degrees(np.arccos(np.clip(V @ V.T, -1, 1)))
    np.fill_diagonal(d, 999)
    assert abs(d.min() - 36.0) < 1e-6, d.min()   # the 600-cell's edge is 36 degrees
    return V


def flight_circle(C, seed=7, trials=40000):
    """A great circle (an orthonormal pair a, b) whose closest approach to any vertex is as large as
    possible, so the camera never enters a ball. Returns a, b and the clearance in radians."""
    rng = np.random.default_rng(seed)
    best = (-1.0, None, None)
    for _ in range(trials):
        a = rng.normal(size=4)
        a /= np.linalg.norm(a)
        b = rng.normal(size=4)
        b -= a * (a @ b)
        b /= np.linalg.norm(b)
        clearance = np.arccos(np.clip(np.hypot(C @ a, C @ b).max(), -1, 1))
        if clearance > best[0]:
            best = (clearance, a, b)
    return best[1], best[2], best[0]


def complete_frame(a, b):
    """Two unit vectors orthogonal to both a and b: the camera's right and up, parallel along the circle."""
    basis = [a, b]
    for e in np.eye(4):
        v = e.copy()
        for q in basis:
            v -= q * (q @ v)
        if np.linalg.norm(v) > 1e-6:
            basis.append(v / np.linalg.norm(v))
        if len(basis) == 4:
            break
    return basis[2], basis[3]


def hue_rgb(h):
    """h in [0,1) -> rgb in [0,1], a plain six-segment wheel."""
    h = (h % 1.0) * 6
    i = np.floor(h).astype(int)
    f = h - i
    out = np.zeros(h.shape + (3,))
    seg = [(1, "t", 0), ("q", 1, 0), (0, 1, "t"), (0, "q", 1), ("t", 0, 1), (1, 0, "q")]
    for k, comps in enumerate(seg):
        m = i == k
        for ch, cval in enumerate(comps):
            if cval == "t":
                out[m, ch] = f[m]
            elif cval == "q":
                out[m, ch] = 1 - f[m]
            else:
                out[m, ch] = cval
    return out


class Scene:
    def __init__(self):
        C = cell600()
        self.a, self.b, self.clearance = flight_circle(C)
        self.r, self.u = complete_frame(self.a, self.b)
        assert self.clearance > RHO + RHO_SELF + np.radians(1), "the flight would enter a ball"
        # colour each vertex by its angle around one fixed plane, so the structure reads as rings
        hue = (np.arctan2(C @ self.u, C @ self.r) / TWO_PI) % 1.0
        col = 0.15 + 0.85 * hue_rgb(hue)
        # the observer's own head is the last ball: dark slate, so the backdrop reads as a surface
        self.C = np.vstack([C, np.zeros((1, 4))])            # the head's centre is set per frame
        self.col = np.vstack([col, np.array([[0.30, 0.32, 0.36]])])
        self.rho = np.concatenate([np.full(120, RHO), [RHO_SELF]])
        self.cosr = np.cos(self.rho).astype(np.float32)
        f = (W / 2) / np.tan(np.radians(FOV_DEG) / 2)
        xs = (np.arange(W) - (W - 1) / 2) / f
        ys = -(np.arange(H) - (H - 1) / 2) / f
        self.X, self.Y = np.meshgrid(xs, ys)

    def camera(self, s):
        p = np.cos(s) * self.a + np.sin(s) * self.b
        fwd = -np.sin(s) * self.a + np.cos(s) * self.b
        return p, fwd

    def render(self, s, exposure=0.75):
        p, fwd = self.camera(s)
        C = self.C.copy()
        # the head sits just behind the eye, so the eye is on its front surface and the lamp is outside it
        C[-1] = p * np.cos(RHO_SELF) - fwd * np.sin(RHO_SELF)
        D = (fwd[None, None, :] + self.X[..., None] * self.r[None, None, :]
             + self.Y[..., None] * self.u[None, None, :])
        D /= np.linalg.norm(D, axis=-1, keepdims=True)
        D = D.astype(np.float32)
        tbest = np.full((H, W), np.inf, np.float32)
        idx = np.full((H, W), -1, np.int32)
        A_all = (C @ p).astype(np.float32)
        Cf = C.astype(np.float32)
        tb_flat, idx_flat = tbest.ravel(), idx.ravel()       # views: writes land in tbest and idx
        for k in range(len(C)):
            B = D @ Cf[k]
            R = np.hypot(A_all[k], B)
            ii = np.flatnonzero(R >= self.cosr[k])           # only the pixels whose ray meets this ball
            if ii.size == 0:
                continue
            Bf, Rf = B.ravel()[ii], R.ravel()[ii]
            t = np.arctan2(Bf, A_all[k]) - np.arccos(np.clip(self.cosr[k] / Rf, -1, 1))
            t = np.mod(t, TWO_PI).astype(np.float32)
            better = t < tb_flat[ii]
            jj = ii[better]
            tb_flat[jj] = t[better]
            idx_flat[jj] = k
        assert (idx >= 0).all(), "a ray escaped a closed universe"
        # shading at the hit: q on the ball's surface, the ray direction there, the inward normal
        t = tbest.astype(np.float64)
        ct, st = np.cos(t)[..., None], np.sin(t)[..., None]
        Dd = D.astype(np.float64)
        q = p[None, None, :] * ct + Dd * st
        g = -p[None, None, :] * st + Dd * ct
        c = C[idx]
        n_in = c - np.sum(c * q, axis=-1, keepdims=True) * q
        n_in /= np.maximum(np.linalg.norm(n_in, axis=-1, keepdims=True), 1e-9)
        lam = np.clip(np.sum(g * n_in, axis=-1), 0, 1)
        irr = 1.0 / np.maximum(np.sin(t) ** 2, 1e-4)        # a headlamp's irradiance on the 3-sphere
        col = self.col[idx]
        # the back of your own head: dark (albedo a few percent, it is hair) with a faint grid so it reads
        # as a surface -- because your own lamp lights it at 1/sin^2(6 deg), ninety times anything a
        # quarter of the way round, and a pale head would render as a white sky
        head = idx == len(C) - 1
        if head.any():
            qr = np.sum(q[head] * self.r, axis=-1)
            qu = np.sum(q[head] * self.u, axis=-1)
            grid = (np.floor(qr * 300) + np.floor(qu * 300)) % 2
            col[head] = (0.018 + 0.022 * grid)[:, None] * np.array([[0.85, 0.9, 1.0]])
        v = exposure * (irr * lam)[..., None] * col + 0.05 * col
        img = v / (1 + v)                                   # Reinhard, per channel: hot things go white
        return (np.clip(img, 0, 1) * 255).astype(np.uint8), tbest

    def caption(self, img, s):
        frac = (s / TWO_PI) % 1.0
        lines = [f"inside the 3-sphere   {frac:5.1%} of the way round the universe",
                 "straight ahead is the antipode; behind everything is the back of your own head"]
        y = H - 14 - 24 * len(lines)
        for ln in lines:
            draw_text(img, 16, y, ln, scale=2, color=(215, 215, 215))
            y += 24
        note = "Reinhard exposure, headlamp only"
        draw_text(img, W - 16 - text_width(note, 2), H - 26, note, scale=2, color=(150, 150, 150))
        return img


def write_png(path, img):
    subprocess.run([FF, "-v", "error", "-y", "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", f"{W}x{H}",
                    "-i", "-", "-frames:v", "1", "-update", "1", path], input=img.tobytes(), check=True)


if __name__ == "__main__":
    mode = sys.argv[1]
    sc = Scene()
    if mode == "path":
        print(f"clearance {np.degrees(sc.clearance):.2f} deg; balls {np.degrees(RHO):.0f} deg; "
              f"head {np.degrees(RHO_SELF):.0f} deg")
        sys.exit(0)
    if mode == "still":
        frames = 480
        k = int(sys.argv[3]) if len(sys.argv) > 3 else 0
        s = TWO_PI * k / frames
        t0 = time.time()
        img, tb = sc.render(s)
        print(f"frame {k}: {time.time() - t0:.1f} s; nearest hit {np.degrees(tb.min()):.1f} deg, "
              f"farthest {np.degrees(tb.max()):.1f} deg")
        write_png(sys.argv[2], sc.caption(img, s))
        sys.exit(0)
    if mode == "film":
        frames = int(sys.argv[3]) if len(sys.argv) > 3 else 480
        out = sys.argv[2]
        # frames go to disk as PNG and are encoded from there: a 2.7 MB write to a Windows pipe fails
        import os
        fdir = out + ".frames"
        os.makedirs(fdir, exist_ok=True)
        t0 = time.time()
        for k in range(frames):
            s = TWO_PI * k / frames
            img, _ = sc.render(s)
            write_png(os.path.join(fdir, f"f{k:04d}.png"), sc.caption(img, s))
            if k % 24 == 0:
                print(f"  frame {k}/{frames}  {time.time() - t0:.0f} s", flush=True)
        n = len([f for f in os.listdir(fdir) if f.endswith(".png")])
        assert n == frames, f"{n} frames on disk, {frames} expected"
        subprocess.run([FF, "-v", "error", "-y", "-framerate", str(FPS), "-i", os.path.join(fdir, "f%04d.png"),
                        "-vf", "format=yuv420p", "-c:v", "h264_mf", "-b:v", "20M",   # this build has no libx264
                        "-movflags", "+faststart", out], check=True)
        print(f"FILM DONE {frames} frames in {time.time() - t0:.0f} s -> {out}")
nball.py (4 kB) · The volume of the unit ball in every dimension, drawn with the 5x7 font. download
#!/usr/bin/env python3
"""The volume of the unit ball in n dimensions, drawn in numpy with the Hawking caption font (2026-09-10).

    V_n = pi^(n/2) / Gamma(n/2 + 1)          S_(n-1) = n V_n  (the surface measure of the unit (n-1)-sphere)

Every value is exact and knowable; none is visualisable past n = 3. The curve rises to a peak at n = 5 and
then falls to zero: a unit ball in a thousand dimensions has a volume of about 10^-886, and almost all of
what volume it has lies within a hair of its surface. These are facts about the very objects the owner says
cannot be seen, and they were known before anyone could draw a 4-cube.
"""
import math
import os
import pathlib
import subprocess
import sys

import numpy as np

sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))   # hawkcolor.py is beside this file
from hawkcolor import draw_text, text_width

FF = os.environ.get("FFMPEG", "ffmpeg")
W, H = 1280, 720


def V(n):
    return math.pi ** (n / 2) / math.gamma(n / 2 + 1)


ns = list(range(0, 26))
vols = [V(n) for n in ns]
surf = [n * V(n) for n in ns]

img = np.full((H, W, 3), 18, np.uint8)
L, R, T, B = 90, W - 40, 60, H - 90                  # plot box
ymax = 36.0


def px(n, y):
    return int(L + (R - L) * n / 25), int(B - (B - T) * y / ymax)


# axes and grid
for y in range(0, 37, 5):
    x0, yy = px(0, y); x1, _ = px(25, y)
    img[yy, x0:x1] = (45, 45, 45)
    draw_text(img, 20, yy - 7, f"{y:2d}", scale=2, color=(130, 130, 130))
for n in ns:
    x, y0 = px(n, 0)
    img[y0:y0 + 6, x] = (90, 90, 90)
    if n % 5 == 0:
        draw_text(img, x - 6, B + 12, f"{n}", scale=2, color=(130, 130, 130))
draw_text(img, (L + R) // 2 - text_width("dimension n", 2) // 2, B + 30, "dimension n", scale=2,
          color=(170, 170, 170))


def curve(vals, color, radius=4):
    pts = [px(n, min(v, ymax)) for n, v in zip(ns, vals)]
    for (x0, y0), (x1, y1) in zip(pts, pts[1:]):
        steps = max(abs(x1 - x0), abs(y1 - y0), 1)
        for k in range(steps + 1):
            x = int(x0 + (x1 - x0) * k / steps); y = int(y0 + (y1 - y0) * k / steps)
            img[y - 1:y + 2, x - 1:x + 2] = color
    for (x, y) in pts:
        yy, xx = np.ogrid[-radius:radius + 1, -radius:radius + 1]
        m = yy ** 2 + xx ** 2 <= radius ** 2
        img[y - radius:y + radius + 1, x - radius:x + radius + 1][m] = color


curve(surf, (120, 150, 235))
curve(vols, (245, 180, 60))

# labels at the peaks
nv = max(ns, key=V); x, y = px(nv, V(nv))
draw_text(img, x + 12, y - 54, f"unit ball volume peaks at n = {nv}: {V(nv):.3f}", scale=2, color=(245, 180, 60))
draw_text(img, x + 12, y - 32, "then falls to zero for ever", scale=2, color=(245, 180, 60))
nsf = max(ns, key=lambda n: n * V(n)); x, y = px(nsf, nsf * V(nsf))
draw_text(img, x + 12, y - 8, f"unit sphere surface peaks at n = {nsf}: {nsf * V(nsf):.2f}", scale=2, color=(120, 150, 235))

# the familiar rungs, named, in the empty right half
for i, ln in enumerate(("the rungs you can picture:",
                        "  n=1  segment  2         circle   2 pi",
                        "  n=2  disc     pi        sphere   4 pi",
                        "  n=3  ball     4 pi/3    3-sphere 2 pi^2",
                        "  n=4  4-ball   pi^2/2",
                        "  the 3-sphere is the one you would live on")):
    draw_text(img, 640, 240 + 24 * i, ln, scale=2, color=(200, 200, 200))

draw_text(img, L, 18, "V_n = pi^(n/2)/Gamma(n/2+1)    orange: unit n-ball volume    blue: unit (n-1)-sphere surface",
          scale=2, color=(215, 215, 215))
draw_text(img, L, H - 40, "every number here is exact, and none past n = 3 can be pictured", scale=2, color=(150, 150, 150))
draw_text(img, L, H - 20, "a unit ball in 1000 dimensions has volume about 10^-886", scale=2, color=(150, 150, 150))

subprocess.run([FF, "-v", "error", "-y", "-f", "rawvideo", "-pix_fmt", "rgb24", "-s", f"{W}x{H}", "-i", "-",
                "-frames:v", "1", "-update", "1", sys.argv[1]], input=img.tobytes(), check=True)
print("peak volume n =", nv, f"{V(nv):.4f}", "| peak surface n =", nsf, f"{nsf*V(nsf):.3f}",
      "| V_1000 = 10^%.0f" % (1000 / 2 * math.log10(math.pi) - math.lgamma(501) / math.log(10)))

Running them

python glome.py path                     # the flight circle and its clearance from the balls
python glome.py still one.png 0          # one frame, a few seconds
python glome.py film flight.mp4 480      # the circuit: 480 frames at 2.4 s each on one CPU, then the encode
python nball.py nball.png                # the chart

Colophon

The 3-sphere is the unit sphere in four coordinates; distances are angles, so a ball's size is an angle and a journey round the universe is 2π. What was checked: the 600-cell comes out as 120 distinct unit vectors with an edge of exactly 36 degrees; the flight circle clears every ball by 17.27 degrees against a ball-plus-head of 11; every ray on every frame terminates, asserted, since a ray that escaped would mean the universe had a hole in it; the first frame's nearest hit is 19.9 degrees and its farthest 356.1, which is the back of the head. What is not claimed: anything about the real universe beyond the measured bounds quoted above, or anything about how Einstein would have felt.

Written, rendered and packed in a single morning in September 2026, in a working session between the project's author and Claude, as a tangent from the black holes and from the video-interpolation project both have nothing to do with. The author's own construction, two bowls glued at the rim, was the right one; it needed one more rung and a lamp.