Gravitational Time Dilation Simulation with Pygame
Educational Python Project – Visualize How Gravity Affects Time
This tutorial walks through building an interactive Gravitational Time Dilation Simulation using Python and Pygame. The simulation demonstrates how time passes differently under varying gravitational strengths, comparing a clock at Earth’s surface to a clock under stronger or weaker gravity.
Table of Contents
- Overview
- Theory: Gravitational Time Dilation
- Pygame Setup
- Clock Display
- Gravity Adjustment
- Matplotlib Live Plot
- Relative Year Info
- Complete Python Code
- How to Run
- Key Learnings
- Further Ideas
Overview
This project simulates gravitational time dilation using Python. You can interactively increase or decrease gravity and watch how the time on the gravity clock diverges from the Earth clock.
Theory: Gravitational Time Dilation
According to general relativity, time passes differently under different gravitational potentials. A clock closer to a massive object ticks slower than a clock further away. The simulation uses the formula:
\[\text{time factor} = \frac{1}{\sqrt{1 - \frac{2 G M}{r c^2}}}\]Where:
- (G) is the gravitational constant
- (M) is the mass of the object (Earth)
- (r) is the distance from the center of the mass
- (c) is the speed of light
Pygame Setup
We initialize a Pygame window and define fonts for various display elements:
pygame.init()
WIDTH, HEIGHT = 500, 280
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Gravitational Time Dilation Clock")
Fonts are adjustable at the top:
FONT_SIZE_INFO = 28
FONT_SIZE_CLOCK = 50
FONT_SIZE_SMALL = 40
font_info = pygame.font.SysFont(None, FONT_SIZE_INFO)
font_clock = pygame.font.SysFont(None, FONT_SIZE_CLOCK)
font_small = pygame.font.SysFont(None, FONT_SIZE_SMALL)
Clock Display
The simulation shows two clocks:
- Earth Clock: baseline reference
- Gravity Clock: affected by gravity changes
Gravity Adjustment
You can use the UP and DOWN arrow keys to increase or decrease gravity. The clocks update in real-time, and the relative rate is displayed.
Matplotlib Live Plot
A live Matplotlib plot shows the relative clock rate vs gravity. The plot features:
- Blue line with sky-blue dots
- Gradient shadow under the curve
- Dynamic Y-axis
line, = ax.plot([], [], color='blue', marker='o', markerfacecolor='skyblue', markersize=6, linestyle='-')
Relative Year Info
The simulation also shows:
1 Year @ Earth = N Year @ Gravity Clock
This uses the relative_rate variable to dynamically display how 1 Earth year corresponds to a different duration on the gravity clock.
Complete Python Code
import pygame
import sys
import math
import time
import matplotlib.pyplot as plt
#  Constants 
G = 6.67430e-11           # Gravitational constant (m³/kg·s²)
c = 299792458             # Speed of light (m/s)
M = 5.972e24              # Mass of Earth (kg)
R_earth = 6.371e6         # Radius of Earth (m)
g_surface = G * M / R_earth**2  # Surface gravity of Earth (m/s²)
r_s = 2 * G * M / c**2    # Schwarzschild radius for Earth (m)
gf_earth = 1 / math.sqrt(1 - 2*G*M/(R_earth*c**2))  # Time dilation factor at Earth surface
SECONDS_PER_EARTH_YEAR = 365.25 * 24 * 3600  # Seconds in one Earth year
#  Adjustable font sizes 
FONT_SIZE_INFO = 28       # Info line font
FONT_SIZE_CLOCK = 50      # Clock fonts
FONT_SIZE_SMALL = 40      # Gravity / Relative Rate text
#  Pygame setup 
pygame.init()
WIDTH, HEIGHT = 500, 280
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Gravitational Time Dilation Clock")
font_info = pygame.font.SysFont(None, FONT_SIZE_INFO)
font_clock = pygame.font.SysFont(None, FONT_SIZE_CLOCK)
font_small = pygame.font.SysFont(None, FONT_SIZE_SMALL)
clock = pygame.time.Clock()
#  Initial conditions 
gravity = g_surface
keys = {"up": False, "down": False}
t_earth = 0.0
t_gravity = 0.0
prev_time = time.time()
#  Matplotlib setup 
plt.ion()
fig, ax = plt.subplots(figsize=(5, 4))
fig.patch.set_facecolor('black')
ax.set_facecolor('black')
ax.tick_params(axis='x', colors='lime')
ax.tick_params(axis='y', colors='lime')
ax.xaxis.label.set_color('lime')
ax.yaxis.label.set_color('lime')
ax.title.set_color('lime')
fig.subplots_adjust(left=0.25, right=0.95, top=0.9, bottom=0.12)
fig.canvas.manager.set_window_title("Gravitational Time Dilation vs. Local Gravity")
line, = ax.plot([], [], color='blue', marker='o', markerfacecolor='skyblue', markersize=6, linestyle='-')
ax.set_xlabel("Gravity (m/s², log scale)")
ax.set_ylabel("Relative Rate (Earth Clock / Gravity Clock)")
ax.set_xscale('log')
ax.grid(True, color='gray', linestyle='--', alpha=0.5)
gravities = []
relative_rates = []
#  Function to calculate gravity factor safely 
def gravity_factor_from_g(g):
    r = max(math.sqrt(G*M/g), r_s * 1.0001)
    return 1 / math.sqrt(1 - 2*G*M/(r*c**2))
