Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 184 additions & 0 deletions db_connection_pool_diagram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
from manim import *

# Keep a 16:9 render with a taller frame so vertical layouts stay visible.
config.pixel_width = 1920
config.pixel_height = 1080
config.frame_height = 10.5
config.frame_width = config.frame_height * 16 / 9


class DBConnectionPoolDiagram(Scene):
def construct(self):
# ---------- Helpers ----------
def make_node(
label: str,
width=3.0,
height=1.0,
fill_color=None,
text_color=WHITE,
font_size=30,
):
box = RoundedRectangle(
width=width,
height=height,
corner_radius=0.2,
stroke_width=2,
)
if fill_color:
box.set_fill(fill_color, opacity=1)
box.set_stroke(fill_color)
else:
box.set_fill(opacity=0)
box.set_stroke(WHITE)

text = Text(label, font_size=font_size, color=text_color)
text.move_to(box.get_center())
return VGroup(box, text)

def make_connection(slot_label: str):
slot = RoundedRectangle(
width=2.6,
height=0.8,
corner_radius=0.15,
stroke_width=2,
)
slot.set_fill("#ffd166", opacity=1)
slot.set_stroke("#ffd166")
txt = Text(slot_label, font_size=24, color=BLACK)
txt.move_to(slot.get_center())
return VGroup(slot, txt)

def make_request(color=YELLOW):
return Dot(radius=0.16, color=color)

# ---------- Main blocks ----------
incoming = make_node(
"Clients",
width=3.1,
height=1.2,
fill_color="#5c7cfa",
text_color=WHITE,
font_size=30,
)
incoming.move_to(LEFT * 5.2)

pool_shell = RoundedRectangle(
width=4.8,
height=5.6,
corner_radius=0.2,
stroke_width=2,
)
pool_shell.set_fill(opacity=0)
pool_shell.set_stroke(WHITE)

pool_title = Text("DB Connection Pool", font_size=30, color=WHITE)
pool_title.move_to(pool_shell.get_top() + DOWN * 0.45)

connections = VGroup(*[make_connection(f"Conn {i}") for i in range(1, 5)])
connections.arrange(DOWN, buff=0.30)
connections.move_to(pool_shell.get_center() + DOWN * 0.45)

pool = VGroup(pool_shell, pool_title, connections)
pool.move_to(ORIGIN)

database = make_node(
"Database",
width=2.8,
height=1.0,
fill_color="#06d6a0",
text_color=BLACK,
font_size=30,
)
database.move_to(RIGHT * 5.2)

# ---------- Static arrows ----------
request_arrow = Arrow(
start=np.array([incoming.get_right()[0], pool_shell.get_center()[1], 0]),
end=np.array([pool_shell.get_left()[0], pool_shell.get_center()[1], 0]),
buff=0.2,
stroke_width=3,
max_tip_length_to_length_ratio=0.12,
)

db_arrows = VGroup()
for conn in connections:
db_arrows.add(
Arrow(
start=conn.get_right(),
end=database.get_left(),
buff=0.2,
stroke_width=2.5,
max_tip_length_to_length_ratio=0.12,
)
)

# ---------- Draw ----------
self.play(FadeIn(incoming), FadeIn(pool_shell), FadeIn(pool_title), FadeIn(database))
self.play(LaggedStart(*[FadeIn(c) for c in connections], lag_ratio=0.08))
self.play(Create(request_arrow))
self.play(LaggedStart(*[Create(a) for a in db_arrows], lag_ratio=0.06))

# ---------- Request lifecycle animation ----------
status = Text("Acquire connection", font_size=32, color=YELLOW)
status.to_edge(DOWN)
self.play(FadeIn(status), run_time=0.3)

total_requests = 8
for i in range(total_requests):
conn_index = i % len(connections)
chosen = connections[conn_index]

req = make_request(color=YELLOW)
req.move_to(incoming.get_right() + RIGHT * 0.15)

original_color = chosen[0].get_fill_color()
busy_color = "#ff6b6b"

# Acquire in 2 hops: follow incoming arrow into pool, then route to selected connection
self.play(
MoveAlongPath(
req,
Line(req.get_center(), request_arrow.get_end() + RIGHT * 0.06),
rate_func=linear,
),
run_time=0.3,
)

