Creating Flower Animation
3D Voxel Animation Tutorial: Flower
This guide walks you through how to generate a looping 3D voxel animation of a flower using SpatialStudio.
The script creates a beautiful blooming flower with petals that gently sway and a stem that moves naturally 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
- Generates a blooming flower with:
- Colorful petals that open and close
- A green stem with natural bend motion
- Leaves that flutter in the breeze
- A bright yellow center core
- Animates the flower blooming and swaying for 8 seconds at 30 FPS
- Outputs the file
flower.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
). -
Flower petals Petals are drawn as elongated ellipsoids that rotate around the center, with opening/closing animation using sine waves.
-
Stem and leaves The stem is a vertical cylinder with gentle swaying motion, and leaves are flat oval shapes attached at intervals.
-
Center core A bright yellow sphere forms the flower's center, with subtle texture variations for realism.
-
Animation loop A normalized time variable
t
cycles from0 → 2π
, creating smooth blooming and swaying motions. -
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 flower.py
and run:
python flower.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/flower.splv"
# Flower settings
PETAL_COUNT = 6
PETAL_LENGTH = 15
STEM_HEIGHT = 40
LEAF_COUNT = 3
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_petal(volume, cx, cy, cz, angle, opening, color, t):
# Petal opens/closes based on opening factor (0-1)
length = int(PETAL_LENGTH * opening)
width = max(2, int(6 * opening))
for i in range(length):
for j in range(-width//2, width//2 + 1):
# Create petal shape with tapering
taper = 1.0 - (i / length) * 0.7
actual_width = max(1, int(j * taper))
# Calculate position with rotation
px = int(cx + i * np.cos(angle) + actual_width * np.sin(angle))
pz = int(cz + i * np.sin(angle) - actual_width * np.cos(angle))
py = cy + int(np.sin(i * 0.2 + t) * 2) # Slight vertical wave
# Add color variation for depth
brightness = 0.8 + 0.2 * np.sin(i * 0.3)
final_color = tuple(int(c * brightness) for c in color)
add_voxel(volume, px, py, pz, final_color)
def generate_flower_center(volume, cx, cy, cz, t):
center_color = (255, 255, 0) # Yellow
center_radius = 4
for dx in range(-center_radius, center_radius + 1):
for dy in range(-center_radius, center_radius + 1):
for dz in range(-center_radius, center_radius + 1):
distance = np.sqrt(dx*dx + dy*dy + dz*dz)
if distance <= center_radius:
# Add texture to center
texture = int(np.sin(dx*0.5 + dy*0.5 + dz*0.5 + t) * 20)
brightness = 0.9 + texture * 0.01
final_color = tuple(min(255, int(c * brightness)) for c in center_color)
add_voxel(volume, cx + dx, cy + dy, cz + dz, final_color)
def generate_stem(volume, cx, cy, cz, t):
stem_color = (34, 139, 34) # Forest green
stem_width = 2
# Stem sways gently
sway = np.sin(t * 1.5) * 3
for i in range(STEM_HEIGHT):
# Calculate sway that increases toward the top
sway_amount = int(sway * (i / STEM_HEIGHT))
stem_x = cx + sway_amount
stem_y = cy - STEM_HEIGHT + i
# Draw stem cross-section
for dx in range(-stem_width, stem_width + 1):
for dz in range(-stem_width, stem_width + 1):
if dx*dx + dz*dz <= stem_width*stem_width:
add_voxel(volume, stem_x + dx, stem_y, cz + dz, stem_color)
def generate_leaves(volume, cx, cy, cz, t):
leaf_color = (0, 128, 0) # Green
for i in range(LEAF_COUNT):
# Position leaves along stem
leaf_y = cy - STEM_HEIGHT + (i + 1) * (STEM_HEIGHT // (LEAF_COUNT + 1))
leaf_angle = (i * 120) * np.pi / 180 # 120 degrees apart
# Leaf flutter animation
flutter = np.sin(t * 2.0 + i * 0.5) * 0.3
# Generate leaf shape
for j in range(8): # Leaf length
for k in range(-3, 4): # Leaf width
if abs(k) <= 3 - j * 0.3: # Tapered leaf shape
leaf_x = cx + int((j + 5) * np.cos(leaf_angle + flutter))
leaf_z = cz + int((j + 5) * np.sin(leaf_angle + flutter))
leaf_pos_y = leaf_y + k // 2
brightness = 0.7 + 0.3 * np.random.random()
final_color = tuple(int(c * brightness) for c in leaf_color)
add_voxel(volume, leaf_x, leaf_pos_y, leaf_z + k, final_color)
def generate_flower(volume, cx, cy, cz, t):
# Blooming animation - flower opens and closes slowly
bloom_cycle = (np.sin(t * 0.5) + 1) * 0.5 # 0 to 1
opening = 0.3 + 0.7 * bloom_cycle # Never fully closes
# Petal colors - gradient from red to pink
petal_colors = [
(255, 100, 150), # Pink-red
(255, 150, 180), # Light pink
(255, 120, 160), # Medium pink
(255, 80, 140), # Deep pink
(255, 130, 170), # Soft pink
(255, 110, 155), # Rose pink
]
# Generate stem first (behind flower)
generate_stem(volume, cx, cy, cz, t)
generate_leaves(volume, cx, cy, cz, t)
# Generate petals
for i in range(PETAL_COUNT):
angle = (i / PETAL_COUNT) * 2 * np.pi
# Add slight rotation animation
angle += t * 0.1
color = petal_colors[i % len(petal_colors)]
generate_petal(volume, cx, cy, cz, angle, opening, color, t)
# Generate center last (in front)
generate_flower_center(volume, cx, cy, cz, t)
def generate_scene(volume, t):
# Place flower slightly above center for better framing
flower_y = CENTER_Y + 10
generate_flower(volume, CENTER_X, flower_y, CENTER_Z, t)
enc = splv.Encoder(SIZE, SIZE, SIZE, framerate=FPS, outputPath=OUT_PATH, motionVectors="off")
for frame in tqdm(range(FRAMES), desc="Growing flower"):
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
- Change
PETAL_COUNT
to create flowers with different numbers of petals. - Modify
petal_colors
array for different flower varieties (sunflower, rose, etc.). - Adjust the blooming speed by changing the multiplier in
t * 0.5
. - Add multiple flowers by calling
generate_flower()
with different positions. - Experiment with
STEM_HEIGHT
andLEAF_COUNT
for different plant proportions.