2026-02-05 23:01:36 -05:00

2217 lines
68 KiB
Python

"""
SURYA — A Mathematical Journey Through Feeling
Das's 14-track emotional odyssey rendered in mathematical beauty
Created with Manim Community Edition
"""
from manim import *
import numpy as np
from scipy.interpolate import interp1d
import random
# ============================================================================
# CONFIGURATION
# ============================================================================
config.background_color = "#030305"
# Color palette matching the SURYA website aesthetic
class SuryaColors:
# Track colors
INTRO = "#a78bfa" # Purple
SKIN = "#f472b6" # Pink
SAVED = "#22d3ee" # Cyan
NOTHING = "#666666" # Grey
RELIEF = "#a78bfa" # Purple
NATURE = "#5eead4" # Teal
DREAM = "#c4b5fd" # Soft purple
IDK = "#f472b6" # Dark pink
WITH_U = "#fbbf24" # Golden (THE TURN)
POOR = "#fb923c" # Orange
WAIT = "#a78bfa" # Purple longing
RUN = "#22d3ee" # Cyan urgent
MEDS = "#ef4444" # Red
HOLLOW = "#fbbf24" # Golden resolution
# Supporting colors
DARK_BG = "#030305"
SOUL_WHITE = "#ffffff"
GLOW_SOFT = "#ffffff40"
# ============================================================================
# CUSTOM MATHEMATICAL OBJECTS
# ============================================================================
class SoulOrb(VGroup):
"""The persistent soul orb - heart of the journey"""
def __init__(self, color=WHITE, radius=0.3, glow_radius=1.5, **kwargs):
super().__init__(**kwargs)
# Core orb
self.core = Dot(radius=radius, color=color)
self.core.set_fill(color, opacity=1)
# Glow layers (gaussian-like falloff)
self.glow_layers = VGroup()
for i in range(8):
factor = (i + 1) / 8
glow = Circle(radius=radius + glow_radius * factor)
opacity = 0.3 * (1 - factor) ** 2
glow.set_stroke(color, width=2, opacity=opacity)
glow.set_fill(color, opacity=opacity * 0.3)
self.glow_layers.add(glow)
# Pulsing ring
self.ring = Circle(radius=radius * 2)
self.ring.set_stroke(color, width=1, opacity=0.5)
self.add(self.glow_layers, self.core, self.ring)
self.color = color
def pulse(self, scale_factor=1.3, run_time=1):
return AnimationGroup(
self.ring.animate.scale(scale_factor).set_opacity(0),
rate_func=smooth
)
class Spirograph(VMobject):
"""Parametric spirograph curves - mathematical beauty"""
def __init__(self, R=3, r=1, d=1.5, color=WHITE, num_points=1000, **kwargs):
super().__init__(**kwargs)
self.R, self.r, self.d = R, r, d
t = np.linspace(0, 2 * np.pi * self._lcm_period(), num_points)
# Hypotrochoid equations
x = (R - r) * np.cos(t) + d * np.cos((R - r) / r * t)
y = (R - r) * np.sin(t) - d * np.sin((R - r) / r * t)
points = [np.array([x[i], y[i], 0]) for i in range(len(t))]
self.set_points_smoothly(points)
self.set_stroke(color, width=2, opacity=0.8)
def _lcm_period(self):
from math import gcd
r_int, R_int = int(self.r * 100), int(self.R * 100)
return r_int // gcd(r_int, R_int)
class LissajousCurve(VMobject):
"""Lissajous figures - chaos and confusion"""
def __init__(self, a=3, b=4, delta=np.pi/2, color=WHITE, **kwargs):
super().__init__(**kwargs)
t = np.linspace(0, 2 * np.pi, 1000)
x = 2 * np.sin(a * t + delta)
y = 2 * np.sin(b * t)
points = [np.array([x[i], y[i], 0]) for i in range(len(t))]
self.set_points_smoothly(points)
self.set_stroke(color, width=2)
class FlowerOfLife(VGroup):
"""Sacred geometry - spiritual moments"""
def __init__(self, rings=2, radius=0.5, color=WHITE, **kwargs):
super().__init__(**kwargs)
centers = [ORIGIN]
for ring in range(1, rings + 1):
for i in range(6 * ring):
angle = i * TAU / (6 * ring)
dist = radius * ring * np.sqrt(3)
centers.append(np.array([
dist * np.cos(angle),
dist * np.sin(angle),
0
]))
for center in centers[:19]: # Classic 19 circles
circle = Circle(radius=radius, color=color)
circle.move_to(center)
circle.set_stroke(color, width=1.5, opacity=0.6)
self.add(circle)
class GoldenSpiral(VMobject):
"""Golden ratio spiral - resolution and beauty"""
def __init__(self, turns=4, color=GOLD, **kwargs):
super().__init__(**kwargs)
phi = (1 + np.sqrt(5)) / 2 # Golden ratio
t = np.linspace(0, turns * 2 * np.pi, 500)
# Logarithmic spiral with golden ratio
r = 0.1 * phi ** (t / (2 * np.pi))
x = r * np.cos(t)
y = r * np.sin(t)
points = [np.array([x[i], y[i], 0]) for i in range(len(t))]
self.set_points_smoothly(points)
self.set_stroke(color, width=3)
class FractalTree(VGroup):
"""Branching fractals - nature and growth"""
def __init__(self, depth=6, length=2, angle=25, color=GREEN, **kwargs):
super().__init__(**kwargs)
self._build_tree(ORIGIN, UP * length, depth, length, angle, color)
def _build_tree(self, start, direction, depth, length, angle, color):
if depth == 0:
return
end = start + direction
branch = Line(start, end, color=color)
branch.set_stroke(width=depth * 0.8, opacity=0.3 + 0.1 * depth)
self.add(branch)
# Recursive branches
new_length = length * 0.7
left_dir = rotate_vector(direction, angle * DEGREES) * 0.7
right_dir = rotate_vector(direction, -angle * DEGREES) * 0.7
self._build_tree(end, left_dir, depth - 1, new_length, angle, color)
self._build_tree(end, right_dir, depth - 1, new_length, angle, color)
class VoronoiFragments(VGroup):
"""Voronoi diagram - fragmentation and emptiness"""
def __init__(self, num_points=30, color=WHITE, **kwargs):
super().__init__(**kwargs)
# Generate random points
points = np.random.uniform(-4, 4, (num_points, 2))
# Create approximate Voronoi cells using circles
for point in points:
cell = Dot(point=[point[0], point[1], 0], radius=0.05, color=color)
cell.set_fill(color, opacity=0.3)
self.add(cell)
# Add connecting lines
for other in points:
if np.linalg.norm(point - other) < 2 and not np.array_equal(point, other):
mid = (point + other) / 2
angle = np.arctan2(other[1] - point[1], other[0] - point[0])
line = Line(
[mid[0] - np.sin(angle), mid[1] + np.cos(angle), 0],
[mid[0] + np.sin(angle), mid[1] - np.cos(angle), 0],
color=color
)
line.set_stroke(width=0.5, opacity=0.2)
self.add(line)
class ParticleField(VGroup):
"""Particles following mathematical fields"""
def __init__(self, num_particles=100, color=WHITE, **kwargs):
super().__init__(**kwargs)
for _ in range(num_particles):
x = random.uniform(-7, 7)
y = random.uniform(-4, 4)
size = random.uniform(0.02, 0.06)
opacity = random.uniform(0.2, 0.6)
particle = Dot(point=[x, y, 0], radius=size, color=color)
particle.set_fill(color, opacity=opacity)
self.add(particle)
class StrangeAttractor(VMobject):
"""Lorenz-like strange attractor for chaos moments"""
def __init__(self, color=RED, **kwargs):
super().__init__(**kwargs)
# Simplified attractor visualization
sigma, rho, beta = 10, 28, 8/3
dt = 0.01
x, y, z = 0.1, 0, 0
points = []
for _ in range(3000):
dx = sigma * (y - x) * dt
dy = (x * (rho - z) - y) * dt
dz = (x * y - beta * z) * dt
x, y, z = x + dx, y + dy, z + dz
points.append(np.array([x * 0.1, (z - 25) * 0.1, 0]))
self.set_points_smoothly(points)
self.set_stroke(color, width=1, opacity=0.7)
# ============================================================================
# TRACK ANIMATIONS
# ============================================================================
class Track01_Skin(Scene):
"""Alienation - "don't fit in my own skin" """
def construct(self):
# Create distorted soul trying to find its shape
soul = SoulOrb(color=SuryaColors.SKIN, radius=0.3)
self.play(FadeIn(soul, scale=0.5), run_time=2)
# Morphing spirograph representing identity struggle
spiro1 = Spirograph(R=3, r=0.7, d=1.2, color=SuryaColors.SKIN)
spiro2 = Spirograph(R=3, r=1.3, d=0.8, color=SuryaColors.SKIN)
spiro1.scale(0.8)
spiro2.scale(0.8)
self.play(Create(spiro1), run_time=3)
# Soul pulsates uncomfortably
self.play(
soul.animate.scale(0.7),
spiro1.animate.set_opacity(0.5),
run_time=1
)
self.play(
soul.animate.scale(1.3),
run_time=1
)
# Transform to different identity
self.play(
Transform(spiro1, spiro2),
soul.animate.scale(0.8),
run_time=4
)
# Particles of self floating away
particles = ParticleField(50, color=SuryaColors.SKIN)
self.play(FadeIn(particles, lag_ratio=0.1), run_time=2)
# Everything fades with lingering unease
self.play(
FadeOut(spiro1),
FadeOut(particles),
soul.animate.scale(0.5).set_opacity(0.3),
run_time=3
)
self.wait(1)
class Track02_USavedMe(Scene):
"""Hope emerges - "you saved me from my broken soul" """
def construct(self):
# Broken soul fragments
soul = SoulOrb(color=SuryaColors.SAVED, radius=0.2)
soul.set_opacity(0.5)
# Fragments scattered
fragments = VGroup(*[
Dot(point=[random.uniform(-2, 2), random.uniform(-2, 2), 0],
radius=0.05, color=SuryaColors.SAVED)
for _ in range(20)
])
self.play(FadeIn(fragments, lag_ratio=0.1), run_time=2)
# Fragments begin to coalesce
self.play(
*[f.animate.move_to(ORIGIN) for f in fragments],
run_time=3,
rate_func=smooth
)
# Soul emerges whole
self.play(
FadeOut(fragments),
FadeIn(soul, scale=2),
run_time=2
)
# Hopeful spirograph expands outward
spiro = Spirograph(R=4, r=1.5, d=2, color=SuryaColors.SAVED)
spiro.scale(0)
self.add(spiro)
self.play(
spiro.animate.scale(0.7),
soul.animate.scale(1.5).set_opacity(1),
run_time=4
)
# Sacred geometry appears - spiritual awakening
flower = FlowerOfLife(rings=2, radius=0.6, color=SuryaColors.SAVED)
flower.set_opacity(0)
self.add(flower)
self.play(
flower.animate.set_opacity(0.4),
spiro.animate.set_opacity(0.3),
run_time=3
)
# Gentle pulsing in harmony
self.play(
soul.animate.scale(1.2),
flower.animate.scale(1.1),
run_time=1.5
)
self.play(
soul.animate.scale(1/1.2),
flower.animate.scale(1/1.1),
run_time=1.5
)
self.play(
FadeOut(spiro, flower),
soul.animate.scale(0.6),
run_time=2
)
class Track03_Nothing(Scene):
"""Emptiness - "I lost my heart, now I feel nothing" """
def construct(self):
# Soul starts visible but will fade
soul = SoulOrb(color=SuryaColors.NOTHING, radius=0.3)
self.add(soul)
# Voronoi fragmentation - self breaking apart
voronoi = VoronoiFragments(num_points=25, color=SuryaColors.NOTHING)
# Soul slowly dims
self.play(
soul.animate.scale(0.7).set_opacity(0.5),
run_time=3
)
# Fragments appear
self.play(Create(voronoi), run_time=4)
# Everything becomes grey and distant
grey_circle = Circle(radius=3, color=SuryaColors.NOTHING)
grey_circle.set_stroke(width=1, opacity=0.2)
grey_circle.set_fill(SuryaColors.NOTHING, opacity=0.05)
self.play(
FadeIn(grey_circle),
soul.animate.scale(0.3).set_opacity(0.2),
voronoi.animate.set_opacity(0.1),
run_time=4
)
# Hollow pulsing - mechanical, lifeless
for _ in range(2):
self.play(
grey_circle.animate.scale(1.1),
run_time=1,
rate_func=there_and_back
)
# Static emptiness
self.wait(2)
# Fragments scatter into void
self.play(
FadeOut(voronoi, shift=UP * 2),
FadeOut(grey_circle),
soul.animate.set_opacity(0.1),
run_time=3
)
class Track04_SweetRelief(Scene):
"""Haunted - "seeing ghosts around my throat" """
def construct(self):
soul = SoulOrb(color=SuryaColors.RELIEF, radius=0.25)
self.add(soul)
# Ghost-like spiraling forms
ghosts = VGroup()
for i in range(5):
ghost = Spirograph(R=2 + i * 0.3, r=0.5, d=1 + i * 0.2,
color=SuryaColors.RELIEF)
ghost.scale(0.5)
ghost.set_opacity(0.1 + i * 0.05)
ghost.rotate(i * PI / 5)
ghosts.add(ghost)
self.play(
*[Create(g) for g in ghosts],
run_time=4,
lag_ratio=0.3
)
# Ghosts circle the soul menacingly
self.play(
Rotate(ghosts, PI, about_point=ORIGIN),
soul.animate.scale(0.8),
run_time=4
)
# Soul struggles
self.play(
soul.animate.shift(UP * 0.3),
run_time=0.5
)
self.play(
soul.animate.shift(DOWN * 0.3),
run_time=0.5
)
# Ghosts close in
self.play(
ghosts.animate.scale(0.7),
soul.animate.set_opacity(0.6),
run_time=3
)
# Brief moment of relief (ghosts recede slightly)
self.play(
ghosts.animate.scale(1.3).set_opacity(0.1),
soul.animate.scale(1.2).set_opacity(1),
run_time=2
)
self.play(
FadeOut(ghosts),
run_time=2
)
class Track05_Tiptoe(Scene):
"""Tense, cautious movement"""
def construct(self):
soul = SoulOrb(color=SuryaColors.RELIEF, radius=0.2)
soul.shift(LEFT * 4)
self.add(soul)
# Careful path - Lissajous representing caution
path = LissajousCurve(a=3, b=2, delta=PI/4, color=SuryaColors.RELIEF)
path.set_opacity(0.3)
self.play(Create(path), run_time=2)
# Soul moves carefully along path
# Small, hesitant steps
positions = [LEFT * 3, LEFT * 2, LEFT * 1, ORIGIN, RIGHT * 1, RIGHT * 2, RIGHT * 3]
for pos in positions:
self.play(
soul.animate.move_to(pos),
run_time=0.8,
rate_func=smooth
)
# Pause - checking surroundings
self.wait(0.3)
# Tension builds - path becomes more complex
path2 = LissajousCurve(a=5, b=4, delta=PI/3, color=SuryaColors.RELIEF)
path2.set_opacity(0.2)
self.play(
Transform(path, path2),
soul.animate.move_to(ORIGIN),
run_time=3
)
# Soul retreats to center, bracing
self.play(
soul.animate.scale(0.7),
path.animate.set_opacity(0.1),
run_time=2
)
self.play(FadeOut(path), run_time=1)
class Track06_NaturesCall(Scene):
"""Peaceful interlude - "thank you for joining us" """
def construct(self):
soul = SoulOrb(color=SuryaColors.NATURE, radius=0.3)
self.play(FadeIn(soul), run_time=2)
# Fractal tree grows - nature emerging
tree = FractalTree(depth=6, length=1.5, color=SuryaColors.NATURE)
tree.shift(DOWN * 2)
tree.set_opacity(0)
self.add(tree)
self.play(
tree.animate.set_opacity(0.6),
run_time=5
)
# Particles like leaves/pollen floating up
particles = VGroup()
for _ in range(50):
p = Dot(
point=[random.uniform(-4, 4), random.uniform(-3, -2), 0],
radius=random.uniform(0.02, 0.05),
color=SuryaColors.NATURE
)
p.set_opacity(random.uniform(0.3, 0.7))
particles.add(p)
self.play(FadeIn(particles, lag_ratio=0.05), run_time=2)
# Particles float upward peacefully
self.play(
*[p.animate.shift(UP * random.uniform(4, 6)) for p in particles],
run_time=6,
rate_func=smooth
)
# Golden ratio spiral emerges - nature's mathematics
spiral = GoldenSpiral(turns=3, color=SuryaColors.NATURE)
spiral.scale(0.3)
spiral.set_opacity(0)
self.play(
spiral.animate.set_opacity(0.5).scale(2),
soul.animate.scale(1.3),
run_time=4
)
# Peaceful conclusion
self.play(
tree.animate.set_opacity(0.2),
spiral.animate.set_opacity(0.2),
run_time=3
)
self.play(
FadeOut(tree, spiral, particles),
run_time=2
)
class Track07_Dreamcatcher(Scene):
"""Falling - "falling through the cracks" """
def construct(self):
soul = SoulOrb(color=SuryaColors.DREAM, radius=0.3)
soul.shift(UP * 3)
self.play(FadeIn(soul), run_time=1)
# Dreamcatcher web - sacred geometry
web = VGroup()
for i in range(6):
angle = i * PI / 3
line = Line(ORIGIN, 3 * np.array([np.cos(angle), np.sin(angle), 0]))
line.set_stroke(SuryaColors.DREAM, width=1, opacity=0.4)
web.add(line)
for r in [0.5, 1, 1.5, 2, 2.5]:
circle = Circle(radius=r, color=SuryaColors.DREAM)
circle.set_stroke(width=1, opacity=0.3)
web.add(circle)
self.play(Create(web), run_time=3)
# Soul begins to fall through the web
self.play(
soul.animate.shift(DOWN * 2),
run_time=2,
rate_func=rate_functions.ease_in_quad
)
# Passing through layers - web pulses
for _ in range(3):
self.play(
web.animate.scale(1.1).set_opacity(0.2),
run_time=0.5
)
self.play(
web.animate.scale(1/1.1).set_opacity(0.4),
run_time=0.5
)
self.play(
soul.animate.shift(DOWN * 1),
run_time=1.5
)
# Soul falls through completely
self.play(
soul.animate.shift(DOWN * 2).scale(0.5).set_opacity(0.4),
web.animate.set_opacity(0.1),
run_time=3,
rate_func=rate_functions.ease_in_quad
)
self.play(FadeOut(web), run_time=1)
class Track08_IDK(Scene):
"""Confusion - "drugs don't work on me no more" """
def construct(self):
soul = SoulOrb(color=SuryaColors.IDK, radius=0.25)
soul.set_opacity(0.6)
self.add(soul)
# Strange attractor - chaos and confusion
attractor = StrangeAttractor(color=SuryaColors.IDK)
attractor.scale(0.5)
self.play(Create(attractor), run_time=5)
# Soul wanders erratically
random_points = [
np.array([random.uniform(-2, 2), random.uniform(-1.5, 1.5), 0])
for _ in range(8)
]
for point in random_points:
self.play(
soul.animate.move_to(point),
run_time=0.6,
rate_func=rate_functions.ease_in_out_sine
)
# Multiple Lissajous curves - mind racing
curves = VGroup()
for i in range(4):
curve = LissajousCurve(
a=random.randint(2, 5),
b=random.randint(2, 5),
delta=random.uniform(0, PI),
color=SuryaColors.IDK
)
curve.scale(0.5 + i * 0.2)
curve.set_opacity(0.2)
curves.add(curve)
self.play(
*[Create(c) for c in curves],
run_time=3,
lag_ratio=0.2
)
# Everything spins in confusion
self.play(
Rotate(curves, PI, about_point=ORIGIN),
soul.animate.move_to(ORIGIN).scale(0.8),
run_time=4
)
# Collapse into uncertainty
self.play(
FadeOut(attractor, curves),
soul.animate.scale(0.5).set_opacity(0.4),
run_time=3
)
class Track09_WithU(Scene):
"""THE TURN - "floating through the stars" - Two souls meet"""
def construct(self):
# Primary soul
soul1 = SoulOrb(color=WHITE, radius=0.3)
soul1.shift(LEFT * 3)
# Second soul appears - golden warmth
soul2 = SoulOrb(color=SuryaColors.WITH_U, radius=0.25)
soul2.shift(RIGHT * 5)
soul2.set_opacity(0)
self.play(FadeIn(soul1), run_time=2)
# Stars begin appearing
stars = VGroup()
for _ in range(80):
star = Dot(
point=[random.uniform(-7, 7), random.uniform(-4, 4), 0],
radius=random.uniform(0.01, 0.04),
color=WHITE
)
star.set_opacity(0)
stars.add(star)
self.add(stars)
self.play(
*[s.animate.set_opacity(random.uniform(0.3, 0.9)) for s in stars],
run_time=3,
lag_ratio=0.02
)
# Second soul emerges from the stars
self.play(
soul2.animate.set_opacity(1).shift(LEFT * 2),
run_time=3
)
# Souls move toward each other
self.play(
soul1.animate.shift(RIGHT * 1.5),
soul2.animate.shift(LEFT * 1.5),
run_time=3,
rate_func=smooth
)
# They begin orbiting each other - binary star system
orbit_center = (soul1.get_center() + soul2.get_center()) / 2
# Golden spiral emerges between them
spiral = GoldenSpiral(turns=4, color=SuryaColors.WITH_U)
spiral.scale(0.4)
spiral.move_to(orbit_center)
spiral.set_opacity(0)
self.play(spiral.animate.set_opacity(0.5), run_time=2)
# Orbiting animation
for _ in range(2):
self.play(
Rotate(soul1, PI, about_point=orbit_center),
Rotate(soul2, PI, about_point=orbit_center),
Rotate(spiral, PI/2, about_point=orbit_center),
run_time=4,
rate_func=smooth
)
# Souls move closer, colors blend
self.play(
soul1.animate.scale(1.2).move_to(orbit_center + LEFT * 0.3),
soul2.animate.scale(1.2).move_to(orbit_center + RIGHT * 0.3),
spiral.animate.scale(1.5),
run_time=3
)
# Radiant climax - sacred geometry
flower = FlowerOfLife(rings=2, radius=0.5, color=SuryaColors.WITH_U)
flower.move_to(orbit_center)
flower.set_opacity(0)
self.play(
flower.animate.set_opacity(0.4).scale(1.2),
stars.animate.set_opacity(0.8),
run_time=3
)
# Final pulse of connection
self.play(
soul1.animate.scale(1.3),
soul2.animate.scale(1.3),
flower.animate.scale(1.3),
run_time=1.5
)
self.play(
soul1.animate.scale(1/1.3),
soul2.animate.scale(1/1.3),
flower.animate.scale(1/1.3),
run_time=1.5
)
# Gentle fade maintaining connection
self.play(
flower.animate.set_opacity(0.1),
spiral.animate.set_opacity(0.2),
stars.animate.set_opacity(0.3),
run_time=3
)
class Track10_PoorYouPoorMe(Scene):
"""Bittersweet - "you left your cardigan on my bed" """
def construct(self):
# Two souls, but one is fading
soul1 = SoulOrb(color=SuryaColors.POOR, radius=0.3)
soul2 = SoulOrb(color=SuryaColors.WITH_U, radius=0.25)
soul2.shift(RIGHT * 1.5)
self.play(FadeIn(soul1, soul2), run_time=2)
# Spirograph of intertwined paths
spiro = Spirograph(R=3, r=1.8, d=1.5, color=SuryaColors.POOR)
spiro.scale(0.6)
spiro.set_opacity(0.4)
self.play(Create(spiro), run_time=4)
# One soul begins to drift away
self.play(
soul2.animate.shift(RIGHT * 2).set_opacity(0.6),
run_time=3
)
# Lingering warmth - particles like memories
memories = VGroup()
for _ in range(30):
m = Dot(
point=soul2.get_center() + np.array([
random.uniform(-1, 1),
random.uniform(-1, 1),
0
]),
radius=0.03,
color=SuryaColors.WITH_U
)
m.set_opacity(0.5)
memories.add(m)
self.play(FadeIn(memories), run_time=2)
# Memories drift toward remaining soul
self.play(
*[m.animate.move_to(
soul1.get_center() + np.array([random.uniform(-0.5, 0.5), random.uniform(-0.5, 0.5), 0])
) for m in memories],
soul2.animate.shift(RIGHT * 2).set_opacity(0.2),
run_time=4
)
# Bittersweet pulse
self.play(
soul1.animate.scale(1.2),
spiro.animate.set_opacity(0.6),
run_time=2
)
self.play(
soul1.animate.scale(1/1.2),
spiro.animate.set_opacity(0.3),
run_time=2
)
self.play(
FadeOut(soul2, memories, spiro),
run_time=3
)
class Track11_Wait4U(Scene):
"""Longing - waiting"""
def construct(self):
soul = SoulOrb(color=SuryaColors.WAIT, radius=0.3)
self.play(FadeIn(soul), run_time=2)
# Concentric circles - time passing, waiting
circles = VGroup()
for i in range(8):
c = Circle(radius=0.5 + i * 0.4, color=SuryaColors.WAIT)
c.set_stroke(width=1, opacity=0.3 - i * 0.03)
circles.add(c)
self.play(
*[Create(c) for c in circles],
run_time=3,
lag_ratio=0.3
)
# Slow pulsing - patient waiting
for _ in range(3):
self.play(
circles.animate.scale(1.1),
soul.animate.scale(0.9),
run_time=2
)
self.play(
circles.animate.scale(1/1.1),
soul.animate.scale(1/0.9),
run_time=2
)
# Spiral of longing
spiral = GoldenSpiral(turns=5, color=SuryaColors.WAIT)
spiral.scale(0.3)
spiral.set_opacity(0)
self.play(
spiral.animate.set_opacity(0.4).scale(2),
circles.animate.set_opacity(0.1),
run_time=4
)
self.play(
FadeOut(circles, spiral),
run_time=2
)
class Track12_RunToU(Scene):
"""Urgency - "if the sky was falling I would run to you" """
def construct(self):
# Soul starts at edge, urgent energy
soul = SoulOrb(color=SuryaColors.RUN, radius=0.3)
soul.shift(LEFT * 5)
# Target - the other soul (faint in distance)
target = SoulOrb(color=SuryaColors.WITH_U, radius=0.25)
target.shift(RIGHT * 5)
target.set_opacity(0.3)
self.play(FadeIn(soul, target), run_time=1)
# Streaking particles - speed lines
speed_lines = VGroup()
for _ in range(20):
y = random.uniform(-3, 3)
line = Line(LEFT * 7, RIGHT * 7, color=SuryaColors.RUN)
line.shift(UP * y)
line.set_stroke(width=1, opacity=0.2)
speed_lines.add(line)
self.play(
*[Create(l) for l in speed_lines],
run_time=1
)
# Running motion - urgent spirograph
spiro = Spirograph(R=2, r=0.3, d=2, color=SuryaColors.RUN)
spiro.scale(0.3)
spiro.move_to(soul.get_center())
spiro.set_opacity(0)
# Urgent movement toward target
self.play(
soul.animate.shift(RIGHT * 4),
spiro.animate.shift(RIGHT * 4).set_opacity(0.5),
target.animate.set_opacity(0.6),
Rotate(spiro, TAU * 2, about_point=soul.get_center()),
run_time=3,
rate_func=rate_functions.ease_in_quad
)
# Final push
self.play(
soul.animate.shift(RIGHT * 4),
target.animate.shift(LEFT * 1).set_opacity(1),
speed_lines.animate.set_opacity(0.5),
run_time=2,
rate_func=rate_functions.ease_out_quad
)
# Collision/embrace - burst of energy
burst = VGroup()
for i in range(12):
angle = i * PI / 6
ray = Line(ORIGIN, 2 * np.array([np.cos(angle), np.sin(angle), 0]))
ray.set_stroke(SuryaColors.RUN, width=3, opacity=0.6)
burst.add(ray)
burst.move_to(RIGHT * 2)
self.play(
FadeIn(burst, scale=0.5),
soul.animate.move_to(RIGHT * 2).scale(1.3),
target.animate.move_to(RIGHT * 2).scale(1.3),
run_time=1
)
self.play(
burst.animate.scale(2).set_opacity(0),
FadeOut(speed_lines, spiro),
run_time=2
)
class Track13_Medications(Scene):
"""Struggle - "raging in my head" """
def construct(self):
soul = SoulOrb(color=SuryaColors.MEDS, radius=0.3)
self.play(FadeIn(soul), run_time=1)
# Strange attractor - chaos in the mind
attractor = StrangeAttractor(color=SuryaColors.MEDS)
attractor.scale(0.6)
self.play(Create(attractor), run_time=3)
# Aggressive spirograph - war in the head
spiros = VGroup()
for i in range(3):
s = Spirograph(R=2 + i, r=0.5 + i * 0.3, d=1.5, color=SuryaColors.MEDS)
s.scale(0.4)
s.set_opacity(0.3)
s.rotate(i * PI / 3)
spiros.add(s)
self.play(
*[Create(s) for s in spiros],
run_time=3
)
# Soul shakes violently
original_pos = soul.get_center()
for _ in range(6):
offset = np.array([random.uniform(-0.3, 0.3), random.uniform(-0.3, 0.3), 0])
self.play(
soul.animate.move_to(original_pos + offset),
run_time=0.15
)
self.play(soul.animate.move_to(original_pos), run_time=0.2)
# Red pulses - pain
for _ in range(3):
pulse = Circle(radius=0.5, color=SuryaColors.MEDS)
pulse.set_stroke(width=3, opacity=0.8)
self.add(pulse)
self.play(
pulse.animate.scale(4).set_opacity(0),
spiros.animate.rotate(PI/6),
run_time=1
)
self.remove(pulse)
# Struggle continues
self.play(
Rotate(attractor, PI, about_point=ORIGIN),
spiros.animate.set_opacity(0.5),
soul.animate.scale(0.8),
run_time=3
)
# Eventual exhaustion
self.play(
attractor.animate.set_opacity(0.2),
spiros.animate.set_opacity(0.1).scale(0.8),
soul.animate.scale(0.7).set_opacity(0.6),
run_time=3
)
self.play(FadeOut(attractor, spiros), run_time=2)
class Track14_Hollow(Scene):
"""Resolution - "you took my sorrow and flew it to the moon" """
def construct(self):
# Soul begins small and humble
soul = SoulOrb(color=SuryaColors.HOLLOW, radius=0.2)
soul.set_opacity(0.5)
self.play(FadeIn(soul), run_time=2)
# Stars return - hope
stars = VGroup()
for _ in range(100):
star = Dot(
point=[random.uniform(-7, 7), random.uniform(-4, 4), 0],
radius=random.uniform(0.01, 0.05),
color=WHITE
)
star.set_opacity(0)
stars.add(star)
self.add(stars)
self.play(
*[s.animate.set_opacity(random.uniform(0.2, 0.8)) for s in stars],
soul.animate.set_opacity(0.8).scale(1.2),
run_time=4,
lag_ratio=0.01
)
# Golden spiral - resolution through golden ratio
spiral = GoldenSpiral(turns=5, color=SuryaColors.HOLLOW)
spiral.scale(0.2)
spiral.set_opacity(0)
self.play(
spiral.animate.set_opacity(0.6).scale(3),
run_time=5
)
# Moon appears (large circle in upper area)
moon = Circle(radius=1.5, color=SuryaColors.HOLLOW)
moon.set_fill(SuryaColors.HOLLOW, opacity=0.1)
moon.set_stroke(SuryaColors.HOLLOW, width=2, opacity=0.5)
moon.shift(UP * 2 + RIGHT * 3)
self.play(FadeIn(moon, scale=0.5), run_time=3)
# Sorrow (dark particles) flying to the moon
sorrow = VGroup()
for _ in range(20):
s = Dot(
point=soul.get_center() + np.array([
random.uniform(-0.5, 0.5),
random.uniform(-0.5, 0.5),
0
]),
radius=0.04,
color=GREY
)
sorrow.add(s)
self.play(FadeIn(sorrow), run_time=1)
# Sorrow flies to moon
self.play(
*[s.animate.move_to(moon.get_center() + np.array([
random.uniform(-0.5, 0.5),
random.uniform(-0.5, 0.5),
0
])).set_opacity(0) for s in sorrow],
soul.animate.scale(1.3).set_opacity(1),
run_time=4
)
# Sacred geometry finale
flower = FlowerOfLife(rings=2, radius=0.6, color=SuryaColors.HOLLOW)
flower.set_opacity(0)
self.play(
flower.animate.set_opacity(0.4),
spiral.animate.set_opacity(0.8),
run_time=3
)
# Final radiant pulse
self.play(
soul.animate.scale(1.5),
flower.animate.scale(1.3),
spiral.animate.scale(1.2),
stars.animate.set_opacity(1),
run_time=2
)
# Peaceful hold
self.wait(3)
# Gentle fade to completion
self.play(
soul.animate.scale(2).set_opacity(0.5),
flower.animate.set_opacity(0.2),
spiral.animate.set_opacity(0.3),
stars.animate.set_opacity(0.5),
moon.animate.set_opacity(0.3),
run_time=4
)
# ============================================================================
# MAIN JOURNEY - FULL COMPOSITION
# ============================================================================
class SuryaJourney(Scene):
"""
SURYA — The Complete Journey Through Feeling
14 tracks of mathematical beauty
"""
def construct(self):
# Persistent elements
self.soul = SoulOrb(color=WHITE, radius=0.3)
self.stars = self.create_stars()
self.particles = self.create_particles()
# Journey begins
self.intro()
self.track_01_skin()
self.track_02_u_saved_me()
self.track_03_nothing()
self.track_04_sweet_relief()
self.track_05_tiptoe()
self.track_06_natures_call()
self.track_07_dreamcatcher()
self.track_08_idk()
self.track_09_with_u()
self.track_10_poor_you_poor_me()
self.track_11_wait_4_u()
self.track_12_run_to_u()
self.track_13_medications()
self.track_14_hollow()
self.finale()
def create_stars(self):
stars = VGroup()
for _ in range(100):
star = Dot(
point=[random.uniform(-7, 7), random.uniform(-4, 4), 0],
radius=random.uniform(0.01, 0.05),
color=WHITE
)
star.set_opacity(0)
stars.add(star)
return stars
def create_particles(self):
particles = VGroup()
for _ in range(60):
p = Dot(
point=[random.uniform(-7, 7), random.uniform(-5, -4), 0],
radius=random.uniform(0.02, 0.05),
color=WHITE
)
p.set_opacity(0)
particles.add(p)
return particles
def transition_color(self, new_color, run_time=2):
"""Smoothly transition soul and ambient elements to new color"""
new_soul = SoulOrb(color=new_color, radius=self.soul.core.radius * 5)
new_soul.move_to(self.soul.get_center())
self.play(
Transform(self.soul, new_soul),
run_time=run_time
)
def show_title(self, number, title, lyric="", color=WHITE):
"""Display track title"""
num_text = Text(f"{number:02d}", font_size=24, color=color)
num_text.set_opacity(0.4)
num_text.to_edge(UP, buff=1)
title_text = Text(title, font_size=72, color=color, slant=ITALIC)
title_text.next_to(num_text, DOWN, buff=0.5)
lyric_text = Text(lyric, font_size=28, color=color, slant=ITALIC)
lyric_text.set_opacity(0.6)
lyric_text.next_to(title_text, DOWN, buff=0.5)
group = VGroup(num_text, title_text, lyric_text)
self.play(
FadeIn(group, shift=UP * 0.5),
run_time=1.5
)
self.wait(1)
self.play(
FadeOut(group, shift=UP * 0.5),
run_time=1
)
# =========== INTRO ===========
def intro(self):
# Title sequence
surya_text = Text("SURYA", font_size=120, color=WHITE)
sub_text = Text("a journey through feeling", font_size=32, color=WHITE)
sub_text.set_opacity(0.5)
sub_text.next_to(surya_text, DOWN, buff=0.5)
artist_text = Text("DAS", font_size=20, color=WHITE)
artist_text.set_opacity(0.3)
artist_text.next_to(sub_text, DOWN, buff=1)
self.play(
FadeIn(surya_text, scale=0.9),
run_time=3
)
self.play(FadeIn(sub_text, artist_text), run_time=2)
self.wait(2)
# Soul emerges
self.add(self.stars, self.particles)
self.play(
FadeIn(self.soul, scale=0.5),
FadeOut(surya_text, sub_text, artist_text),
run_time=3
)
self.wait(1)
# =========== TRACK 01: SKIN ===========
def track_01_skin(self):
color = SuryaColors.SKIN
self.show_title(1, "skin", '"don\'t fit in my own skin"', color)
# Transform soul color
self.transition_color(color)
# Morphing spirograph - identity struggle
spiro1 = Spirograph(R=3, r=0.7, d=1.2, color=color)
spiro2 = Spirograph(R=3, r=1.3, d=0.8, color=color)
spiro1.scale(0.8)
spiro2.scale(0.8)
self.play(Create(spiro1), run_time=3)
# Soul pulsates uncomfortably
self.play(
self.soul.animate.scale(0.7),
run_time=1
)
self.play(
self.soul.animate.scale(1.4),
run_time=1
)
# Transform identity
self.play(
Transform(spiro1, spiro2),
self.soul.animate.scale(0.8),
run_time=4
)
# Particles of self floating
temp_particles = ParticleField(40, color=color)
self.play(FadeIn(temp_particles, lag_ratio=0.05), run_time=2)
self.play(
temp_particles.animate.shift(UP * 2).set_opacity(0),
run_time=3
)
# Fade out
self.play(
FadeOut(spiro1, temp_particles),
self.soul.animate.scale(0.8),
run_time=2
)
# =========== TRACK 02: U SAVED ME ===========
def track_02_u_saved_me(self):
color = SuryaColors.SAVED
self.show_title(2, "u saved me", '"you saved me from my broken soul"', color)
# Soul fragments scattered
fragments = VGroup(*[
Dot(point=[random.uniform(-2, 2), random.uniform(-2, 2), 0],
radius=0.05, color=color)
for _ in range(25)
])
self.play(
FadeIn(fragments, lag_ratio=0.05),
self.soul.animate.set_opacity(0.3),
run_time=2
)
# Fragments coalesce
self.play(
*[f.animate.move_to(self.soul.get_center()) for f in fragments],
run_time=3
)
self.transition_color(color)
self.play(
FadeOut(fragments),
self.soul.animate.set_opacity(1).scale(1.3),
run_time=2
)
# Sacred geometry - spiritual awakening
flower = FlowerOfLife(rings=2, radius=0.6, color=color)
flower.set_opacity(0)
self.add(flower)
# Hopeful spirograph
spiro = Spirograph(R=4, r=1.5, d=2, color=color)
spiro.scale(0.7)
spiro.set_opacity(0)
self.add(spiro)
self.play(
flower.animate.set_opacity(0.4),
spiro.animate.set_opacity(0.5),
run_time=4
)
# Harmony pulse
self.play(
self.soul.animate.scale(1.2),
flower.animate.scale(1.1),
run_time=1.5
)
self.play(
self.soul.animate.scale(1/1.2),
flower.animate.scale(1/1.1),
run_time=1.5
)
self.play(
FadeOut(flower, spiro),
run_time=2
)
# =========== TRACK 03: NOTHING ===========
def track_03_nothing(self):
color = SuryaColors.NOTHING
self.show_title(3, "nothing", '"I lost my heart, now I feel nothing"', color)
self.transition_color(color)
# Voronoi fragmentation
voronoi = VoronoiFragments(num_points=25, color=color)
self.play(
self.soul.animate.scale(0.6),
run_time=2
)
self.play(Create(voronoi), run_time=4)
# Grey void
grey_circle = Circle(radius=3, color=color)
grey_circle.set_stroke(width=1, opacity=0.2)
grey_circle.set_fill(color, opacity=0.03)
self.play(
FadeIn(grey_circle),
self.soul.animate.scale(0.5).set_opacity(0.3),
voronoi.animate.set_opacity(0.1),
run_time=4
)
# Hollow pulsing
for _ in range(2):
self.play(
grey_circle.animate.scale(1.1),
run_time=1.5,
rate_func=there_and_back
)
self.wait(1)
# Scatter into void
self.play(
FadeOut(voronoi, shift=UP),
FadeOut(grey_circle),
self.soul.animate.set_opacity(0.2),
run_time=3
)
# =========== TRACK 04: SWEET RELIEF ===========
def track_04_sweet_relief(self):
color = SuryaColors.RELIEF
self.show_title(4, "sweet relief", '"seeing ghosts around my throat"', color)
self.transition_color(color)
self.play(self.soul.animate.set_opacity(0.8).scale(1.5), run_time=1)
# Ghost spirals
ghosts = VGroup()
for i in range(5):
ghost = Spirograph(R=2 + i * 0.3, r=0.5, d=1 + i * 0.2, color=color)
ghost.scale(0.5)
ghost.set_opacity(0.15 + i * 0.05)
ghost.rotate(i * PI / 5)
ghosts.add(ghost)
self.play(
*[Create(g) for g in ghosts],
run_time=4,
lag_ratio=0.3
)
# Ghosts circle menacingly
self.play(
Rotate(ghosts, PI, about_point=ORIGIN),
self.soul.animate.scale(0.8),
run_time=4
)
# Soul struggles
self.play(self.soul.animate.shift(UP * 0.3), run_time=0.4)
self.play(self.soul.animate.shift(DOWN * 0.3), run_time=0.4)
# Ghosts close in
self.play(
ghosts.animate.scale(0.7),
run_time=3
)
# Brief relief
self.play(
ghosts.animate.scale(1.5).set_opacity(0.05),
self.soul.animate.scale(1.2).set_opacity(1),
run_time=2
)
self.play(FadeOut(ghosts), run_time=2)
# =========== TRACK 05: TIPTOE ===========
def track_05_tiptoe(self):
color = SuryaColors.RELIEF
self.show_title(5, "tiptoe", "", color)
# Lissajous path
path = LissajousCurve(a=3, b=2, delta=PI/4, color=color)
path.set_opacity(0.3)
self.play(Create(path), run_time=2)
# Careful movement
self.soul.move_to(LEFT * 3)
positions = [LEFT * 2, LEFT * 1, ORIGIN, RIGHT * 1, RIGHT * 2]
for pos in positions:
self.play(
self.soul.animate.move_to(pos),
run_time=0.8
)
self.wait(0.2)
# Tension path
path2 = LissajousCurve(a=5, b=4, delta=PI/3, color=color)
path2.set_opacity(0.2)
self.play(
Transform(path, path2),
self.soul.animate.move_to(ORIGIN).scale(0.8),
run_time=3
)
self.play(FadeOut(path), run_time=1)
# =========== TRACK 06: NATURE'S CALL ===========
def track_06_natures_call(self):
color = SuryaColors.NATURE
self.show_title(6, "nature's call", '"thank you for joining us"', color)
self.transition_color(color)
self.play(self.soul.animate.scale(1.3), run_time=1)
# Fractal tree grows
tree = FractalTree(depth=6, length=1.5, color=color)
tree.shift(DOWN * 2)
tree.set_opacity(0)
self.add(tree)
self.play(
tree.animate.set_opacity(0.5),
run_time=5
)
# Nature particles float up
nature_particles = VGroup()
for _ in range(40):
p = Dot(
point=[random.uniform(-4, 4), random.uniform(-3, -2), 0],
radius=random.uniform(0.02, 0.05),
color=color
)
p.set_opacity(random.uniform(0.3, 0.6))
nature_particles.add(p)
self.play(FadeIn(nature_particles, lag_ratio=0.03), run_time=2)
self.play(
*[p.animate.shift(UP * random.uniform(4, 6)) for p in nature_particles],
run_time=5
)
# Golden spiral - nature's math
spiral = GoldenSpiral(turns=3, color=color)
spiral.scale(0.3)
spiral.set_opacity(0)
self.play(
spiral.animate.set_opacity(0.5).scale(2),
run_time=4
)
self.play(
FadeOut(tree, spiral, nature_particles),
run_time=2
)
# =========== TRACK 07: DREAMCATCHER ===========
def track_07_dreamcatcher(self):
color = SuryaColors.DREAM
self.show_title(7, "dreamcatcher", '"falling through the cracks"', color)
self.transition_color(color)
self.soul.move_to(UP * 2.5)
# Dreamcatcher web
web = VGroup()
for i in range(6):
angle = i * PI / 3
line = Line(ORIGIN, 3 * np.array([np.cos(angle), np.sin(angle), 0]))
line.set_stroke(color, width=1, opacity=0.4)
web.add(line)
for r in [0.5, 1, 1.5, 2, 2.5]:
circle = Circle(radius=r, color=color)
circle.set_stroke(width=1, opacity=0.3)
web.add(circle)
self.play(Create(web), run_time=3)
# Soul falls through
self.play(
self.soul.animate.shift(DOWN * 2),
run_time=2,
rate_func=rate_functions.ease_in_quad
)
# Passing through layers
for _ in range(2):
self.play(
web.animate.scale(1.1).set_opacity(0.2),
run_time=0.5
)
self.play(
web.animate.scale(1/1.1).set_opacity(0.4),
run_time=0.5
)
self.play(
self.soul.animate.shift(DOWN * 1.2),
run_time=1.5
)
# Falls through completely
self.play(
self.soul.animate.move_to(ORIGIN).scale(0.7).set_opacity(0.5),
web.animate.set_opacity(0.1),
run_time=3
)
self.play(FadeOut(web), run_time=1)
# =========== TRACK 08: IDK ===========
def track_08_idk(self):
color = SuryaColors.IDK
self.show_title(8, "idk", '"drugs don\'t work on me no more"', color)
self.transition_color(color)
self.play(self.soul.animate.set_opacity(0.7).scale(1.2), run_time=1)
# Strange attractor - chaos
attractor = StrangeAttractor(color=color)
attractor.scale(0.5)
self.play(Create(attractor), run_time=4)
# Erratic wandering
for _ in range(6):
point = np.array([random.uniform(-1.5, 1.5), random.uniform(-1, 1), 0])
self.play(
self.soul.animate.move_to(point),
run_time=0.5
)
# Multiple confused curves
curves = VGroup()
for i in range(3):
curve = LissajousCurve(
a=random.randint(2, 5),
b=random.randint(2, 5),
delta=random.uniform(0, PI),
color=color
)
curve.scale(0.5 + i * 0.2)
curve.set_opacity(0.2)
curves.add(curve)
self.play(
*[Create(c) for c in curves],
run_time=3,
lag_ratio=0.2
)
# Spin in confusion
self.play(
Rotate(curves, PI, about_point=ORIGIN),
self.soul.animate.move_to(ORIGIN).scale(0.8),
run_time=4
)
# Collapse
self.play(
FadeOut(attractor, curves),
self.soul.animate.scale(0.7).set_opacity(0.5),
run_time=3
)
# =========== TRACK 09: WITH U (THE TURN) ===========
def track_09_with_u(self):
color = SuryaColors.WITH_U
self.show_title(9, "with u", '"floating through the stars"', color)
# This is THE TURN - second soul appears
self.play(self.soul.animate.shift(LEFT * 2).set_opacity(1).scale(1.3), run_time=2)
self.transition_color(WHITE)
# Stars light up
self.play(
*[s.animate.set_opacity(random.uniform(0.3, 0.9)) for s in self.stars],
run_time=3,
lag_ratio=0.01
)
# Second soul emerges - golden
soul2 = SoulOrb(color=color, radius=0.25)
soul2.shift(RIGHT * 5)
soul2.set_opacity(0)
self.add(soul2)
self.play(
soul2.animate.set_opacity(1).shift(LEFT * 3),
run_time=3
)
# Souls approach each other
self.play(
self.soul.animate.shift(RIGHT * 0.5),
soul2.animate.shift(LEFT * 0.5),
run_time=2
)
# Golden spiral between them
orbit_center = (self.soul.get_center() + soul2.get_center()) / 2
spiral = GoldenSpiral(turns=4, color=color)
spiral.scale(0.4)
spiral.move_to(orbit_center)
spiral.set_opacity(0)
self.play(spiral.animate.set_opacity(0.6), run_time=2)
# Binary orbit
for _ in range(2):
self.play(
Rotate(self.soul, PI, about_point=orbit_center),
Rotate(soul2, PI, about_point=orbit_center),
Rotate(spiral, PI/2, about_point=orbit_center),
run_time=4
)
# Closer, colors blend
self.play(
self.soul.animate.scale(1.2).move_to(orbit_center + LEFT * 0.3),
soul2.animate.scale(1.2).move_to(orbit_center + RIGHT * 0.3),
spiral.animate.scale(1.5),
run_time=3
)
# Sacred geometry climax
flower = FlowerOfLife(rings=2, radius=0.5, color=color)
flower.move_to(orbit_center)
flower.set_opacity(0)
self.play(
flower.animate.set_opacity(0.4).scale(1.2),
self.stars.animate.set_opacity(0.9),
run_time=3
)
# Peak pulse
self.play(
self.soul.animate.scale(1.3),
soul2.animate.scale(1.3),
flower.animate.scale(1.3),
run_time=1.5
)
self.play(
self.soul.animate.scale(1/1.3),
soul2.animate.scale(1/1.3),
flower.animate.scale(1/1.3),
run_time=1.5
)
# Store soul2 for later
self.soul2 = soul2
self.play(
flower.animate.set_opacity(0.1),
spiral.animate.set_opacity(0.2),
run_time=2
)
self.play(FadeOut(flower, spiral), run_time=1)
# =========== TRACK 10: POOR YOU POOR ME ===========
def track_10_poor_you_poor_me(self):
color = SuryaColors.POOR
self.show_title(10, "poor you poor me", '"you left your cardigan on my bed"', color)
self.transition_color(color)
# Bittersweet spirograph
spiro = Spirograph(R=3, r=1.8, d=1.5, color=color)
spiro.scale(0.6)
spiro.set_opacity(0.4)
self.play(Create(spiro), run_time=3)
# One soul drifts away
self.play(
self.soul2.animate.shift(RIGHT * 3).set_opacity(0.4),
run_time=4
)
# Memories drift
memories = VGroup()
for _ in range(20):
m = Dot(
point=self.soul2.get_center() + np.array([
random.uniform(-0.5, 0.5),
random.uniform(-0.5, 0.5),
0
]),
radius=0.03,
color=SuryaColors.WITH_U
)
m.set_opacity(0.5)
memories.add(m)
self.play(FadeIn(memories), run_time=1)
self.play(
*[m.animate.move_to(
self.soul.get_center() + np.array([random.uniform(-0.3, 0.3), random.uniform(-0.3, 0.3), 0])
) for m in memories],
self.soul2.animate.shift(RIGHT * 2).set_opacity(0.1),
run_time=4
)
# Bittersweet pulse
self.play(
self.soul.animate.scale(1.2),
spiro.animate.set_opacity(0.6),
run_time=2
)
self.play(
self.soul.animate.scale(1/1.2),
spiro.animate.set_opacity(0.3),
run_time=2
)
self.play(
FadeOut(self.soul2, memories, spiro),
run_time=2
)
# =========== TRACK 11: WAIT 4 U ===========
def track_11_wait_4_u(self):
color = SuryaColors.WAIT
self.show_title(11, "wait 4 u", "", color)
self.transition_color(color)
self.soul.move_to(ORIGIN)
# Concentric circles - time passing
circles = VGroup()
for i in range(8):
c = Circle(radius=0.5 + i * 0.4, color=color)
c.set_stroke(width=1, opacity=0.3 - i * 0.03)
circles.add(c)
self.play(
*[Create(c) for c in circles],
run_time=3,
lag_ratio=0.3
)
# Patient pulsing
for _ in range(2):
self.play(
circles.animate.scale(1.1),
self.soul.animate.scale(0.9),
run_time=2
)
self.play(
circles.animate.scale(1/1.1),
self.soul.animate.scale(1/0.9),
run_time=2
)
# Longing spiral
spiral = GoldenSpiral(turns=5, color=color)
spiral.scale(0.3)
spiral.set_opacity(0)
self.play(
spiral.animate.set_opacity(0.4).scale(2),
circles.animate.set_opacity(0.1),
run_time=4
)
self.play(FadeOut(circles, spiral), run_time=2)
# =========== TRACK 12: RUN TO U ===========
def track_12_run_to_u(self):
color = SuryaColors.RUN
self.show_title(12, "run to u", '"if the sky was falling I would run to you"', color)
self.transition_color(color)
self.soul.move_to(LEFT * 4)
# Target in distance
target = SoulOrb(color=SuryaColors.WITH_U, radius=0.25)
target.shift(RIGHT * 4)
target.set_opacity(0.3)
self.add(target)
# Speed lines
speed_lines = VGroup()
for _ in range(15):
y = random.uniform(-3, 3)
line = Line(LEFT * 7, RIGHT * 7, color=color)
line.shift(UP * y)
line.set_stroke(width=1, opacity=0.2)
speed_lines.add(line)
self.play(FadeIn(speed_lines), run_time=0.5)
# Urgent run
self.play(
self.soul.animate.shift(RIGHT * 4),
target.animate.set_opacity(0.7),
run_time=2,
rate_func=rate_functions.ease_in_quad
)
# Final push
self.play(
self.soul.animate.shift(RIGHT * 3),
target.animate.shift(LEFT * 1).set_opacity(1),
run_time=1.5,
rate_func=rate_functions.ease_out_quad
)
# Collision burst
burst = VGroup()
for i in range(12):
angle = i * PI / 6
ray = Line(ORIGIN, 2 * np.array([np.cos(angle), np.sin(angle), 0]))
ray.set_stroke(color, width=3, opacity=0.6)
burst.add(ray)
burst.move_to(RIGHT * 1.5)
self.play(
FadeIn(burst, scale=0.5),
self.soul.animate.move_to(RIGHT * 1.5).scale(1.2),
target.animate.move_to(RIGHT * 1.5).scale(1.2),
run_time=1
)
self.play(
burst.animate.scale(2).set_opacity(0),
FadeOut(speed_lines, target),
run_time=2
)
self.remove(burst)
# =========== TRACK 13: MEDICATIONS ===========
def track_13_medications(self):
color = SuryaColors.MEDS
self.show_title(13, "medications", '"raging in my head"', color)
self.transition_color(color)
self.soul.move_to(ORIGIN)
# Chaos attractor
attractor = StrangeAttractor(color=color)
attractor.scale(0.5)
self.play(Create(attractor), run_time=3)
# Aggressive spirographs
spiros = VGroup()
for i in range(3):
s = Spirograph(R=2 + i, r=0.5 + i * 0.3, d=1.5, color=color)
s.scale(0.4)
s.set_opacity(0.3)
s.rotate(i * PI / 3)
spiros.add(s)
self.play(
*[Create(s) for s in spiros],
run_time=2
)
# Soul shakes
original_pos = self.soul.get_center()
for _ in range(5):
offset = np.array([random.uniform(-0.3, 0.3), random.uniform(-0.3, 0.3), 0])
self.play(
self.soul.animate.move_to(original_pos + offset),
run_time=0.1
)
self.play(self.soul.animate.move_to(original_pos), run_time=0.2)
# Red pulses
for _ in range(2):
pulse = Circle(radius=0.5, color=color)
pulse.set_stroke(width=3, opacity=0.8)
self.add(pulse)
self.play(
pulse.animate.scale(4).set_opacity(0),
spiros.animate.rotate(PI/6),
run_time=1
)
self.remove(pulse)
# Struggle
self.play(
Rotate(attractor, PI/2, about_point=ORIGIN),
spiros.animate.set_opacity(0.5),
self.soul.animate.scale(0.8),
run_time=3
)
# Exhaustion
self.play(
attractor.animate.set_opacity(0.2),
spiros.animate.set_opacity(0.1),
self.soul.animate.scale(0.8).set_opacity(0.6),
run_time=3
)
self.play(FadeOut(attractor, spiros), run_time=2)
# =========== TRACK 14: HOLLOW ===========
def track_14_hollow(self):
color = SuryaColors.HOLLOW
self.show_title(14, "hollow", '"you took my sorrow and flew it to the moon"', color)
self.transition_color(color)
self.play(self.soul.animate.move_to(ORIGIN).scale(1.3).set_opacity(1), run_time=2)
# Stars return bright
self.play(
*[s.animate.set_opacity(random.uniform(0.4, 0.9)) for s in self.stars],
run_time=3,
lag_ratio=0.01
)
# Golden spiral - resolution
spiral = GoldenSpiral(turns=5, color=color)
spiral.scale(0.2)
spiral.set_opacity(0)
self.play(
spiral.animate.set_opacity(0.6).scale(3),
run_time=5
)
# Moon appears
moon = Circle(radius=1.5, color=color)
moon.set_fill(color, opacity=0.1)
moon.set_stroke(color, width=2, opacity=0.5)
moon.shift(UP * 2 + RIGHT * 3)
self.play(FadeIn(moon, scale=0.5), run_time=3)
# Sorrow flies to moon
sorrow = VGroup()
for _ in range(15):
s = Dot(
point=self.soul.get_center() + np.array([
random.uniform(-0.4, 0.4),
random.uniform(-0.4, 0.4),
0
]),
radius=0.04,
color=GREY
)
sorrow.add(s)
self.play(FadeIn(sorrow), run_time=1)
self.play(
*[s.animate.move_to(moon.get_center() + np.array([
random.uniform(-0.4, 0.4),
random.uniform(-0.4, 0.4),
0
])).set_opacity(0) for s in sorrow],
self.soul.animate.scale(1.3).set_opacity(1),
run_time=4
)
# Sacred geometry finale
flower = FlowerOfLife(rings=2, radius=0.6, color=color)
flower.set_opacity(0)
self.play(
flower.animate.set_opacity(0.4),
spiral.animate.set_opacity(0.8),
run_time=3
)
# Final radiance
self.play(
self.soul.animate.scale(1.5),
flower.animate.scale(1.3),
spiral.animate.scale(1.2),
self.stars.animate.set_opacity(1),
run_time=2
)
self.wait(2)
# Store for finale
self.flower = flower
self.spiral = spiral
self.moon = moon
# =========== FINALE ===========
def finale(self):
# Final title
finale_text = Text("ready to feel something?", font_size=48,
color=SuryaColors.HOLLOW, slant=ITALIC)
finale_text.shift(DOWN * 1)
finale_text.set_opacity(0)
self.play(
finale_text.animate.set_opacity(0.8),
self.soul.animate.scale(1.2),
run_time=3
)
self.wait(2)
# Gentle fade
self.play(
self.soul.animate.scale(2).set_opacity(0.3),
self.flower.animate.set_opacity(0.1),
self.spiral.animate.set_opacity(0.2),
self.stars.animate.set_opacity(0.4),
self.moon.animate.set_opacity(0.2),
finale_text.animate.set_opacity(0.4),
run_time=4
)
# SURYA text returns
surya_final = Text("SURYA", font_size=80, color=SuryaColors.HOLLOW)
surya_final.set_opacity(0)
surya_final.shift(UP * 1)
das_final = Text("DAS", font_size=24, color=WHITE)
das_final.set_opacity(0)
das_final.next_to(surya_final, DOWN, buff=0.5)
self.play(
surya_final.animate.set_opacity(0.6),
das_final.animate.set_opacity(0.3),
finale_text.animate.shift(DOWN * 0.5).set_opacity(0.2),
run_time=3
)
self.wait(3)
# Final fade to black
self.play(
*[mob.animate.set_opacity(0) for mob in self.mobjects],
run_time=4
)
self.wait(1)
# ============================================================================
# INDIVIDUAL TRACK SCENES (for testing)
# ============================================================================
class TestSoul(Scene):
"""Quick test of soul orb"""
def construct(self):
soul = SoulOrb(color=SuryaColors.WITH_U, radius=0.3)
self.play(FadeIn(soul, scale=0.5), run_time=2)
for _ in range(3):
self.play(soul.animate.scale(1.3), run_time=1)
self.play(soul.animate.scale(1/1.3), run_time=1)
self.wait(1)
class TestMathObjects(Scene):
"""Test mathematical objects"""
def construct(self):
# Spirograph
spiro = Spirograph(R=3, r=1, d=1.5, color=PINK)
spiro.scale(0.5)
self.play(Create(spiro), run_time=3)
self.wait(1)
# Golden spiral
spiral = GoldenSpiral(turns=4, color=GOLD)
spiral.scale(0.5)
self.play(Transform(spiro, spiral), run_time=2)
self.wait(1)
# Flower of life
flower = FlowerOfLife(rings=2, radius=0.5, color=TEAL)
self.play(FadeIn(flower), run_time=2)
self.wait(1)
if __name__ == "__main__":
print("SURYA — A Mathematical Journey Through Feeling")
print("Render with: manim -pqh --fps 60 surya_journey.py SuryaJourney")
print("Preview with: manim -pql surya_journey.py SuryaJourney")