self.play(
MoveAlongPath(
req,
Line(req.get_center(), chosen.get_left() + RIGHT * 0.15),
rate_func=linear,
),
chosen[0].animate.set_fill(busy_color, opacity=1),
run_time=0.3,
)

# Use connection (connection -> DB)
self.play(
MoveAlongPath(
req,
Line(chosen.get_right() + RIGHT * 0.05, database.get_left() + LEFT * 0.05),
rate_func=linear,
),
run_time=0.45,
)

# Release: return to pool and mark as free
self.play(
MoveAlongPath(
req,
Line(database.get_left() + LEFT * 0.05, chosen.get_center()),
rate_func=linear,
),
chosen[0].animate.set_fill(original_color, opacity=1),
run_time=0.45,
)

self.play(FadeOut(req), run_time=0.15)

if i == total_requests // 2 - 1:
self.play(Transform(status, Text("Release after use", font_size=32, color=GREEN).to_edge(DOWN)))

self.play(FadeOut(status), run_time=0.25)
self.wait(0.5)
111 changes: 111 additions & 0 deletions hierarchical_wal_replication.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from manim import *

config.pixel_width = 1920
config.pixel_height = 1080
config.frame_height = 10.5
config.frame_width = config.frame_height * 16 / 9


class HierarchicalWALReplication(Scene):
def construct(self):
def make_node(label, w=2.8, h=1.0, fill="#171717", text_color=WHITE, fs=28):
box = RoundedRectangle(width=w, height=h, corner_radius=0.24, stroke_width=2)
box.set_fill(fill, opacity=1)
box.set_stroke(WHITE, width=2)
txt = Text(label, font_size=fs, color=text_color)
txt.move_to(box.get_center())
return VGroup(box, txt)

def wal_arrow(start, end):
return Arrow(
start=start,
end=end,
buff=0.14,
stroke_width=2.8,
max_tip_length_to_length_ratio=0.16,
color=WHITE,
)

title = Text("PostgreSQL Cascading Replication", font_size=42, color=WHITE).to_edge(UP)

primary = make_node("PRIMARY", w=3.2, h=1.1, fill="#0398fc", text_color=WHITE, fs=30)
primary.move_to(UP * 3.0)

rep_l2_left = make_node("READ\nREPLICA", w=3.0, h=1.2, fill="#ffb30f", text_color=BLACK, fs=27)
rep_l2_right = make_node("READ\nREPLICA", w=3.0, h=1.2, fill="#ffb30f", text_color=BLACK, fs=27)
rep_l2_left.move_to(LEFT * 3.0 + UP * 0.9)
rep_l2_right.move_to(RIGHT * 3.0 + UP * 0.9)
level2 = VGroup(rep_l2_left, rep_l2_right)

read_left_1 = make_node("READ\nREPLICA", w=2.7, h=1.0, fill="#171717", fs=26)
read_left_2 = make_node("READ\nREPLICA", w=2.7, h=1.0, fill="#171717", fs=26)
read_right_1 = make_node("READ\nREPLICA", w=2.7, h=1.0, fill="#171717", fs=26)
read_right_2 = make_node("READ\nREPLICA", w=2.7, h=1.0, fill="#171717", fs=26)

read_left_1.move_to(LEFT * 4.5 + DOWN * 1.6)
read_left_2.move_to(LEFT * 1.5 + DOWN * 1.6)
read_right_1.move_to(RIGHT * 1.5 + DOWN * 1.6)
read_right_2.move_to(RIGHT * 4.5 + DOWN * 1.6)
reads = VGroup(read_left_1, read_left_2, read_right_1, read_right_2)

ellipsis_top = Text("...", font_size=42, color=WHITE).move_to(ORIGIN + UP * 1.0)
ellipsis_bottom_left = Text("...", font_size=42, color=WHITE).move_to(LEFT * 3.0 + DOWN * 1.6)
ellipsis_bottom_right = Text("...", font_size=42, color=WHITE).move_to(RIGHT * 3.0 + DOWN * 1.6)

a_p_l = wal_arrow(primary.get_bottom() + LEFT * 0.65, rep_l2_left.get_top() + RIGHT * 0.35)
a_p_r = wal_arrow(primary.get_bottom() + RIGHT * 0.65, rep_l2_right.get_top() + LEFT * 0.35)
a_l_1 = wal_arrow(rep_l2_left.get_bottom() + LEFT * 0.45, read_left_1.get_top() + RIGHT * 0.25)
a_l_2 = wal_arrow(rep_l2_left.get_bottom() + RIGHT * 0.45, read_left_2.get_top() + LEFT * 0.25)
a_r_1 = wal_arrow(rep_l2_right.get_bottom() + LEFT * 0.45, read_right_1.get_top() + RIGHT * 0.25)
a_r_2 = wal_arrow(rep_l2_right.get_bottom() + RIGHT * 0.45, read_right_2.get_top() + LEFT * 0.25)

