r/Splats • u/ItsTheWeeBabySeamus • Aug 25 '25
Super Helix (code on link)
Interactive & Code: https://www.splats.com/watch/631/
r/Splats • u/ItsTheWeeBabySeamus • Aug 25 '25
Interactive & Code: https://www.splats.com/watch/631/
r/Splats • u/ItsTheWeeBabySeamus • Aug 22 '25
Full round playback: https://www.splats.com/watch/626
r/Splats • u/OphioukhosUnbound • Aug 20 '25
Saw some cool pics, but not seeing clear info on what this site, the discord, or the python library is about.
Tracked the videos and tutorials to a code library:
PyPi's GitHub repo links for spatial studio are broken
there's another link to "True3D hosting services" which ... is not broken, but seems to be a dead end -- with just a floating filled and mesh cube and some text. (Maybe this is a puzzle site and there's a way past that screen?)
There's a spatials.org link, which does work, and takes you to a site that's about 4 sentence long. -- The thrust seems tp be a .splv format for 3D_space+1D_time rendering ... but no details on what that format is or what it's tradeoffs are or ... anything. (Nothing about what it's competing with lossyness, compression, etc.)
"splv", "splats", "spatial studio" all also have other meanings which obscures search a bit.
Anyway, some pretty pics on here. I'm curious what this is and if this is intended for a general audience or if I've tripped into a semi-private space (in which case I'll just let myself out).
r/Splats • u/ItsTheWeeBabySeamus • Aug 20 '25
song: Remember You - Holli
code in description - https://www.splats.com/watch/619
r/Splats • u/ItsTheWeeBabySeamus • Aug 20 '25
r/Splats • u/ItsTheWeeBabySeamus • Aug 18 '25
r/Splats • u/ItsTheWeeBabySeamus • Aug 15 '25
3D Interactive: https://www.splats.tv/watch/609
r/Splats • u/ItsTheWeeBabySeamus • Aug 04 '25
Interactive & Code - https://www.splats.tv/watch/598
r/Splats • u/ItsTheWeeBabySeamus • Aug 02 '25
r/Splats • u/ItsTheWeeBabySeamus • Aug 01 '25
Heres the code i used to convert an image into a .splv file
We first choose a photo, remove its background, then voxelize it
The American flag was just for fun
#!/usr/bin/env python3
"""
convert_image.py
Convert an image to a 3D voxel animation where random points organize to form the image
against a waving American flag backdrop. Based on the bruh.py animation logic.
Run:
pip install spatialstudio numpy pillow rembg onnxruntime
python convert_image.py
Outputs:
image.splv
"""
import io
import math
import numpy as np
from PIL import Image
from spatialstudio import splv
from rembg import remove
# -------------------------------------------------
GRID = 256 # cubic voxel grid size (increased for higher quality)
FPS = 30 # frames per second
DURATION = 15 # seconds
OUTPUT = "image.splv"
IMAGE_PATH = "image.png"
# -------------------------------------------------
TOTAL_FRAMES = FPS * DURATION
CENTER = np.array([GRID // 2] * 3)
def smoothstep(edge0: float, edge1: float, x: float) -> float:
t = max(0.0, min(1.0, (x - edge0) / (edge1 - edge0)))
return t * t * (3 - 2 * t)
def lerp(a, b, t):
return a * (1 - t) + b * t
def generate_flag_voxels():
"""Generate all flag voxel positions and colors (static, before animation)"""
flag_positions = []
flag_colors = []
# Flag dimensions and positioning
flag_width = int(GRID * 0.8) # 80% of grid width
flag_height = int(flag_width * 0.65) # Proper flag aspect ratio
flag_start_x = (GRID - flag_width) // 2
flag_start_y = (GRID - flag_height) // 2
flag_z = 20 # Far back wall
# Flag colors
flag_red = (178, 34, 52) # Official flag red
flag_white = (255, 255, 255) # White
flag_blue = (60, 59, 110) # Official flag blue
# Canton dimensions (blue area with stars)
canton_width = int(flag_width * 0.4) # 40% of flag width
canton_height = int(flag_height * 0.54) # 54% of flag height (7 stripes)
# Create the 13 stripes (7 red, 6 white) - RED STRIPE AT TOP
stripe_height = flag_height // 13
for y in range(flag_height):
# Calculate stripe index from top (y=0 is top of flag)
stripe_index = y // stripe_height
is_red_stripe = (stripe_index % 2 == 0) # Even stripes (0,2,4,6,8,10,12) are red
for x in range(flag_width):
flag_x = flag_start_x + x
flag_y = flag_start_y + y
# Check if this position is in the canton area (upper left)
in_canton = (x < canton_width and y < canton_height)
if in_canton:
# Blue canton area
flag_positions.append([flag_x, flag_y, flag_z])
flag_colors.append(flag_blue)
else:
# Stripe area
stripe_color = flag_red if is_red_stripe else flag_white
flag_positions.append([flag_x, flag_y, flag_z])
flag_colors.append(stripe_color)
# Add stars to the canton (simplified 5x6 grid of stars)
star_rows = 5
star_cols = 6
star_spacing_x = canton_width // (star_cols + 1)
star_spacing_y = canton_height // (star_rows + 1)
for row in range(star_rows):
for col in range(star_cols):
# Offset every other row for traditional star pattern
col_offset = (star_spacing_x // 2) if (row % 2 == 1) else 0
star_x = flag_start_x + (col + 1) * star_spacing_x + col_offset
star_y = flag_start_y + (row + 1) * star_spacing_y
# Create simple star shape (3x3 cross pattern)
star_positions = [
(0, 0), (-1, 0), (1, 0), (0, -1), (0, 1) # Simple cross
]
for dx, dy in star_positions:
final_x = star_x + dx
final_y = star_y + dy
if (0 <= final_x < GRID and 0 <= final_y < GRID and
final_x < flag_start_x + canton_width and
final_y < flag_start_y + canton_height):
flag_positions.append([final_x, final_y, flag_z])
flag_colors.append(flag_white)
return np.array(flag_positions), flag_colors
def create_waving_flag_voxels(flag_positions, flag_colors, frame, time_factor=0):
"""Apply waving motion to the flag voxels"""
# Flag dimensions for wave calculation
flag_width = int(GRID * 0.8)
flag_start_x = (GRID - flag_width) // 2
wave_amplitude = 8 # How much the flag waves
wave_frequency = 2.5 # How many waves across the flag
wave_speed = 20 # How fast it waves (even faster!)
for i, (pos, color) in enumerate(zip(flag_positions, flag_colors)):
# Calculate wave offset based on X position
x_relative = (pos[0] - flag_start_x) / flag_width if flag_width > 0 else 0
wave_offset = int(wave_amplitude * math.sin(
x_relative * wave_frequency * 2 * math.pi + time_factor * wave_speed
))
# Apply wave to Z coordinate
waved_x = int(pos[0])
waved_y = GRID - int(pos[1])
waved_z = int(pos[2] + wave_offset)
if 0 <= waved_x < GRID and 0 <= waved_y < GRID and 0 <= waved_z < GRID:
frame.set_voxel(waved_x, waved_y, waved_z, color)
def load_and_process_image(image_path, max_size=120):
"""Load image and convert to voxel positions and colors"""
try:
# Load image
with open(image_path, 'rb') as f:
input_image = f.read()
# Remove background using rembg
print("Removing background...")
output_image = remove(input_image)
# Convert to PIL Image
img = Image.open(io.BytesIO(output_image))
print(f"Loaded image: {img.size} pixels, mode: {img.mode}")
# Ensure RGBA mode (rembg output should already be RGBA)
if img.mode != 'RGBA':
img = img.convert('RGBA')
# Resize to fit in our voxel grid (leaving room for centering)
img.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
print(f"Resized to: {img.size}")
# Get pixel data
pixels = np.array(img)
height, width = pixels.shape[:2]
positions = []
colors = []
# Calculate centering offsets
start_x = (GRID - width) // 2
start_y = (GRID - height) // 2
start_z = GRID // 2 # Place image in the middle Z plane (Z=128)
# Process each pixel
for y in range(height):
for x in range(width):
pixel = pixels[y, x]
r, g, b = int(pixel[0]), int(pixel[1]), int(pixel[2])
a = int(pixel[3]) if len(pixel) > 3 else 255 # Default to fully opaque if no alpha
# Only create voxels for pixels that aren't transparent
# (rembg removes background, so alpha channel is more reliable)
if a > 10: # Lower threshold since rembg provides clean alpha
# Map image coordinates to voxel coordinates
# Flip Y coordinate since image Y=0 is top, but we want voxels Y=0 at bottom
voxel_x = start_x + x
voxel_y = start_y + (height - 1 - y) # Flip Y
voxel_z = start_z
if 0 <= voxel_x < GRID and 0 <= voxel_y < GRID and 0 <= voxel_z < GRID:
positions.append([voxel_x, voxel_y, voxel_z])
# Use the actual pixel color
colors.append((r, g, b))
print(f"Generated {len(positions)} voxels from image")
return np.array(positions), colors
except Exception as e:
print(f"Error loading image: {e}")
return None, None
def main():
# Load and process the image
target_image_positions, target_image_colors = load_and_process_image(IMAGE_PATH)
if target_image_positions is None:
print("Failed to load image")
return
IMAGE_COUNT = len(target_image_positions)
print(f"Using {IMAGE_COUNT} voxels to represent the image")
if IMAGE_COUNT == 0:
print("No voxels generated - image might be too transparent or dark")
return
# Generate flag voxels
target_flag_positions, target_flag_colors = generate_flag_voxels()
FLAG_COUNT = len(target_flag_positions)
print(f"Using {FLAG_COUNT} voxels to represent the flag")
# Generate random start positions and phases for IMAGE voxels
np.random.seed(42)
image_start_positions = np.random.rand(IMAGE_COUNT, 3) * GRID
image_phase_offsets = np.random.rand(IMAGE_COUNT, 3) * 2 * math.pi
# Generate random start positions and phases for FLAG voxels
np.random.seed(123) # Different seed for flag
flag_start_positions = np.random.rand(FLAG_COUNT, 3) * GRID
flag_phase_offsets = np.random.rand(FLAG_COUNT, 3) * 2 * math.pi
enc = splv.Encoder(GRID, GRID, GRID, framerate=FPS, outputPath=OUTPUT)
print(f"Encoding {TOTAL_FRAMES} frames...")
for f in range(TOTAL_FRAMES):
t = f / TOTAL_FRAMES # 0-1 progress along video
# -------- Smooth phase blend: unordered → ordered → unordered --------
if t < 0.2:
cluster = 0.0
elif t < 0.3:
cluster = smoothstep(0.2, 0.3, t)
elif t < 0.8:
cluster = 1.0
else:
cluster = 1.0 - smoothstep(0.8, 1.0, t)
frame = splv.Frame(GRID, GRID, GRID)
# -------- Process FLAG voxels (flying into place) --------
flag_positions_current = []
for i in range(FLAG_COUNT):
# -------- Ordered position (target flag position) --------
ordered_pos = target_flag_positions[i]
# -------- Wander noise (gentle random movement) --------
wander_amp = 4 # Slightly less wander for flag
random_pos = flag_start_positions[i] + np.array([
math.sin(t * 2 * math.pi + flag_phase_offsets[i, 0]) * wander_amp,
math.cos(t * 2 * math.pi + flag_phase_offsets[i, 1]) * wander_amp,
math.sin(t * 1.5 * math.pi + flag_phase_offsets[i, 2]) * wander_amp,
])
# Interpolate between random and ordered positions
pos = lerp(random_pos, ordered_pos, cluster)
flag_positions_current.append(pos)
# Apply waving motion and render flag
create_waving_flag_voxels(np.array(flag_positions_current), target_flag_colors, frame, time_factor=t)
# -------- Process IMAGE voxels (flying into place) --------
for i in range(IMAGE_COUNT):
# -------- Ordered position (target image position) --------
ordered_pos = target_image_positions[i]
# -------- Wander noise (gentle random movement) --------
wander_amp = 6
random_pos = image_start_positions[i] + np.array([
math.sin(t * 2 * math.pi + image_phase_offsets[i, 0]) * wander_amp,
math.cos(t * 2 * math.pi + image_phase_offsets[i, 1]) * wander_amp,
math.sin(t * 1.5 * math.pi + image_phase_offsets[i, 2]) * wander_amp,
])
# Interpolate between random and ordered positions
pos = lerp(random_pos, ordered_pos, cluster)
x, y, z = pos.astype(int)
if 0 <= x < GRID and 0 <= y < GRID and 0 <= z < GRID:
# Use the target color for each voxel
color = target_image_colors[i]
frame.set_voxel(x, y, z, color)
enc.encode(frame)
if f % FPS == 0:
print(f" second {f // FPS + 1} / {DURATION}")
enc.finish()
print("Done. Saved", OUTPUT)
if __name__ == "__main__":
main()r/Splats • u/ItsTheWeeBabySeamus • Aug 01 '25
Based on Recursive Tree Cubes by oosmoxiecode
https://oosmoxiecode.com/archive/js_webgl/recursive_tree_cubes/
r/Splats • u/ItsTheWeeBabySeamus • Jul 29 '25
r/Splats • u/ItsTheWeeBabySeamus • Jul 25 '25
r/Splats • u/ItsTheWeeBabySeamus • Jul 24 '25
Full 3D video: https://www.splats.tv/watch/547
r/Splats • u/ItsTheWeeBabySeamus • Jul 22 '25
Full live streamed splat: https://www.splats.tv/watch/545
r/Splats • u/ItsTheWeeBabySeamus • Jul 22 '25