#  Format time function 
def format_time(t):
    t_int = int(t)
    h = t_int // 3600
    m = (t_int % 3600) // 60
    s = t_int % 60
    return f"{h:02d}:{m:02d}:{s:02d}"
#  Main loop 
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                keys["up"] = True
            elif event.key == pygame.K_DOWN:
                keys["down"] = True
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_UP:
                keys["up"] = False
            elif event.key == pygame.K_DOWN:
                keys["down"] = False
    #  Compute delta time 
    current_time = time.time()
    delta_earth = current_time - prev_time
    prev_time = current_time
    #  Update gravity 
    if keys["up"]:
        gravity *= 10**0.05
    if keys["down"]:
        gravity /= 10**0.05
    gravity = max(gravity, 1e-5)
    #  Update clocks 
    t_earth += delta_earth / gf_earth
    gf = gravity_factor_from_g(gravity)
    t_gravity += delta_earth / gf
    relative_rate = gf_earth / gf
    #  Update Matplotlib plot 
    if keys["up"] or keys["down"]:
        gravities.append(gravity)
        relative_rates.append(relative_rate)
        if len(gravities) > 30:
            gravities.pop(0)
            relative_rates.pop(0)
        line.set_data(gravities, relative_rates)
        # Remove old gradient fills
        for coll in ax.collections:
            coll.remove()
        # Gradient shadow under the curve
        alpha_max = 0.5
        n_layers = 10
        for i in range(n_layers):
            alpha = alpha_max * (i+1)/n_layers
            ax.fill_between(
                gravities,
                [r*(i/n_layers) for r in relative_rates],
                relative_rates,
                color='skyblue',
                alpha=alpha
            )
        # Dynamic Y-axis limits
        ymin = min(relative_rates) - 0.1
        ymax = max(relative_rates) + 0.1
        ax.set_ylim(ymin, ymax)
        ax.relim()
        ax.autoscale_view(scalex=True, scaley=False)
        plt.tight_layout()
        plt.pause(0.001)
    #  Compute 1 Year equivalence 
    gravity_years = relative_rate  # 1 Earth year = relative_rate Gravity Clock years
    info_str = f"1 Year @ Earth = {gravity_years:.6f} Year @ Gravity Clock"
    #  Draw Pygame display 
    screen.fill((0, 0, 0))
    screen.blit(font_info.render(info_str, True, (255, 200, 0)), (20, 10))
    screen.blit(font_clock.render(f"Earth Clock: {format_time(t_earth)}", True, (255, 255, 0)), (20, 60))
    screen.blit(font_clock.render(f"Gravity Clock: {format_time(t_gravity)}", True, (0, 255, 255)), (20, 120))
    screen.blit(font_small.render(f"Gravity: {gravity:.2e} m/s²", True, (255, 100, 100)), (20, 180))
    screen.blit(font_small.render(f"Relative Rate: {relative_rate:.6f}", True, (0, 255, 0)), (20, 230))
    pygame.display.flip()
    clock.tick(60)
pygame.quit()
sys.exit()
How to Run
- Install required libraries:
pip install pygame matplotlib
- Run the Python script:
python gravitational_time_dilation.py
- Clikc on the PyGame window and then Use UP/DOWN arrow keys to change gravity interactively.
Key Learnings
- Gravitational time dilation in practice
- Interactive simulations with Pygame
- Real-time plotting with Matplotlib
- Combining physics with visualization for education
Further Ideas
- Add multiple objects with different masses
- Show a 3D representation of gravity wells
- Include relativistic effects at high velocities
- Export simulation data for further analysis