top_arrows = VGroup(a_p_l, a_p_r)
bottom_arrows = VGroup(a_l_1, a_l_2, a_r_1, a_r_2)

self.play(FadeIn(title), run_time=0.8)
self.play(FadeIn(primary), run_time=0.6)
self.play(FadeIn(level2), FadeIn(ellipsis_top), run_time=0.8)
self.play(FadeIn(reads), FadeIn(ellipsis_bottom_left), FadeIn(ellipsis_bottom_right), run_time=0.9)

self.play(LaggedStart(*[Create(a) for a in top_arrows], lag_ratio=0.15), run_time=0.8)
self.play(LaggedStart(*[Create(a) for a in bottom_arrows], lag_ratio=0.1), run_time=0.9)

wal_events_top = VGroup(*[Dot(radius=0.09, color=YELLOW) for _ in top_arrows])
for dot, arrow in zip(wal_events_top, top_arrows):
dot.move_to(arrow.get_start())
self.play(FadeIn(wal_events_top), run_time=0.2)

for _ in range(3):
self.play(
*[MoveAlongPath(dot, arrow, rate_func=linear) for dot, arrow in zip(wal_events_top, top_arrows)],
run_time=0.8,
)
for dot, arrow in zip(wal_events_top, top_arrows):
dot.move_to(arrow.get_start())

branch_dots = VGroup(*[Dot(radius=0.08, color="#7df9ff") for _ in bottom_arrows])
for dot, arrow in zip(branch_dots, bottom_arrows):
dot.move_to(arrow.get_start())
self.play(FadeIn(branch_dots), run_time=0.15)
self.play(
*[MoveAlongPath(dot, arrow, rate_func=linear) for dot, arrow in zip(branch_dots, bottom_arrows)],
run_time=0.9,
)
self.play(FadeOut(branch_dots), run_time=0.15)

self.play(FadeOut(wal_events_top), run_time=0.2)

self.play(
primary[0].animate.set_stroke(YELLOW, width=4),
rep_l2_left[0].animate.set_stroke(YELLOW, width=4),
rep_l2_right[0].animate.set_stroke(YELLOW, width=4),
run_time=0.3,
)
self.play(
reads[0][0].animate.set_stroke(GREEN, width=4),
reads[1][0].animate.set_stroke(GREEN, width=4),
reads[2][0].animate.set_stroke(GREEN, width=4),
reads[3][0].animate.set_stroke(GREEN, width=4),
run_time=0.4,
)
self.wait(0.8)
101 changes: 101 additions & 0 deletions neural_network_line_fit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
from manim import *

config.pixel_width = 1920
config.pixel_height = 1080
config.frame_height = 10.5
config.frame_width = config.frame_height * 16 / 9


class NeuralNetworkLearnsLine(Scene):
def construct(self):
def node(label, fill, text_color=WHITE, w=1.9, h=0.95, fs=30):
box = RoundedRectangle(width=w, height=h, corner_radius=0.2, stroke_width=2)
box.set_fill(fill, opacity=1)
box.set_stroke(WHITE, width=2)
txt = Text(label, font_size=fs, color=text_color)
txt.move_to(box.get_center())
return VGroup(box, txt)

title = Text("Neural Network Learning y = m x + C", font_size=40, color=WHITE).to_edge(UP)

x_node = node("x", fill="#5c7cfa", w=1.4, fs=36)
mult_node = node("× m", fill="#ffb30f", text_color=BLACK, w=2.0, fs=32)
add_node = node("+ C", fill="#ffb30f", text_color=BLACK, w=2.0, fs=32)
yhat_node = node("ŷ", fill="#06d6a0", text_color=BLACK, w=1.6, fs=36)
y_node = node("y", fill="#ef476f", w=1.4, fs=36)
loss_node = node("Loss", fill="#7b2cbf", w=2.2, fs=32)

