from manim import * import numpy as np import random class OSKVLogoReveal(Scene): def construct(self): self.camera.background_color = "#050510" # === PART 1: Particles converge to center === particles = VGroup() for _ in range(100): dot = Dot( point=np.array([ random.uniform(-8, 8), random.uniform(-5, 5), 0 ]), radius=random.uniform(0.02, 0.06), color=random.choice([WHITE, BLUE_B, PURPLE_B]) ) dot.set_opacity(random.uniform(0.3, 0.8)) particles.add(dot) self.add(particles) # Converge to center self.play( *[dot.animate.move_to( np.array([random.uniform(-0.5, 0.5), random.uniform(-0.2, 0.2), 0]) ).set_opacity(0) for dot in particles], run_time=1.5, rate_func=rush_into ) self.remove(particles) # === PART 2: Flash and logo image appears === # White flash flash = Rectangle( width=16, height=10, fill_color=WHITE, fill_opacity=0.8, stroke_width=0 ) self.play(FadeIn(flash, run_time=0.1)) self.play(FadeOut(flash, run_time=0.3)) # Load the actual OSKV logo logo = ImageMobject("/Users/jakeshore/.clawdbot/workspace/oskv_logo.png") logo.scale_to_fit_width(8) logo.set_opacity(0) self.add(logo) # Fade in the logo self.play(logo.animate.set_opacity(1), run_time=1) # === PART 3: Geometric frame builds around logo === # Inner frame inner_frame = RoundedRectangle( width=9, height=3, corner_radius=0.3, color=WHITE, stroke_width=1.5 ) inner_frame.set_stroke(opacity=0.5) # Outer frame outer_frame = RoundedRectangle( width=10, height=3.8, corner_radius=0.4, color=BLUE_B, stroke_width=1 ) outer_frame.set_stroke(opacity=0.3) self.play( Create(inner_frame), run_time=0.8 ) self.play( Create(outer_frame), run_time=0.6 ) # === PART 4: Orbiting dots === orbit_dots = VGroup() orbit_trails = VGroup() n_orbits = 6 for i in range(n_orbits): dot = Dot(radius=0.06, color=[BLUE, PURPLE, TEAL, WHITE, BLUE_B, PURPLE_B][i]) dot.set_glow_factor(1.5) angle = i * TAU / n_orbits radius_x = 5.5 radius_y = 2.2 dot.move_to([ radius_x * np.cos(angle), radius_y * np.sin(angle), 0 ]) orbit_dots.add(dot) self.play( LaggedStart(*[GrowFromCenter(d) for d in orbit_dots], lag_ratio=0.1), run_time=0.6 ) # Orbit around the logo orbit_anims = [] for i, dot in enumerate(orbit_dots): orbit_anims.append( Rotate(dot, TAU * 0.4, about_point=ORIGIN) ) self.play(*orbit_anims, run_time=2, rate_func=smooth) # === PART 5: Pulse effect === # Scale pulse logo_group = VGroup(inner_frame, outer_frame) self.play( logo.animate.scale(1.08), logo_group.animate.scale(1.08), *[dot.animate.scale(1.5).set_opacity(0.5) for dot in orbit_dots], rate_func=there_and_back, run_time=0.6 ) # === PART 6: Expanding ring waves === for _ in range(2): ring = Circle(radius=0.5, color=BLUE_B, stroke_width=3) ring.set_stroke(opacity=0.8) self.add(ring) self.play( ring.animate.scale(12).set_stroke(opacity=0), run_time=0.8, rate_func=rush_from ) self.remove(ring) # === PART 7: Corner accents === corners = VGroup() corner_positions = [ (UP + LEFT, 0), (UP + RIGHT, -PI/2), (DOWN + RIGHT, PI), (DOWN + LEFT, PI/2) ] for pos, rot in corner_positions: corner = VGroup( Line(ORIGIN, RIGHT * 0.5, color=TEAL, stroke_width=2), Line(ORIGIN, UP * 0.5, color=TEAL, stroke_width=2) ) corner.rotate(rot) corner.move_to(pos * np.array([4.8, 1.8, 0])) corners.add(corner) self.play( LaggedStart(*[Create(c) for c in corners], lag_ratio=0.1), run_time=0.6 ) # === PART 8: Ambient floating particles === ambient = VGroup() for _ in range(40): p = Dot( radius=random.uniform(0.01, 0.04), color=random.choice([WHITE, BLUE_B, PURPLE_B, TEAL]) ) p.set_opacity(random.uniform(0.15, 0.4)) p.move_to([random.uniform(-7, 7), random.uniform(-4, 4), 0]) ambient.add(p) self.play(FadeIn(ambient), run_time=0.3) # Gentle upward drift drift_anims = [ p.animate.shift(UP * random.uniform(0.3, 0.8)).set_opacity(0) for p in ambient ] # Second pulse while particles drift self.play( *drift_anims, logo.animate.scale(1.05), logo_group.animate.scale(1.05), rate_func=there_and_back, run_time=2 ) # === PART 9: Final hold with slow orbit === final_orbit = [ Rotate(dot, TAU * 0.15, about_point=ORIGIN) for dot in orbit_dots ] self.play(*final_orbit, run_time=2, rate_func=linear) # Hold self.wait(1) # === PART 10: Fade to black === everything = VGroup(logo_group, orbit_dots, corners) self.play( FadeOut(everything), logo.animate.set_opacity(0), FadeOut(ambient), run_time=1.5 )