Creating Octopus Animation
3D Voxel Animation: Octopus
This guide walks you through how to generate a looping 3D voxel animation of an octopus using SpatialStudio.
The script creates a majestic octopus with flowing tentacles that sway gracefully underwater 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 1 animated octopus featuring:
- A bulbous head/mantle with texture
- 8 flowing tentacles with suction cups
- Realistic underwater swaying motion
- Color variations for depth and realism
- Animates the octopus floating and moving for 8 seconds at 30 FPS
- Outputs the file
octopus.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
). -
Octopus body The main body is drawn as an ellipsoid with subtle texture variations to simulate the soft, organic surface.
-
Tentacles Eight tentacles extend from the body, each following a unique sinusoidal path that creates natural underwater movement.
-
Suction cups Small circular details are added along each tentacle to represent the characteristic suction cups.
-
Animation loop A normalized time variable
t
cycles from0 → 2π
, creating smooth tentacle waves and gentle body bobbing. -
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 octopus.py
and run:
python octopus.py
Full Script
import numpy as np
from spatialstudio import splv
from tqdm import tqdm
# Scene setup
SIZE, FPS, SECONDS = 128, 30, 8
FRAMES = FPS * SECONDS
CENTER_X = CENTER_Y = CENTER_Z = SIZE // 2
OUT_PATH = "../outputs/octopus.splv"
# Octopus settings
BODY_WIDTH = 12
BODY_HEIGHT = 16
TENTACLE_COUNT = 8
TENTACLE_LENGTH = 35
TENTACLE_THICKNESS = 4
def add_voxel(volume, x, y, z, color):
if 0 <= x < SIZE and 0 <= y < SIZE and 0 <= z < SIZE:
volume[x, y, z, :3] = color
volume[x, y, z, 3] = 255
def generate_octopus_body(volume, cx, cy, cz, t):
# Main body colors - reddish-orange octopus
base_color = (180, 80, 60)
# Generate ellipsoid body
for dx in range(-BODY_WIDTH, BODY_WIDTH+1):
for dy in range(-BODY_HEIGHT, BODY_HEIGHT+1):
for dz in range(-BODY_WIDTH, BODY_WIDTH+1):
# Ellipsoid equation
if (dx*dx)/(BODY_WIDTH*BODY_WIDTH) + (dy*dy)/(BODY_HEIGHT*BODY_HEIGHT) + (dz*dz)/(BODY_WIDTH*BODY_WIDTH) <= 1:
# Add texture with sine waves
texture = np.sin(dx*0.2 + t*0.5) * np.sin(dz*0.2 + t*0.3) * 0.3
brightness = 1.0 + texture
# Vary color based on position for depth
color_variation = 1.0 + (dy / BODY_HEIGHT) * 0.2
final_color = tuple(min(255, max(50, int(c * brightness * color_variation))) for c in base_color)
add_voxel(volume, cx+dx, cy+dy, cz+dz, final_color)
def generate_tentacle(volume, cx, cy, cz, tentacle_index, t):
tentacle_color = (160, 70, 50)
sucker_color = (140, 50, 40)
# Each tentacle has a unique phase and direction
angle_base = (tentacle_index / TENTACLE_COUNT) * 2 * np.pi
phase = tentacle_index * 0.5
for segment in range(TENTACLE_LENGTH):
# Calculate position along tentacle
progress = segment / TENTACLE_LENGTH
# Base direction from body center
base_x = np.cos(angle_base) * (8 + segment * 0.8)
base_z = np.sin(angle_base) * (8 + segment * 0.8)
# Add wave motion
wave_x = np.sin(t * 1.5 + phase + segment * 0.15) * progress * 8
wave_y = np.cos(t * 1.2 + phase + segment * 0.1) * progress * 4
wave_z = np.sin(t * 1.8 + phase + segment * 0.12) * progress * 6
# Final tentacle position
tx = int(cx + base_x + wave_x)
ty = int(cy + BODY_HEIGHT * 0.5 - segment * 0.3 + wave_y)
tz = int(cz + base_z + wave_z)
# Draw tentacle segment with tapering thickness
current_thickness = max(1, int(TENTACLE_THICKNESS * (1 - progress * 0.7)))
for dx in range(-current_thickness, current_thickness+1):
for dy in range(-current_thickness, current_thickness+1):
for dz in range(-current_thickness, current_thickness+1):
if dx*dx + dy*dy + dz*dz <= current_thickness*current_thickness:
add_voxel(volume, tx+dx, ty+dy, tz+dz, tentacle_color)
# Add suction cups every few segments
if segment % 4 == 0 and segment > 5:
# Position suckers on the underside
sucker_y = ty + current_thickness + 1
for dx in range(-1, 2):
for dz in range(-1, 2):
if dx*dx + dz*dz <= 1:
add_voxel(volume, tx+dx, sucker_y, tz+dz, sucker_color)
def generate_octopus_eyes(volume, cx, cy, cz, t):
eye_color = (20, 20, 20) # Dark eyes
eye_highlight = (255, 255, 255)
# Left eye
eye_x1 = cx - 6
eye_y1 = cy - 4
eye_z1 = cz + 8
# Right eye
eye_x2 = cx + 6
eye_y2 = cy - 4
eye_z2 = cz + 8
# Draw both eyes
for eye_x, eye_y, eye_z in [(eye_x1, eye_y1, eye_z1), (eye_x2, eye_y2, eye_z2)]:
# Main eye
for dx in range(-2, 3):
for dy in range(-2, 3):
for dz in range(-1, 2):
if dx*dx + dy*dy + dz*dz <= 4:
add_voxel(volume, eye_x+dx, eye_y+dy, eye_z+dz, eye_color)
# Eye highlight
add_voxel(volume, eye_x-1, eye_y-1, eye_z, eye_highlight)
def generate_octopus(volume, cx, cy, cz, t):
# Generate main body
generate_octopus_body(volume, cx, cy, cz, t)
# Generate all tentacles
for i in range(TENTACLE_COUNT):
generate_tentacle(volume, cx, cy, cz, i, t)
# Add eyes
generate_octopus_eyes(volume, cx, cy, cz, t)
def generate_scene(volume, t):
# Add gentle bobbing motion to the whole octopus
bob_y = int(np.sin(t * 0.8) * 3)
generate_octopus(volume, CENTER_X, CENTER_Y + bob_y, CENTER_Z, 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 octopus"):
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}")
Next steps
- Adjust
TENTACLE_LENGTH
to make longer or shorter tentacles - Change the base colors to create different octopus species (try blues for a blue-ringed octopus)
- Modify
TENTACLE_COUNT
to experiment with different creatures - Add bubble effects by placing small white voxels that rise upward
- Create a seafloor by adding voxels at the bottom of the scene
- Experiment with the wave parameters to make more dramatic or subtle tentacle movements