Creating Solar System Educational Animation
Solar System Educational - 3D Voxel Animation
This guide walks you through how to generate a looping 3D voxel animation of a solar system using SpatialStudio.
The script creates a realistic solar system with planets orbiting around the sun, complete with moons, asteroid belts, and glowing effects inside a cubic 3D space, then saves the animation to a .splv
file.
What this script does
- Creates a 3D scene of size 128×128×128
- Spawns 8 planets orbiting a central sun, each with:
- Realistic colors and sizes based on our solar system
- Elliptical orbital paths at different speeds
- Some planets have orbiting moons
- Glowing effects for the sun and gas giants
- Adds an asteroid belt between Mars and Jupiter
- Animates a complete orbital cycle for 12 seconds at 30 FPS
- Outputs the file
solar_system_educational.splv
that you can play in your viewer
How it works (simplified)
-
Voxel volume Each frame is a 3D grid filled with RGBA values (
SIZE × SIZE × SIZE × 4
). -
Central sun A large, bright yellow-orange sphere with pulsing glow effects to simulate solar activity.
-
Planetary orbits Each planet follows an elliptical path with realistic relative speeds and distances from the sun.
-
Planet rendering Planets are drawn as textured spheres with colors matching real planetary appearances.
-
Moons and rings Earth gets its moon, and Saturn gets visible rings for educational accuracy.
-
Asteroid belt Small rocky fragments orbit between Mars and Jupiter in a scattered belt formation.
-
Animation loop A normalized time variable
t
cycles through0 → 2π
, ensuring smooth orbital motion that loops perfectly. -
Encoding Frames are passed into
splv.Encoder
, which writes them into the.splv
video file.
Try it yourself
Install requirements first:
pip install spatialstudio numpy tqdm
Then copy this script into solar_system_educational.py
and run:
python solar_system_educational.py
Full Script
import numpy as np
from spatialstudio import splv
from tqdm import tqdm
# Scene setup
SIZE, FPS, SECONDS = 128, 30, 12
FRAMES = FPS * SECONDS
CENTER_X = CENTER_Y = CENTER_Z = SIZE // 2
OUT_PATH = "../outputs/solar_system_educational.splv"
# Solar system settings
SUN_RADIUS = 4
ASTEROID_COUNT = 150
# Planet data: [name, radius, orbit_distance, orbit_speed, color]
PLANETS = [
("Mercury", 1, 12, 4.0, (169, 169, 169)), # Gray
("Venus", 2, 16, 3.5, (255, 198, 73)), # Yellow-orange
("Earth", 2, 20, 3.0, (100, 149, 237)), # Blue
("Mars", 1, 24, 2.4, (205, 92, 92)), # Red
("Jupiter", 4, 35, 1.3, (255, 140, 0)), # Orange
("Saturn", 3, 42, 1.0, (250, 230, 143)), # Pale yellow
("Uranus", 2, 48, 0.7, (79, 208, 231)), # Cyan
("Neptune", 2, 54, 0.5, (72, 61, 139)) # Dark blue
]
def add_voxel(volume, x, y, z, color, alpha=255):
if 0 <= x < SIZE and 0 <= y < SIZE and 0 <= z < SIZE:
volume[x, y, z, :3] = color
volume[x, y, z, 3] = alpha
def generate_sphere(volume, cx, cy, cz, radius, color, glow=False, t=0):
for dx in range(-radius-2, radius+3):
for dy in range(-radius-2, radius+3):
for dz in range(-radius-2, radius+3):
distance = np.sqrt(dx*dx + dy*dy + dz*dz)
if distance <= radius:
# Main sphere body
if glow:
# Add pulsing effect for sun
pulse = 1.0 + 0.3 * np.sin(t * 3.0)
brightness = min(1.5, pulse)
final_color = tuple(min(255, int(c * brightness)) for c in color)
else:
# Add surface texture variation
texture = 1.0 + 0.2 * np.sin(dx*0.5 + dy*0.3 + dz*0.4)
final_color = tuple(min(255, int(c * texture)) for c in color)
add_voxel(volume, cx+dx, cy+dy, cz+dz, final_color)
elif glow and distance <= radius + 2:
# Glow effect for sun
glow_intensity = max(0, (radius + 2 - distance) / 2.0)
alpha = int(100 * glow_intensity)
if alpha > 0:
glow_color = tuple(min(255, int(c * 0.8)) for c in color)
add_voxel(volume, cx+dx, cy+dy, cz+dz, glow_color, alpha)
def generate_sun(volume, t):
# Animated sun with solar flares
base_radius = SUN_RADIUS
flare_radius = int(base_radius + 2 * np.sin(t * 2.5))
sun_color = (255, 200, 50) # Bright yellow-orange
generate_sphere(volume, CENTER_X, CENTER_Y, CENTER_Z, flare_radius, sun_color, glow=True, t=t)
def generate_planets(volume, t):
for i, (name, radius, orbit_dist, speed, color) in enumerate(PLANETS):
# Calculate orbital position
angle = t * speed + (i * 0.3) # Stagger initial positions
# Elliptical orbit (slight eccentricity)
ecc = 0.1
orbit_x = orbit_dist * (1 + ecc * np.cos(angle)) * np.cos(angle)
orbit_z = orbit_dist * (1 + ecc * np.cos(angle)) * np.sin(angle) * 0.8
orbit_y = np.sin(angle * 0.1) * 2 # Slight inclination
px = CENTER_X + int(orbit_x)
py = CENTER_Y + int(orbit_y)
pz = CENTER_Z + int(orbit_z)
# Generate planet
is_gas_giant = radius > 3
generate_sphere(volume, px, py, pz, radius, color, glow=is_gas_giant, t=t)
# Add Earth's moon
if name == "Earth":
moon_angle = t * 12.0 # Moon orbits faster
moon_dist = 6
moon_x = px + int(moon_dist * np.cos(moon_angle))
moon_z = pz + int(moon_dist * np.sin(moon_angle))
add_voxel(volume, moon_x, py, moon_z, (192, 192, 192))
add_voxel(volume, moon_x+1, py, moon_z, (192, 192, 192))
# Add Saturn's rings
if name == "Saturn":
generate_rings(volume, px, py, pz, radius + 2, radius + 4, t)
def generate_rings(volume, cx, cy, cz, inner_radius, outer_radius, t):
ring_color = (200, 200, 150)
rotation = t * 0.5
for angle in np.linspace(0, 2*np.pi, 64):
for r in range(inner_radius, outer_radius + 1):
if np.random.random() > 0.3: # Sparse rings
x = cx + int(r * np.cos(angle + rotation))
z = cz + int(r * np.sin(angle + rotation))
add_voxel(volume, x, cy, z, ring_color, alpha=150)
def generate_asteroid_belt(volume, t):
asteroid_color = (139, 128, 116) # Rocky gray-brown
np.random.seed(42) # Consistent asteroid positions
for i in range(ASTEROID_COUNT):
# Asteroid belt between Mars and Jupiter (orbit distance 28-32)
belt_radius = 28 + 4 * np.random.random()
angle = np.random.random() * 2 * np.pi
height_variation = (np.random.random() - 0.5) * 6
# Slow orbital motion
current_angle = angle + t * 0.8
ax = CENTER_X + int(belt_radius * np.cos(current_angle))
ay = CENTER_Y + int(height_variation)
az = CENTER_Z + int(belt_radius * np.sin(current_angle))
# Small, irregular asteroids
if np.random.random() > 0.7: # Only show some asteroids each frame
add_voxel(volume, ax, ay, az, asteroid_color)
if np.random.random() > 0.8: # Some larger asteroids
add_voxel(volume, ax+1, ay, az, asteroid_color)
def generate_orbital_trails(volume, t):
# Faint orbital trails for educational purposes
trail_color = (100, 100, 100)
for _, radius, orbit_dist, _, _ in PLANETS:
for angle in np.linspace(0, 2*np.pi, 48):
x = CENTER_X + int(orbit_dist * np.cos(angle))
z = CENTER_Z + int(orbit_dist * np.sin(angle))
# Very faint trail
if np.random.random() > 0.9:
add_voxel(volume, x, CENTER_Y, z, trail_color, alpha=50)
def generate_scene(volume, t):
# Generate orbital trails first (background)
generate_orbital_trails(volume, t)
# Generate asteroid belt
generate_asteroid_belt(volume, t)
# Generate planets
generate_planets(volume, t)
# Generate sun last (foreground)
generate_sun(volume, t)
# Initialize encoder
enc = splv.Encoder(SIZE, SIZE, SIZE, framerate=FPS, outputPath=OUT_PATH, motionVectors="off")
# Generate animation frames
for frame in tqdm(range(FRAMES), desc="Generating solar system"):
volume = np.zeros((SIZE, SIZE, SIZE, 4), dtype=np.uint8)
t = (frame / FRAMES) * 2 * np.pi
generate_scene(volume, t)
enc.encode(splv.Frame(volume, lrAxis="x", udAxis="y", fbAxis="z"))
enc.finish()
print(f"Created {OUT_PATH}")
Educational Features
This animation includes several educational elements:
- Realistic orbital speeds - Inner planets orbit faster than outer planets
- Size relationships - Jupiter is the largest planet, Mercury is the smallest
- Asteroid belt - Shows the rocky debris between Mars and Jupiter
- Saturn's rings - Distinctive ring system for easy identification
- Earth's moon - Our natural satellite in realistic orbital motion
- Solar activity - Pulsing sun with glow effects
- Orbital trails - Faint paths showing planetary orbits
Next steps
- Modify
SECONDS
to create longer orbital periods - Adjust planet
orbit_speed
values to change relative motion - Add more moons to other planets (Jupiter has 4 major moons!)
- Experiment with different
colors
for artistic interpretation - Increase
ASTEROID_COUNT
for a denser asteroid belt - Add comet trails by modifying the asteroid generation code
This solar system animation is perfect for educational presentations, astronomy lessons, or just exploring our cosmic neighborhood in 3D!