network = VGroup(x_node, mult_node, add_node, yhat_node)
network.arrange(RIGHT, buff=1.0).move_to(UP * 0.7)
y_node.next_to(yhat_node, DOWN, buff=1.5)
loss_node.next_to(yhat_node, RIGHT, buff=2.2).shift(DOWN * 1.5)

a1 = Arrow(x_node.get_right(), mult_node.get_left(), buff=0.12, stroke_width=2.8)
a2 = Arrow(mult_node.get_right(), add_node.get_left(), buff=0.12, stroke_width=2.8)
a3 = Arrow(add_node.get_right(), yhat_node.get_left(), buff=0.12, stroke_width=2.8)
compare_arrow = Arrow(yhat_node.get_bottom(), loss_node.get_left() + LEFT * 0.1, buff=0.15, stroke_width=2.6)
gt_arrow = Arrow(y_node.get_right(), loss_node.get_left() + DOWN * 0.15, buff=0.15, stroke_width=2.6)
back_m = Arrow(loss_node.get_top(), mult_node.get_bottom(), buff=0.15, stroke_width=2.4, color=ORANGE)
back_c = Arrow(loss_node.get_top(), add_node.get_bottom(), buff=0.15, stroke_width=2.4, color=ORANGE)

eq = MathTex(r"\hat{y} = m x + C", font_size=44).next_to(network, DOWN, buff=1.1).shift(LEFT * 2.0)
m_val = DecimalNumber(0.40, num_decimal_places=2, font_size=38, color=YELLOW)
c_val = DecimalNumber(1.80, num_decimal_places=2, font_size=38, color=YELLOW)
loss_val = DecimalNumber(8.60, num_decimal_places=2, font_size=38, color=RED)

m_label = Text("m:", font_size=30, color=WHITE)
c_label = Text("C:", font_size=30, color=WHITE)
l_label = Text("loss:", font_size=30, color=WHITE)

stats = VGroup(
VGroup(m_label, m_val).arrange(RIGHT, buff=0.2),
VGroup(c_label, c_val).arrange(RIGHT, buff=0.2),
VGroup(l_label, loss_val).arrange(RIGHT, buff=0.2),
).arrange(DOWN, aligned_edge=LEFT, buff=0.25)
stats.next_to(loss_node, RIGHT, buff=1.0)

self.play(FadeIn(title), run_time=0.8)
self.play(FadeIn(network), run_time=1.0)
self.play(Create(a1), Create(a2), Create(a3), run_time=0.9)
self.play(FadeIn(y_node), run_time=0.4)
self.play(FadeIn(loss_node), Create(compare_arrow), Create(gt_arrow), run_time=0.9)
self.play(Write(eq), FadeIn(stats), run_time=0.9)

forward_dot = Dot(radius=0.09, color=YELLOW)
for _ in range(2):
forward_dot.move_to(a1.get_start())
self.play(FadeIn(forward_dot), run_time=0.15)
self.play(MoveAlongPath(forward_dot, a1, rate_func=linear), run_time=0.35)
self.play(MoveAlongPath(forward_dot, a2, rate_func=linear), run_time=0.35)
self.play(MoveAlongPath(forward_dot, a3, rate_func=linear), run_time=0.35)
self.play(MoveAlongPath(forward_dot, compare_arrow, rate_func=linear), run_time=0.45)
self.play(FadeOut(forward_dot), run_time=0.15)

epochs = [
(0.75, 1.45, 5.20),
(1.10, 1.05, 2.90),
(1.35, 0.70, 1.40),
(1.50, 0.50, 0.35),
]

for m_new, c_new, l_new in epochs:
self.play(Create(back_m), Create(back_c), run_time=0.25)
self.play(
m_val.animate.set_value(m_new),
c_val.animate.set_value(c_new),
loss_val.animate.set_value(l_new),
mult_node[0].animate.set_stroke(ORANGE, width=5),
add_node[0].animate.set_stroke(ORANGE, width=5),
run_time=0.8,
)
self.play(
mult_node[0].animate.set_stroke(WHITE, width=2),
add_node[0].animate.set_stroke(WHITE, width=2),
FadeOut(back_m),
FadeOut(back_c),
run_time=0.35,
)

final_box = SurroundingRectangle(eq, color=GREEN, buff=0.2, stroke_width=3)
self.play(Create(final_box), loss_node[0].animate.set_stroke(GREEN, width=4), run_time=0.6)
self.wait(0.8)
Loading