https://www.youtube.com/watch?v=3gSy_yN9YBo&t=1s https://www.youtube.com/watch?v=CYncNBMPLLk by using this reference alphafold video , can you make new video animated which easily explaine and downloadable format mp4
https://www.youtube.com/watch?v=3gSy_yN9YBo
pip show manim 2>&1 | head -5; python3 -c "import manim; print('manim version:',…
python3 -c "import cv2; print('cv2:', cv2.__version__)" 2>&1; python3 -c "import…
mkdir -p /home/daytona/workspace/alphafold-video/frames && echo "dir created"
~/alphafold-video/generate_video.py
"""
AlphaFold Animated Explainer Video Generator
Covers: Protein structure problem, AlphaFold2 solution, key steps, impact
Output: MP4 video
"""
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.patches import FancyArrowPatch, FancyBboxPatch, Arc
import matplotlib.patheffects as pe
from matplotlib.colors import LinearSegmentedColormap
import os, math
OUT_DIR = "/home/daytona/workspace/alphafold-video/frames"
FPS = 24
W, H = 1280, 720
DPI = 96 # so figsize = (W/DPI, H/DPI)
figsize = (W / DPI, H / DPI)
# ── colour palette ──────────────────────────────────────────────
BG = "#0D1117"
BLUE = "#58A6FF"
GREEN = "#3FB950"
ORANGE = "#F78166"
PURPLE = "#BC8CFF"
YELLOW = "#E3B341"
WHITE = "#F0F6FC"
GREY = "#8B949E"
TEAL = "#39D353"
PINK = "#FF7B72"
frame_idx = [0] # mutable counter
def new_fig():
fig, ax = plt.subplots(figsize=figsize, dpi=DPI)
fig.patch.set_facecolor(BG)
ax.set_facecolor(BG)
ax.set_xlim(0, W); ax.set_ylim(0, H)
ax.axis('off')
return fig, ax
def save(fig, n_copies=1):
for _ in range(n_copies):
path = os.path.join(OUT_DIR, f"frame_{frame_idx[0]:05d}.png")
fig.savefig(path, dpi=DPI, bbox_inches='tight', pad_inches=0,
facecolor=BG)
frame_idx[0] += 1
plt.close(fig)
def lerp(a, b, t):
return a + (b - a) * t
def ease_in_out(t):
return t * t * (3 - 2 * t)
# ── helpers for drawing ─────────────────────────────────────────
def draw_title_card(ax, title, subtitle="", title_color=BLUE):
ax.text(W/2, H/2 + 40, title,
ha='center', va='center', fontsize=38, fontweight='bold',
color=title_color, fontfamily='DejaVu Sans',
path_effects=[pe.withStroke(linewidth=6, foreground=BG)])
if subtitle:
ax.text(W/2, H/2 - 30, subtitle,
ha='center', va='center', fontsize=20,
color=GREY, fontfamily='DejaVu Sans')
def draw_footer(ax, text):
ax.text(W/2, 18, text, ha='center', va='bottom',
fontsize=11, color=GREY, fontstyle='italic')
def draw_section_label(ax, text, color=YELLOW):
rect = FancyBboxPatch((20, H-50), 280, 34,
boxstyle="round,pad=4", linewidth=1.5,
edgecolor=color, facecolor=BG)
ax.add_patch(rect)
ax.text(160, H-33, text, ha='center', va='center',
fontsize=13, color=color, fontweight='bold')
# ══════════════════════════════════════════════════════════
# SCENE 1 – Title Card (~2 s = 48 frames)
# ══════════════════════════════════════════════════════════
print("Scene 1: Title Card")
STEPS = 24
for i in range(STEPS):
t = ease_in_out(i / (STEPS - 1))
fig, ax = new_fig()
# animated background dots
rng = np.random.default_rng(42)
xs = rng.uniform(50, W-50, 120)
ys = rng.uniform(50, H-50, 120)
sizes = rng.uniform(2, 6, 120)
alphas = rng.uniform(0.1, 0.4, 120)
for x, y, s, a in zip(xs, ys, sizes, alphas):
ax.plot(x, y, 'o', color=BLUE, markersize=s, alpha=a * t)
alpha_title = t
ax.text(W/2, H/2 + 60, "How AlphaFold Works",
ha='center', va='center', fontsize=44, fontweight='bold',
color=BLUE, alpha=alpha_title,
path_effects=[pe.withStroke(linewidth=8, foreground=BG)])
ax.text(W/2, H/2 - 10, "The AI that solved the 50-year protein folding problem",
ha='center', va='center', fontsize=21, color=WHITE, alpha=alpha_title * 0.9)
ax.text(W/2, H/2 - 55, "by DeepMind",
ha='center', va='center', fontsize=16, color=GREY, alpha=alpha_title * 0.7)
draw_footer(ax, "AlphaFold2 • Nature 2021 • Jumper et al.")
save(fig, 1)
# hold title 2 s
for _ in range(48):
fig, ax = new_fig()
rng = np.random.default_rng(42)
xs = rng.uniform(50, W-50, 120)
ys = rng.uniform(50, H-50, 120)
sizes = rng.uniform(2, 6, 120)
alphas = rng.uniform(0.1, 0.4, 120)
for x, y, s, a in zip(xs, ys, sizes, alphas):
ax.plot(x, y, 'o', color=BLUE, markersize=s, alpha=a)
ax.text(W/2, H/2 + 60, "How AlphaFold Works",
ha='center', va='center', fontsize=44, fontweight='bold',
color=BLUE,
path_effects=[pe.withStroke(linewidth=8, foreground=BG)])
ax.text(W/2, H/2 - 10, "The AI that solved the 50-year protein folding problem",
ha='center', va='center', fontsize=21, color=WHITE)
ax.text(W/2, H/2 - 55, "by DeepMind",
ha='center', va='center', fontsize=16, color=GREY)
draw_footer(ax, "AlphaFold2 • Nature 2021 • Jumper et al.")
save(fig, 1)
# ══════════════════════════════════════════════════════════
# SCENE 2 – What is a Protein? (~5 s = 120 frames)
# ══════════════════════════════════════════════════════════
print("Scene 2: What is a protein")
def draw_amino_chain(ax, n, progress, cx=640, cy=360, spread=55):
"""Draw a chain of amino acid circles, revealed up to progress (0-1)."""
colors = [BLUE, GREEN, ORANGE, PURPLE, YELLOW, TEAL, PINK,
BLUE, GREEN, ORANGE, PURPLE, YELLOW, TEAL, PINK,
BLUE, GREEN, ORANGE, PURPLE, YELLOW]
visible = max(1, int(n * progress))
positions = []
angle_step = 2 * math.pi / n
rx, ry = spread * n / 8, spread * 0.6
for k in range(visible):
x = cx + rx * math.cos(k * angle_step - math.pi / 2)
y = cy + ry * math.sin(k * angle_step - math.pi / 2) * 0.4
positions.append((x, y))
# draw bonds
for k in range(len(positions) - 1):
x1, y1 = positions[k]
x2, y2 = positions[k+1]
ax.plot([x1, x2], [y1, y2], color=WHITE, lw=2.5, alpha=0.5, zorder=1)
# draw circles
for k, (x, y) in enumerate(positions):
circle = plt.Circle((x, y), 18, color=colors[k % len(colors)],
zorder=2, alpha=0.92)
ax.add_patch(circle)
ax.text(x, y, "AA", ha='center', va='center',
fontsize=7, color=WHITE, fontweight='bold', zorder=3)
return positions
# sub-scene 2a: amino acid chain appears (1 s)
n_aa = 14
for i in range(24):
t = ease_in_out(i / 23)
fig, ax = new_fig()
draw_section_label(ax, "PROTEINS 101")
ax.text(W/2, H - 100, "Proteins are chains of amino acids",
ha='center', va='center', fontsize=26, color=WHITE, alpha=t)
draw_amino_chain(ax, n_aa, t, cx=640, cy=380)
ax.text(W/2, 80, "Each bead = 1 amino acid | 20 types exist",
ha='center', fontsize=16, color=GREY, alpha=t)
draw_footer(ax, "There are ~200 amino acid types, but 20 standard ones code proteins")
save(fig, 1)
# sub-scene 2b: folding hint (hold + text)
for i in range(48):
t_fold = ease_in_out(min(1, i / 24))
fig, ax = new_fig()
draw_section_label(ax, "PROTEINS 101")
ax.text(W/2, H - 100, "Proteins are chains of amino acids",
ha='center', va='center', fontsize=26, color=WHITE)
# show folded spiral
positions = draw_amino_chain(ax, n_aa, 1.0, cx=640, cy=380)
# overlay arrow toward 3D
ax.annotate("", xy=(850, 360), xytext=(750, 360),
arrowprops=dict(arrowstyle="->", color=YELLOW,
lw=2.5, mutation_scale=22))
# folded blob placeholder
blob_alpha = t_fold
ellipse = patches.Ellipse((960, 360), 140, 90,
facecolor=PURPLE, alpha=0.25 * blob_alpha,
edgecolor=PURPLE, linewidth=2, linestyle='--')
ax.add_patch(ellipse)
ax.text(960, 360, "3-D\nShape", ha='center', va='center',
fontsize=14, color=PURPLE, alpha=blob_alpha, fontweight='bold')
ax.text(W/2, 80, "Sequence → folds into a specific 3-D shape → determines function",
ha='center', fontsize=16, color=GREY)
draw_footer(ax, "Anfinsen's dogma: sequence encodes structure")
save(fig, 1)
# ══════════════════════════════════════════════════════════
# SCENE 3 – The 50-year Problem (~4 s)
# ══════════════════════════════════════════════════════════
print("Scene 3: The problem")
for i in range(24):
t = ease_in_out(i / 23)
fig, ax = new_fig()
draw_section_label(ax, "THE PROBLEM", color=ORANGE)
ax.text(W/2, H/2 + 120, "Levinthal's Paradox",
ha='center', va='center', fontsize=32, fontweight='bold',
color=ORANGE, alpha=t)
ax.text(W/2, H/2 + 50,
"A 100 AA protein has ~10¹³⁰ possible conformations",
ha='center', va='center', fontsize=19, color=WHITE, alpha=t)
ax.text(W/2, H/2,
"Even sampling 1 per femtosecond would take",
ha='center', va='center', fontsize=17, color=GREY, alpha=t)
ax.text(W/2, H/2 - 50,
"longer than the age of the universe",
ha='center', va='center', fontsize=22, color=PINK,
fontweight='bold', alpha=t)
draw_footer(ax, "Proteins fold in microseconds — nature finds the answer instantly")
save(fig, 1)
# hold
for _ in range(60):
fig, ax = new_fig()
draw_section_label(ax, "THE PROBLEM", color=ORANGE)
ax.text(W/2, H/2 + 120, "Levinthal's Paradox",
ha='center', va='center', fontsize=32, fontweight='bold',
color=ORANGE)
ax.text(W/2, H/2 + 50,
"A 100 AA protein has ~10¹³⁰ possible conformations",
ha='center', va='center', fontsize=19, color=WHITE)
ax.text(W/2, H/2,
"Even sampling 1 per femtosecond would take",
ha='center', va='center', fontsize=17, color=GREY)
ax.text(W/2, H/2 - 50,
"longer than the age of the universe",
ha='center', va='center', fontsize=22, color=PINK,
fontweight='bold')
# CASP timeline bar
years = [1994, 1996, 1998, 2000, 2002, 2004, 2006, 2008, 2010, 2012,
2014, 2016, 2018, 2020]
scores = [25, 27, 30, 33, 36, 38, 40, 43, 45, 50,
54, 56, 58, 92]
bar_x0, bar_y0 = 160, 120
bar_w = 900
ax.text(640, bar_y0 + 60, "CASP Competition: GDT Score over Years",
ha='center', fontsize=13, color=GREY)
for k, (yr, sc) in enumerate(zip(years, scores)):
bh = sc * 0.7
bx = bar_x0 + k * (bar_w / len(years))
col = GREEN if yr == 2020 else BLUE
rect = patches.Rectangle((bx, bar_y0 - bh/2), 46, bh,
facecolor=col, alpha=0.7, edgecolor='none')
ax.add_patch(rect)
ax.text(bx + 23, bar_y0 - bh/2 - 10, str(yr)[-2:],
ha='center', fontsize=7, color=GREY)
# AlphaFold annotation
ax.annotate("AlphaFold2\n92/100",
xy=(bar_x0 + 13 * (bar_w / len(years)) + 23, bar_y0 + 5),
xytext=(bar_x0 + 10 * (bar_w / len(years)), bar_y0 + 50),
fontsize=11, color=GREEN, fontweight='bold',
arrowprops=dict(arrowstyle="->", color=GREEN, lw=1.5))
draw_footer(ax, "CASP: Critical Assessment of protein Structure Prediction (biennial competition)")
save(fig, 1)
# ══════════════════════════════════════════════════════════
# SCENE 4 – AlphaFold2 Pipeline Overview (~6 s)
# ══════════════════════════════════════════════════════════
print("Scene 4: Pipeline overview")
pipeline = [
("Input\nSequence", BLUE, 160),
("Multiple\nSeq Align", TEAL, 330),
("Evoformer\nNetwork", PURPLE, 510),
("Structure\nModule", ORANGE, 690),
("3-D\nStructure", GREEN, 870),
]
for i in range(36):
t = ease_in_out(i / 35)
visible = max(1, int(len(pipeline) * t * 1.2))
fig, ax = new_fig()
draw_section_label(ax, "ALPHAFOLD2 PIPELINE", color=PURPLE)
ax.text(640, H - 80, "From amino acid sequence → 3-D atomic coordinates",
ha='center', va='center', fontsize=20, color=WHITE)
for k, (label, color, cx) in enumerate(pipeline[:visible]):
cy = H / 2
# box
box = FancyBboxPatch((cx - 70, cy - 50), 140, 100,
boxstyle="round,pad=8", linewidth=2,
edgecolor=color, facecolor=BG)
ax.add_patch(box)
ax.text(cx, cy, label, ha='center', va='center',
fontsize=14, color=color, fontweight='bold')
# arrow to next
if k < len(pipeline[:visible]) - 1:
nx = pipeline[k+1][2]
ax.annotate("", xy=(nx - 72, cy),
xytext=(cx + 72, cy),
arrowprops=dict(arrowstyle="->", color=WHITE,
lw=2, mutation_scale=18))
draw_footer(ax, "AlphaFold2 architecture — Jumper et al., Nature 2021")
save(fig, 1)
# hold pipeline for 2 s
for _ in range(48):
fig, ax = new_fig()
draw_section_label(ax, "ALPHAFOLD2 PIPELINE", color=PURPLE)
ax.text(640, H - 80, "From amino acid sequence → 3-D atomic coordinates",
ha='center', va='center', fontsize=20, color=WHITE)
for k, (label, color, cx) in enumerate(pipeline):
cy = H / 2
box = FancyBboxPatch((cx - 70, cy - 50), 140, 100,
boxstyle="round,pad=8", linewidth=2,
edgecolor=color, facecolor=BG)
ax.add_patch(box)
ax.text(cx, cy, label, ha='center', va='center',
fontsize=14, color=color, fontweight='bold')
if k < len(pipeline) - 1:
nx = pipeline[k+1][2]
ax.annotate("", xy=(nx - 72, cy),
xytext=(cx + 72, cy),
arrowprops=dict(arrowstyle="->", color=WHITE,
lw=2, mutation_scale=18))
draw_footer(ax, "AlphaFold2 architecture — Jumper et al., Nature 2021")
save(fig, 1)
# ══════════════════════════════════════════════════════════
# SCENE 5 – MSA: Mining Evolution (~5 s)
# ══════════════════════════════════════════════════════════
print("Scene 5: MSA")
aa_letters = list("ACDEFGHIKLMNPQRSTVWY")
rng = np.random.default_rng(7)
query_seq = list("MKTAYIAKQRQISFVKSHFSRQLEERLGLIEVQAPILSRVGDGTQDNLSGAEKAVQVKVKALPDAQFEVVHSLAKWKRQTLGQHDFSAGEGLYTHMKALRPDEDRLSPLHSVYVDQWDWERVMGDGERQFSTLKSTVEAIWAGIKATEAAVSEEFGLAPFLPDQIHFVHSQELLSRYPDLDAKGRERAIAKDLGAVFLVGIGGKLSDGHRHDVRAPDYDDWSTPSELGHAGLNGDILVWNPVLEDAFELSSMGIRVDADTLKHQLALTGDEDRLELEWHQALLRGEMPQTIGGGIGQSRLTMLLLQLPHIGQVQAGVWPAAVRESVPSLL")[:18]
msa_seqs = []
for _ in range(8):
seq = [aa if rng.random() > 0.25 else rng.choice(aa_letters)
for aa in query_seq]
msa_seqs.append(seq)
color_map = {a: c for a, c in zip(
aa_letters,
[BLUE, GREEN, ORANGE, PURPLE, YELLOW, TEAL, PINK,
BLUE, GREEN, ORANGE, PURPLE, YELLOW, TEAL, PINK,
BLUE, GREEN, ORANGE, PURPLE, YELLOW, TEAL])}
for i in range(36):
t = ease_in_out(i / 35)
fig, ax = new_fig()
draw_section_label(ax, "STEP 1: MSA (Evolution)", color=TEAL)
ax.text(640, H - 80,
"Search databases for homologous sequences across species",
ha='center', va='center', fontsize=20, color=WHITE, alpha=t)
rows = [query_seq] + msa_seqs
row_labels = ["Query"] + [f"Homolog {k+1}" for k in range(len(msa_seqs))]
visible_rows = max(1, int(len(rows) * min(1, t * 1.5)))
cell_w, cell_h = 38, 32
start_x = (W - len(query_seq) * cell_w) / 2
start_y = H - 160
for r, (seq, lbl) in enumerate(zip(rows[:visible_rows], row_labels)):
y = start_y - r * (cell_h + 4)
lbl_col = YELLOW if r == 0 else GREY
ax.text(start_x - 80, y + cell_h/2, lbl,
ha='right', va='center', fontsize=10, color=lbl_col)
for c, aa in enumerate(seq):
col = color_map.get(aa, WHITE)
rect = patches.Rectangle((start_x + c * cell_w, y),
cell_w - 2, cell_h - 2,
facecolor=col, alpha=0.75, edgecolor='none')
ax.add_patch(rect)
ax.text(start_x + c * cell_w + (cell_w-2)/2, y + (cell_h-2)/2,
aa, ha='center', va='center', fontsize=9, color=BG,
fontweight='bold')
ax.text(640, 60,
"Co-evolution patterns reveal which residues interact",
ha='center', fontsize=16, color=TEAL, alpha=t)
draw_footer(ax, "MSA = Multiple Sequence Alignment — reads evolutionary constraints")
save(fig, 1)
# hold 2 s
for _ in range(48):
fig, ax = new_fig()
draw_section_label(ax, "STEP 1: MSA (Evolution)", color=TEAL)
ax.text(640, H - 80,
"Search databases for homologous sequences across species",
ha='center', va='center', fontsize=20, color=WHITE)
rows = [query_seq] + msa_seqs
row_labels = ["Query"] + [f"Homolog {k+1}" for k in range(len(msa_seqs))]
cell_w, cell_h = 38, 32
start_x = (W - len(query_seq) * cell_w) / 2
start_y = H - 160
for r, (seq, lbl) in enumerate(zip(rows, row_labels)):
y = start_y - r * (cell_h + 4)
lbl_col = YELLOW if r == 0 else GREY
ax.text(start_x - 80, y + cell_h/2, lbl,
ha='right', va='center', fontsize=10, color=lbl_col)
for c, aa in enumerate(seq):
col = color_map.get(aa, WHITE)
rect = patches.Rectangle((start_x + c * cell_w, y),
cell_w - 2, cell_h - 2,
facecolor=col, alpha=0.75, edgecolor='none')
ax.add_patch(rect)
ax.text(start_x + c * cell_w + (cell_w-2)/2, y + (cell_h-2)/2,
aa, ha='center', va='center', fontsize=9, color=BG,
fontweight='bold')
ax.text(640, 60, "Co-evolution patterns reveal which residues interact",
ha='center', fontsize=16, color=TEAL)
draw_footer(ax, "MSA = Multiple Sequence Alignment — reads evolutionary constraints")
save(fig, 1)
# ══════════════════════════════════════════════════════════
# SCENE 6 – Pairwise Distance Map (~4 s)
# ══════════════════════════════════════════════════════════
print("Scene 6: Distance map")
n_res = 24
rng2 = np.random.default_rng(99)
dist_map = np.zeros((n_res, n_res))
for ii in range(n_res):
for jj in range(n_res):
base = abs(ii - jj)
dist_map[ii, jj] = base + rng2.normal(0, 1.5)
dist_map = np.clip(dist_map, 0, 20)
for i in range(24):
t = ease_in_out(i / 23)
fig, ax = new_fig()
draw_section_label(ax, "STEP 2: Pairwise Distances", color=BLUE)
ax.text(640, H - 80,
"Predict distances between every pair of residues",
ha='center', fontsize=22, color=WHITE, alpha=t)
# left: sequence diagram
sx = 150
for k in range(n_res):
ax.plot([sx + k*18, sx + k*18 + 16], [400, 400],
color=BLUE, lw=8, solid_capstyle='round', alpha=0.8)
ax.text(sx + n_res*9, 360, "Sequence", ha='center', fontsize=13, color=GREY)
# right: heatmap
hm_x0, hm_y0 = 480, 160
cell = 18
cmap = plt.cm.viridis
for ii in range(int(n_res * t)):
for jj in range(int(n_res * t)):
norm_d = dist_map[ii, jj] / 20.0
col = cmap(norm_d)
rect = patches.Rectangle((hm_x0 + jj*cell, hm_y0 + ii*cell),
cell-1, cell-1,
facecolor=col, edgecolor='none')
ax.add_patch(rect)
ax.text(hm_x0 + n_res*cell/2, hm_y0 - 18,
"Predicted Distance Map (i,j residues)",
ha='center', fontsize=13, color=GREY, alpha=t)
draw_footer(ax, "Distance histogram → NN outputs probability of distance bins 2-22 Å")
save(fig, 1)
# hold
for _ in range(60):
fig, ax = new_fig()
draw_section_label(ax, "STEP 2: Pairwise Distances", color=BLUE)
ax.text(640, H - 80,
"Predict distances between every pair of residues",
ha='center', fontsize=22, color=WHITE)
sx = 150
for k in range(n_res):
ax.plot([sx + k*18, sx + k*18 + 16], [400, 400],
color=BLUE, lw=8, solid_capstyle='round', alpha=0.8)
ax.text(sx + n_res*9, 360, "Sequence", ha='center', fontsize=13, color=GREY)
hm_x0, hm_y0 = 480, 160
cell = 18
cmap = plt.cm.viridis
for ii in range(n_res):
for jj in range(n_res):
norm_d = dist_map[ii, jj] / 20.0
col = cmap(norm_d)
rect = patches.Rectangle((hm_x0 + jj*cell, hm_y0 + ii*cell),
cell-1, cell-1,
facecolor=col, edgecolor='none')
ax.add_patch(rect)
ax.text(hm_x0 + n_res*cell/2, hm_y0 - 18,
"Predicted Distance Map (i,j residues)",
ha='center', fontsize=13, color=GREY)
draw_footer(ax, "Distance histogram → NN outputs probability of distance bins 2-22 Å")
save(fig, 1)
# ══════════════════════════════════════════════════════════
# SCENE 7 – Evoformer (~5 s)
# ══════════════════════════════════════════════════════════
print("Scene 7: Evoformer")
evo_blocks = [
("MSA\nRepresentation", TEAL, 240, 500),
("Pair\nRepresentation", BLUE, 240, 340),
("Row-Wise\nAttention", PURPLE, 500, 500),
("Column-Wise\nAttn", PURPLE, 500, 340),
("Outer\nProduct Mean", ORANGE, 740, 420),
("Triangle\nUpdates", YELLOW, 960, 420),
("Output\nEmbeddings", GREEN, 1140, 420),
]
for i in range(36):
t = ease_in_out(i / 35)
vis = max(1, int(len(evo_blocks) * min(1, t * 1.4)))
fig, ax = new_fig()
draw_section_label(ax, "STEP 3: Evoformer Block", color=PURPLE)
ax.text(640, H - 70, "Transformer-based network: iteratively refines MSA + pair representations",
ha='center', fontsize=18, color=WHITE, alpha=t)
for k, (lbl, col, bx, by) in enumerate(evo_blocks[:vis]):
box = FancyBboxPatch((bx - 70, by - 36), 140, 72,
boxstyle="round,pad=6", linewidth=2,
edgecolor=col, facecolor=BG)
ax.add_patch(box)
ax.text(bx, by, lbl, ha='center', va='center',
fontsize=11, color=col, fontweight='bold')
# arrows
arrow_pairs = [(0,2),(1,3),(2,4),(3,4),(4,5),(5,6)]
for src_i, dst_i in arrow_pairs:
if src_i < vis and dst_i < vis:
sx2 = evo_blocks[src_i][2] + 72
sy2 = evo_blocks[src_i][3]
dx = evo_blocks[dst_i][2] - 72
dy = evo_blocks[dst_i][3]
if sx2 < dx - 5:
ax.annotate("", xy=(dx, dy), xytext=(sx2, sy2),
arrowprops=dict(arrowstyle="->", color=WHITE,
lw=1.5, mutation_scale=14,
connectionstyle="arc3,rad=0.2"))
ax.text(640, 60,
"48 Evoformer blocks stack sequentially → deep understanding of contacts",
ha='center', fontsize=14, color=GREY, alpha=t)
draw_footer(ax, "Key innovation: pair & MSA representations communicate via attention")
save(fig, 1)
# hold
for _ in range(60):
fig, ax = new_fig()
draw_section_label(ax, "STEP 3: Evoformer Block", color=PURPLE)
ax.text(640, H - 70, "Transformer-based network: iteratively refines MSA + pair representations",
ha='center', fontsize=18, color=WHITE)
for k, (lbl, col, bx, by) in enumerate(evo_blocks):
box = FancyBboxPatch((bx - 70, by - 36), 140, 72,
boxstyle="round,pad=6", linewidth=2,
edgecolor=col, facecolor=BG)
ax.add_patch(box)
ax.text(bx, by, lbl, ha='center', va='center',
fontsize=11, color=col, fontweight='bold')
arrow_pairs = [(0,2),(1,3),(2,4),(3,4),(4,5),(5,6)]
for src_i, dst_i in arrow_pairs:
sx2 = evo_blocks[src_i][2] + 72
sy2 = evo_blocks[src_i][3]
dx = evo_blocks[dst_i][2] - 72
dy = evo_blocks[dst_i][3]
if sx2 < dx - 5:
ax.annotate("", xy=(dx, dy), xytext=(sx2, sy2),
arrowprops=dict(arrowstyle="->", color=WHITE,
lw=1.5, mutation_scale=14,
connectionstyle="arc3,rad=0.2"))
ax.text(640, 60,
"48 Evoformer blocks stack sequentially → deep understanding of contacts",
ha='center', fontsize=14, color=GREY)
draw_footer(ax, "Key innovation: pair & MSA representations communicate via attention")
save(fig, 1)
# ══════════════════════════════════════════════════════════
# SCENE 8 – Structure Module (~5 s)
# ══════════════════════════════════════════════════════════
print("Scene 8: Structure module")
def draw_protein_3d(ax, progress, cx=640, cy=360, n=20, scale=180):
"""Draw a simplified protein fold using a parameterized curve."""
rng3 = np.random.default_rng(13)
t_vals = np.linspace(0, 2*math.pi, n)
# helix-like path
xs = cx + scale * np.cos(t_vals) * 0.9
ys = cy + scale * 0.4 * np.sin(t_vals * 2) + scale * 0.3 * np.cos(t_vals)
visible = max(2, int(n * progress))
# draw backbone
ax.plot(xs[:visible], ys[:visible], color=BLUE, lw=4, alpha=0.8, zorder=2)
# draw residue circles
cols = [GREEN, ORANGE, PURPLE, TEAL, YELLOW, PINK]
for k in range(visible):
c = cols[k % len(cols)]
ax.plot(xs[k], ys[k], 'o', color=c, markersize=12, zorder=3, alpha=0.9)
# draw some contact lines
if progress > 0.5:
contact_pairs = [(0,10),(2,15),(5,18),(8,17),(3,12)]
for p1, p2 in contact_pairs:
if p1 < visible and p2 < visible:
ax.plot([xs[p1], xs[p2]], [ys[p1], ys[p2]],
color=YELLOW, lw=1.5, alpha=0.4, linestyle='--', zorder=1)
for i in range(48):
t = ease_in_out(i / 47)
fig, ax = new_fig()
draw_section_label(ax, "STEP 4: Structure Module", color=ORANGE)
ax.text(W/2, H - 80,
"Predicts 3-D backbone frames using Invariant Point Attention (IPA)",
ha='center', fontsize=19, color=WHITE, alpha=t)
draw_protein_3d(ax, t, cx=400, cy=360)
# right panel: explanation
steps_text = [
("N-Cα-C backbone frames", ORANGE, 760, 480),
("Invariant Point Attention", YELLOW, 760, 430),
("Iterative recycling (3×)", PURPLE, 760, 380),
("Side-chain torsion angles", GREEN, 760, 330),
("Confidence: pLDDT score", TEAL, 760, 280),
]
for k, (txt, col, tx, ty) in enumerate(steps_text):
alpha = min(1.0, (t * len(steps_text) - k) * 2)
ax.plot(tx - 20, ty, 'o', color=col, markersize=9, alpha=alpha, zorder=3)
ax.text(tx, ty, txt, va='center', fontsize=14,
color=col, alpha=alpha, fontweight='bold')
draw_footer(ax, "IPA = positions & orientations invariant to global rotation/translation")
save(fig, 1)
# ══════════════════════════════════════════════════════════
# SCENE 9 – pLDDT Confidence (~3 s)
# ══════════════════════════════════════════════════════════
print("Scene 9: pLDDT")
n_res2 = 30
rng4 = np.random.default_rng(55)
plddt = np.clip(rng4.normal(80, 15, n_res2), 10, 100)
plddt[5:10] = np.clip(rng4.normal(30, 8, 5), 10, 50) # low confidence region
for i in range(36):
t = ease_in_out(i / 35)
visible = max(1, int(n_res2 * t))
fig, ax = new_fig()
draw_section_label(ax, "pLDDT CONFIDENCE", color=GREEN)
ax.text(640, H - 80, "Every residue gets a confidence score (0-100)",
ha='center', fontsize=22, color=WHITE, alpha=t)
bar_x0, bar_y0 = 200, 160
bar_w = 880 / n_res2
for k in range(visible):
sc = plddt[k]
if sc >= 70:
col = BLUE
elif sc >= 50:
col = YELLOW
else:
col = ORANGE
rect = patches.Rectangle((bar_x0 + k*bar_w, bar_y0),
bar_w - 2, sc * 3.5,
facecolor=col, alpha=0.85, edgecolor='none')
ax.add_patch(rect)
# y-axis labels
for score, label in [(0, "0"), (50, "50"), (70, "70"), (100, "100")]:
ax.axhline(bar_y0 + score * 3.5, color=GREY, lw=0.8, alpha=0.4,
xmin=0.14, xmax=0.86)
ax.text(190, bar_y0 + score * 3.5, str(score),
ha='right', va='center', fontsize=10, color=GREY)
# legend
legend_items = [
(BLUE, "High confidence (≥ 70)"),
(YELLOW, "Medium (50-70)"),
(ORANGE, "Low (< 50)"),
]
for k, (col, lbl) in enumerate(legend_items):
ax.plot(820, 520 - k*40, 's', color=col, markersize=14)
ax.text(838, 520 - k*40, lbl, va='center', fontsize=13, color=WHITE)
ax.text(640, 70,
"Blue = confident | Orange = disordered region — biologically meaningful!",
ha='center', fontsize=14, color=GREY, alpha=t)
draw_footer(ax, "pLDDT (predicted Local Distance Difference Test) — per-residue accuracy metric")
save(fig, 1)
# hold
for _ in range(36):
fig, ax = new_fig()
draw_section_label(ax, "pLDDT CONFIDENCE", color=GREEN)
ax.text(640, H - 80, "Every residue gets a confidence score (0-100)",
ha='center', fontsize=22, color=WHITE)
bar_x0, bar_y0 = 200, 160
bar_w = 880 / n_res2
for k in range(n_res2):
sc = plddt[k]
if sc >= 70: col = BLUE
elif sc >= 50: col = YELLOW
else: col = ORANGE
rect = patches.Rectangle((bar_x0 + k*bar_w, bar_y0),
bar_w - 2, sc * 3.5,
facecolor=col, alpha=0.85, edgecolor='none')
ax.add_patch(rect)
for score in [0, 50, 70, 100]:
ax.axhline(bar_y0 + score * 3.5, color=GREY, lw=0.8, alpha=0.4,
xmin=0.14, xmax=0.86)
ax.text(190, bar_y0 + score * 3.5, str(score),
ha='right', va='center', fontsize=10, color=GREY)
for k, (col, lbl) in enumerate([(BLUE,"High (≥70)"),(YELLOW,"Medium (50-70)"),(ORANGE,"Low (<50)")]):
ax.plot(820, 520 - k*40, 's', color=col, markersize=14)
ax.text(838, 520 - k*40, lbl, va='center', fontsize=13, color=WHITE)
ax.text(640, 70, "Blue = confident | Orange = disordered region",
ha='center', fontsize=14, color=GREY)
draw_footer(ax, "pLDDT (predicted Local Distance Difference Test) — per-residue accuracy metric")
save(fig, 1)
# ══════════════════════════════════════════════════════════
# SCENE 10 – AlphaFold DB & Impact (~5 s)
# ══════════════════════════════════════════════════════════
print("Scene 10: Impact")
impact_items = [
("200 million+", "protein structures predicted", BLUE, 240, 520),
("Human Proteome", "~20,000 proteins mapped", GREEN, 640, 520),
("Drug Discovery", "new antibiotic candidates found", ORANGE, 1040, 520),
("Open Science", "freely accessible database", PURPLE, 240, 340),
("Nobel Prize", "Chemistry 2024 awarded", YELLOW, 640, 340),
("AlphaFold3", "DNA, RNA, small molecules too", TEAL, 1040, 340),
]
for i in range(48):
t = ease_in_out(i / 47)
vis = max(1, int(len(impact_items) * min(1, t * 1.3)))
fig, ax = new_fig()
draw_section_label(ax, "IMPACT & LEGACY", color=YELLOW)
ax.text(640, H - 70, "AlphaFold has transformed biology and medicine",
ha='center', fontsize=24, color=WHITE, alpha=t)
for k, (big, small, col, bx, by) in enumerate(impact_items[:vis]):
box = FancyBboxPatch((bx - 160, by - 55), 320, 110,
boxstyle="round,pad=8", linewidth=2,
edgecolor=col, facecolor="#161B22")
ax.add_patch(box)
ax.text(bx, by + 18, big, ha='center', va='center',
fontsize=17, color=col, fontweight='bold')
ax.text(bx, by - 18, small, ha='center', va='center',
fontsize=12, color=WHITE)
draw_footer(ax, "AlphaFold DB: ebi.ac.uk/alphafold | AlphaFold3: 2024")
save(fig, 1)
# ══════════════════════════════════════════════════════════
# SCENE 11 – Closing (~3 s)
# ══════════════════════════════════════════════════════════
print("Scene 11: Closing")
summary_lines = [
("1. Input", "Amino acid sequence", BLUE),
("2. MSA", "Evolutionary co-variation", TEAL),
("3. Evoformer", "48-layer attention network", PURPLE),
("4. Structure", "3-D backbone + side chains", ORANGE),
("5. Confidence","pLDDT per residue", GREEN),
]
for i in range(36):
t = ease_in_out(i / 35)
fig, ax = new_fig()
ax.text(640, H - 60, "AlphaFold2 — Summary",
ha='center', fontsize=34, fontweight='bold',
color=BLUE, alpha=t,
path_effects=[pe.withStroke(linewidth=6, foreground=BG)])
for k, (step, desc, col) in enumerate(summary_lines):
alpha = min(1.0, (t * len(summary_lines) - k) * 2)
y = H/2 + 100 - k * 75
ax.plot(200, y, 'o', color=col, markersize=18, alpha=alpha, zorder=3)
ax.text(230, y + 10, step, va='bottom', fontsize=15,
color=col, fontweight='bold', alpha=alpha)
ax.text(230, y - 12, desc, va='top', fontsize=13,
color=WHITE, alpha=alpha)
draw_footer(ax, "References: Jumper et al. Nature 2021 | Senior et al. Nature 2020 | AlphaFold DB")
save(fig, 1)
# hold
for _ in range(72):
fig, ax = new_fig()
ax.text(640, H - 60, "AlphaFold2 — Summary",
ha='center', fontsize=34, fontweight='bold', color=BLUE,
path_effects=[pe.withStroke(linewidth=6, foreground=BG)])
for k, (step, desc, col) in enumerate(summary_lines):
y = H/2 + 100 - k * 75
ax.plot(200, y, 'o', color=col, markersize=18, zorder=3)
ax.text(230, y + 10, step, va='bottom', fontsize=15,
color=col, fontweight='bold')
ax.text(230, y - 12, desc, va='top', fontsize=13, color=WHITE)
draw_footer(ax, "References: Jumper et al. Nature 2021 | Senior et al. Nature 2020 | AlphaFold DB")
save(fig, 1)
# fade-out
for i in range(24):
t = 1 - ease_in_out(i / 23)
fig, ax = new_fig()
ax.text(640, H/2, "AlphaFold2 — Summary",
ha='center', fontsize=34, fontweight='bold',
color=BLUE, alpha=t,
path_effects=[pe.withStroke(linewidth=6, foreground=BG)])
for k, (step, desc, col) in enumerate(summary_lines):
y = H/2 + 100 - k * 75
ax.plot(200, y, 'o', color=col, markersize=18, alpha=t, zorder=3)
ax.text(230, y + 10, step, va='bottom', fontsize=15,
color=col, fontweight='bold', alpha=t)
ax.text(230, y - 12, desc, va='top', fontsize=13, color=WHITE, alpha=t)
save(fig, 1)
print(f"\nDone! Total frames: {frame_idx[0]}")
cd /home/daytona/workspace/alphafold-video && python3 generate_video.py 2>&1
ls /home/daytona/workspace/alphafold-video/frames/ | wc -l
ls /home/daytona/workspace/alphafold-video/frames/ | tail -5
~/alphafold-video/generate_remaining.py
"""
Generate remaining AlphaFold scenes - fast version starting from frame 287
"""
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.patches import FancyBboxPatch
import matplotlib.patheffects as pe
import os, math
OUT_DIR = "/home/daytona/workspace/alphafold-video/frames"
FPS = 24
W, H = 1280, 720
DPI = 96
figsize = (W / DPI, H / DPI)
BG = "#0D1117"; BLUE = "#58A6FF"; GREEN = "#3FB950"; ORANGE = "#F78166"
PURPLE = "#BC8CFF"; YELLOW = "#E3B341"; WHITE = "#F0F6FC"; GREY = "#8B949E"
TEAL = "#39D353"; PINK = "#FF7B72"
# start from frame 287
frame_idx = [287]
def new_fig():
fig, ax = plt.subplots(figsize=figsize, dpi=DPI)
fig.patch.set_facecolor(BG); ax.set_facecolor(BG)
ax.set_xlim(0, W); ax.set_ylim(0, H); ax.axis('off')
return fig, ax
def save(fig, n=1):
for _ in range(n):
path = os.path.join(OUT_DIR, f"frame_{frame_idx[0]:05d}.png")
fig.savefig(path, dpi=DPI, bbox_inches='tight', pad_inches=0, facecolor=BG)
frame_idx[0] += 1
plt.close(fig)
def draw_footer(ax, text):
ax.text(W/2, 18, text, ha='center', va='bottom', fontsize=11, color=GREY, fontstyle='italic')
def draw_section_label(ax, text, color=YELLOW):
rect = FancyBboxPatch((20, H-50), 280, 34, boxstyle="round,pad=4", linewidth=1.5,
edgecolor=color, facecolor=BG)
ax.add_patch(rect)
ax.text(160, H-33, text, ha='center', va='center', fontsize=13, color=color, fontweight='bold')
def ease(t): return t*t*(3-2*t)
# ── SCENE 7: Evoformer (36+48 frames, faster/simpler) ─────────────────
print("Scene 7: Evoformer")
evo_blocks = [
("MSA\nRepresentation", TEAL, 240, 500),
("Pair\nRepresentation", BLUE, 240, 340),
("Row-Wise\nAttention", PURPLE, 500, 500),
("Column-Wise\nAttn", PURPLE, 500, 340),
("Outer Product\nMean", ORANGE, 760, 420),
("Triangle\nUpdates", YELLOW, 980, 420),
("Output\nEmbeddings", GREEN, 1160, 420),
]
# animate in (24 frames)
for i in range(24):
t = ease(i/23)
vis = max(1, int(len(evo_blocks)*min(1, t*1.4)))
fig, ax = new_fig()
draw_section_label(ax, "EVOFORMER BLOCK", color=PURPLE)
ax.text(640, H-70, "48 Transformer blocks: refine MSA + pair representations iteratively",
ha='center', fontsize=18, color=WHITE, alpha=t)
for k,(lbl,col,bx,by) in enumerate(evo_blocks[:vis]):
box = FancyBboxPatch((bx-65,by-34),130,68,boxstyle="round,pad=5",
linewidth=2,edgecolor=col,facecolor=BG)
ax.add_patch(box)
ax.text(bx,by,lbl,ha='center',va='center',fontsize=10,color=col,fontweight='bold')
draw_footer(ax, "Key: MSA & pair representations communicate through attention")
save(fig)
# hold (36 frames)
for _ in range(36):
fig, ax = new_fig()
draw_section_label(ax, "EVOFORMER BLOCK", color=PURPLE)
ax.text(640, H-70, "48 Transformer blocks: refine MSA + pair representations iteratively",
ha='center', fontsize=18, color=WHITE)
for k,(lbl,col,bx,by) in enumerate(evo_blocks):
box = FancyBboxPatch((bx-65,by-34),130,68,boxstyle="round,pad=5",
linewidth=2,edgecolor=col,facecolor=BG)
ax.add_patch(box)
ax.text(bx,by,lbl,ha='center',va='center',fontsize=10,color=col,fontweight='bold')
draw_footer(ax, "Key: MSA & pair representations communicate through attention")
save(fig)
# ── SCENE 8: Structure Module ──────────────────────────────────────────
print("Scene 8: Structure module")
def draw_fold(ax, progress, cx=400, cy=360, scale=150):
n=20
t_v = np.linspace(0, 2*math.pi, n)
xs = cx + scale * np.cos(t_v) * 0.9
ys = cy + scale*0.4*np.sin(t_v*2) + scale*0.3*np.cos(t_v)
vis = max(2, int(n*progress))
ax.plot(xs[:vis], ys[:vis], color=BLUE, lw=4, alpha=0.8, zorder=2)
cols=[GREEN,ORANGE,PURPLE,TEAL,YELLOW,PINK]
for k in range(vis):
ax.plot(xs[k],ys[k],'o',color=cols[k%len(cols)],markersize=10,zorder=3,alpha=0.9)
steps_sm = [
("N-Cα-C backbone frames", ORANGE),
("Invariant Point Attention (IPA)", YELLOW),
("Iterative recycling ×3", PURPLE),
("Side-chain torsion angles", GREEN),
("pLDDT confidence score", TEAL),
]
for i in range(48):
t = ease(i/47)
fig, ax = new_fig()
draw_section_label(ax, "STRUCTURE MODULE", color=ORANGE)
ax.text(W/2, H-80, "Converts pair/MSA embeddings → atomic 3-D coordinates",
ha='center', fontsize=19, color=WHITE, alpha=t)
draw_fold(ax, t, cx=380, cy=360)
for k,(txt,col) in enumerate(steps_sm):
alpha = min(1.0, (t*len(steps_sm)-k)*2)
y = 500 - k*50
ax.plot(720, y, 'o', color=col, markersize=9, alpha=alpha)
ax.text(742, y, txt, va='center', fontsize=14, color=col, alpha=alpha, fontweight='bold')
draw_footer(ax, "IPA: positions/orientations invariant to global rotation & translation")
save(fig)
# ── SCENE 9: pLDDT (48 frames) ────────────────────────────────────────
print("Scene 9: pLDDT")
n_res2 = 30
rng4 = np.random.default_rng(55)
plddt = np.clip(rng4.normal(80, 15, n_res2), 10, 100)
plddt[5:10] = np.clip(rng4.normal(30, 8, 5), 10, 50)
for i in range(48):
t = ease(i/47)
vis = max(1, int(n_res2*t))
fig, ax = new_fig()
draw_section_label(ax, "pLDDT CONFIDENCE", color=GREEN)
ax.text(640, H-80, "Every residue gets a confidence score (0-100)",
ha='center', fontsize=22, color=WHITE, alpha=t)
bar_x0, bar_y0 = 200, 140
bar_w = 880/n_res2
for k in range(vis):
sc = plddt[k]
col = BLUE if sc>=70 else (YELLOW if sc>=50 else ORANGE)
rect = patches.Rectangle((bar_x0+k*bar_w, bar_y0), bar_w-2, sc*3.2,
facecolor=col, alpha=0.85, edgecolor='none')
ax.add_patch(rect)
for score in [0,50,70,100]:
ax.axhline(bar_y0+score*3.2,color=GREY,lw=0.8,alpha=0.4,xmin=0.14,xmax=0.86)
ax.text(190, bar_y0+score*3.2, str(score), ha='right', va='center', fontsize=10, color=GREY)
for k,(col,lbl) in enumerate([(BLUE,"High ≥70"),(YELLOW,"Medium 50-70"),(ORANGE,"Low <50")]):
ax.plot(820, 500-k*38, 's', color=col, markersize=13)
ax.text(838, 500-k*38, lbl, va='center', fontsize=13, color=WHITE)
ax.text(640, 62, "Blue = confident | Orange = disordered (biologically meaningful!)",
ha='center', fontsize=14, color=GREY, alpha=t)
draw_footer(ax, "pLDDT = predicted Local Distance Difference Test (0-100)")
save(fig)
# ── SCENE 10: Impact (48 frames) ──────────────────────────────────────
print("Scene 10: Impact")
impact_items = [
("200M+ Structures", "largest protein DB", BLUE, 220, 520),
("Human Proteome", "~20,000 proteins mapped", GREEN, 620, 520),
("Drug Discovery", "new antibiotic hits", ORANGE, 1020, 520),
("Open Science", "free public database", PURPLE, 220, 340),
("Nobel Prize 2024", "Chemistry awarded", YELLOW, 620, 340),
("AlphaFold 3", "DNA/RNA/small mol too", TEAL, 1020, 340),
]
for i in range(48):
t = ease(i/47)
vis = max(1, int(len(impact_items)*min(1,t*1.3)))
fig, ax = new_fig()
draw_section_label(ax, "IMPACT & LEGACY", color=YELLOW)
ax.text(640, H-70, "AlphaFold has transformed biology and medicine",
ha='center', fontsize=24, color=WHITE, alpha=t)
for k,(big,small,col,bx,by) in enumerate(impact_items[:vis]):
box = FancyBboxPatch((bx-155,by-52),310,104,boxstyle="round,pad=7",
linewidth=2,edgecolor=col,facecolor="#161B22")
ax.add_patch(box)
ax.text(bx,by+16,big,ha='center',va='center',fontsize=17,color=col,fontweight='bold')
ax.text(bx,by-16,small,ha='center',va='center',fontsize=12,color=WHITE)
draw_footer(ax, "AlphaFold DB: ebi.ac.uk/alphafold | AlphaFold3 launched 2024")
save(fig)
# ── SCENE 11: Summary / Closing (48+24 frames) ────────────────────────
print("Scene 11: Closing")
summary_lines = [
("1. Input", "Amino acid sequence", BLUE),
("2. MSA", "Evolutionary co-variation", TEAL),
("3. Evoformer", "48-layer attention network", PURPLE),
("4. Structure", "3-D backbone + side chains", ORANGE),
("5. Confidence", "pLDDT per-residue score", GREEN),
]
for i in range(48):
t = ease(i/47)
fig, ax = new_fig()
ax.text(640, H-60, "AlphaFold2 — Summary",
ha='center', fontsize=34, fontweight='bold', color=BLUE, alpha=t,
path_effects=[pe.withStroke(linewidth=6, foreground=BG)])
for k,(step,desc,col) in enumerate(summary_lines):
alpha = min(1.0, (t*len(summary_lines)-k)*2)
y = H/2+110-k*78
ax.plot(180, y, 'o', color=col, markersize=18, alpha=alpha, zorder=3)
ax.text(212, y+10, step, va='bottom', fontsize=15, color=col, fontweight='bold', alpha=alpha)
ax.text(212, y-12, desc, va='top', fontsize=13, color=WHITE, alpha=alpha)
draw_footer(ax, "Jumper et al. Nature 2021 | Senior et al. Nature 2020 | ebi.ac.uk/alphafold")
save(fig)
# fade-out (18 frames)
for i in range(18):
t = 1 - ease(i/17)
fig, ax = new_fig()
ax.text(640, H/2, "AlphaFold2 — Summary",
ha='center', fontsize=34, fontweight='bold', color=BLUE, alpha=t,
path_effects=[pe.withStroke(linewidth=6, foreground=BG)])
for k,(step,desc,col) in enumerate(summary_lines):
y = H/2+110-k*78
ax.plot(180, y, 'o', color=col, markersize=18, alpha=t, zorder=3)
ax.text(212, y+10, step, va='bottom', fontsize=15, color=col, fontweight='bold', alpha=t)
ax.text(212, y-12, desc, va='top', fontsize=13, color=WHITE, alpha=t)
save(fig)
print(f"\nDone! Total frames written: {frame_idx[0]}")
cd /home/daytona/workspace/alphafold-video && timeout 150 python3 generate_remai…
ls /home/daytona/workspace/alphafold-video/frames/ | wc -l
~/alphafold-video/generate_final.py
"""
Generate final AlphaFold scenes starting from frame 347
"""
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.patches import FancyBboxPatch
import matplotlib.patheffects as pe
import os, math
OUT_DIR = "/home/daytona/workspace/alphafold-video/frames"
W, H = 1280, 720
DPI = 96
figsize = (W / DPI, H / DPI)
BG="#0D1117";BLUE="#58A6FF";GREEN="#3FB950";ORANGE="#F78166"
PURPLE="#BC8CFF";YELLOW="#E3B341";WHITE="#F0F6FC";GREY="#8B949E"
TEAL="#39D353";PINK="#FF7B72"
frame_idx = [347]
def new_fig():
fig, ax = plt.subplots(figsize=figsize, dpi=DPI)
fig.patch.set_facecolor(BG); ax.set_facecolor(BG)
ax.set_xlim(0,W); ax.set_ylim(0,H); ax.axis('off')
return fig, ax
def save(fig):
path = os.path.join(OUT_DIR, f"frame_{frame_idx[0]:05d}.png")
fig.savefig(path, dpi=DPI, bbox_inches='tight', pad_inches=0, facecolor=BG)
frame_idx[0] += 1
plt.close(fig)
def clamp(v, lo=0.0, hi=1.0): return max(lo, min(hi, v))
def ease(t): return t*t*(3-2*t)
def draw_footer(ax, text):
ax.text(W/2,18,text,ha='center',va='bottom',fontsize=11,color=GREY,fontstyle='italic')
def draw_section_label(ax, text, color=YELLOW):
rect = FancyBboxPatch((20,H-50),280,34,boxstyle="round,pad=4",linewidth=1.5,
edgecolor=color,facecolor=BG)
ax.add_patch(rect)
ax.text(160,H-33,text,ha='center',va='center',fontsize=13,color=color,fontweight='bold')
# ── pLDDT (scene 9, 48 frames) ────────────────────────────────────────
print("Scene 9: pLDDT")
n_res = 30
rng = np.random.default_rng(55)
plddt = np.clip(rng.normal(80,15,n_res),10,100)
plddt[5:10] = np.clip(rng.normal(30,8,5),10,50)
for i in range(48):
t = ease(i/47)
vis = max(1,int(n_res*t))
fig,ax = new_fig()
draw_section_label(ax,"pLDDT CONFIDENCE",color=GREEN)
ax.text(640,H-80,"Every residue gets a confidence score (0-100)",
ha='center',fontsize=22,color=WHITE,alpha=clamp(t))
bar_x0,bar_y0 = 200,140; bar_w = 880/n_res
for k in range(vis):
sc=plddt[k]
col=BLUE if sc>=70 else (YELLOW if sc>=50 else ORANGE)
rect=patches.Rectangle((bar_x0+k*bar_w,bar_y0),bar_w-2,sc*3.2,
facecolor=col,alpha=0.85,edgecolor='none')
ax.add_patch(rect)
for sc in [0,50,70,100]:
ax.axhline(bar_y0+sc*3.2,color=GREY,lw=0.8,alpha=0.4,xmin=0.14,xmax=0.86)
ax.text(190,bar_y0+sc*3.2,str(sc),ha='right',va='center',fontsize=10,color=GREY)
for k,(col,lbl) in enumerate([(BLUE,"High ≥70"),(YELLOW,"Medium 50-70"),(ORANGE,"Low <50")]):
ax.plot(820,500-k*38,'s',color=col,markersize=13)
ax.text(838,500-k*38,lbl,va='center',fontsize=13,color=WHITE)
ax.text(640,62,"Blue = confident | Orange = disordered region",
ha='center',fontsize=14,color=GREY,alpha=clamp(t))
draw_footer(ax,"pLDDT = predicted Local Distance Difference Test (0-100)")
save(fig)
# ── Impact (scene 10, 48 frames) ──────────────────────────────────────
print("Scene 10: Impact")
impact_items = [
("200M+ Structures","largest protein DB", BLUE, 220,520),
("Human Proteome", "~20,000 proteins mapped", GREEN, 620,520),
("Drug Discovery", "new antibiotic hits", ORANGE,1020,520),
("Open Science", "free public database", PURPLE, 220,340),
("Nobel Prize 2024","Chemistry awarded", YELLOW, 620,340),
("AlphaFold 3", "DNA/RNA/small mol too", TEAL, 1020,340),
]
for i in range(48):
t = ease(i/47)
vis = max(1,int(len(impact_items)*min(1,t*1.3)))
fig,ax = new_fig()
draw_section_label(ax,"IMPACT & LEGACY",color=YELLOW)
ax.text(640,H-70,"AlphaFold has transformed biology and medicine",
ha='center',fontsize=24,color=WHITE,alpha=clamp(t))
for k,(big,small,col,bx,by) in enumerate(impact_items[:vis]):
box=FancyBboxPatch((bx-155,by-52),310,104,boxstyle="round,pad=7",
linewidth=2,edgecolor=col,facecolor="#161B22")
ax.add_patch(box)
ax.text(bx,by+16,big,ha='center',va='center',fontsize=17,color=col,fontweight='bold')
ax.text(bx,by-16,small,ha='center',va='center',fontsize=12,color=WHITE)
draw_footer(ax,"AlphaFold DB: ebi.ac.uk/alphafold | AlphaFold3 launched 2024")
save(fig)
# ── Summary / Closing (scene 11, 48+18 frames) ────────────────────────
print("Scene 11: Closing")
summary_lines = [
("1. Input", "Amino acid sequence", BLUE),
("2. MSA", "Evolutionary co-variation", TEAL),
("3. Evoformer", "48-layer attention network",PURPLE),
("4. Structure", "3-D backbone + side chains",ORANGE),
("5. Confidence","pLDDT per-residue score", GREEN),
]
for i in range(48):
t = ease(i/47)
fig,ax = new_fig()
ax.text(640,H-60,"AlphaFold2 — Summary",
ha='center',fontsize=34,fontweight='bold',color=BLUE,alpha=clamp(t),
path_effects=[pe.withStroke(linewidth=6,foreground=BG)])
for k,(step,desc,col) in enumerate(summary_lines):
alpha = clamp((t*len(summary_lines)-k)*2)
y = H/2+110-k*78
ax.plot(180,y,'o',color=col,markersize=18,alpha=alpha,zorder=3)
ax.text(212,y+10,step,va='bottom',fontsize=15,color=col,fontweight='bold',alpha=alpha)
ax.text(212,y-12,desc,va='top',fontsize=13,color=WHITE,alpha=alpha)
draw_footer(ax,"Jumper et al. Nature 2021 | Senior et al. Nature 2020 | ebi.ac.uk/alphafold")
save(fig)
# fade-out (18 frames)
for i in range(18):
t = 1-ease(i/17)
fig,ax = new_fig()
ax.text(640,H/2,"AlphaFold2 — Summary",
ha='center',fontsize=34,fontweight='bold',color=BLUE,alpha=clamp(t),
path_effects=[pe.withStroke(linewidth=6,foreground=BG)])
for k,(step,desc,col) in enumerate(summary_lines):
y = H/2+110-k*78
ax.plot(180,y,'o',color=col,markersize=18,alpha=clamp(t),zorder=3)
ax.text(212,y+10,step,va='bottom',fontsize=15,color=col,fontweight='bold',alpha=clamp(t))
ax.text(212,y-12,desc,va='top',fontsize=13,color=WHITE,alpha=clamp(t))
save(fig)
print(f"Done! Total frames: {frame_idx[0]}")
cd /home/daytona/workspace/alphafold-video && timeout 120 python3 generate_final…
https://www.youtube.com/watch?v=3gSy_yN9YBo&t=1s https://www.youtube.com/watch?v=CYncNBMPLLk by using this reference alphafold video , can you make new video animated which easily explaine
python3 -c " from pypdf import PdfReader import os files = { 'how_alphafold':…
rm -rf /home/daytona/workspace/alphafold-video && mkdir -p /home/daytona/workspa…
~/alphafold-video/make_video.py
"""
AlphaFold Animated Explainer - Full Script
Based on: "how AlphaFold actually works", "The brilliance of AlphaFold 3", "What Is AlphaFold NEJM"
Output: frames/*.png → alphafold_explainer.mp4
"""
import numpy as np, math, os
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, Arc, FancyArrowPatch
import matplotlib.patheffects as pe
# ── Config ──────────────────────────────────────────────────────────────
OUT = "/home/daytona/workspace/alphafold-video/frames"
FPS = 20
W, H = 1280, 720
DPI = 80 # lower DPI = faster rendering
FS = (W/DPI, H/DPI) # figsize
# Palette
BG="#0D1117"; BLUE="#58A6FF"; GREEN="#3FB950"; ORANGE="#F78166"
PURPLE="#BC8CFF"; YELLOW="#E3B341"; WHITE="#F0F6FC"; GREY="#8B949E"
TEAL="#56D364"; PINK="#FF7B72"; DARK2="#161B22"
idx = [0] # frame counter
# ── Utilities ─────────────────────────────────────────────────────────
def fig():
f,a = plt.subplots(figsize=FS, dpi=DPI)
f.patch.set_facecolor(BG); a.set_facecolor(BG)
a.set_xlim(0,W); a.set_ylim(0,H); a.axis('off')
return f,a
def save(f, copies=1):
for _ in range(copies):
f.savefig(f"{OUT}/frame_{idx[0]:05d}.png",
dpi=DPI, bbox_inches='tight', pad_inches=0, facecolor=BG)
idx[0] += 1
plt.close(f)
def cl(v): return float(max(0.0, min(1.0, v))) # clamp alpha
def ease(t): return t*t*(3-2*t)
def footer(ax, txt):
ax.text(W/2, 16, txt, ha='center', va='bottom',
fontsize=10, color=GREY, fontstyle='italic')
def badge(ax, txt, color=YELLOW, x=160, y=H-33):
r = FancyBboxPatch((x-140, y-18), 280, 36,
boxstyle="round,pad=4", lw=1.5,
edgecolor=color, facecolor=BG)
ax.add_patch(r)
ax.text(x, y, txt, ha='center', va='center',
fontsize=12, color=color, fontweight='bold')
def title_text(ax, txt, y=H/2+40, size=38, color=BLUE, alpha=1.0):
ax.text(W/2, y, txt, ha='center', va='center',
fontsize=size, fontweight='bold', color=color, alpha=cl(alpha),
path_effects=[pe.withStroke(linewidth=7, foreground=BG)])
def sub_text(ax, txt, y=H/2-20, size=19, color=WHITE, alpha=1.0):
ax.text(W/2, y, txt, ha='center', va='center',
fontsize=size, color=color, alpha=cl(alpha))
def box(ax, cx, cy, w, h, col, label, fsize=12):
r = FancyBboxPatch((cx-w/2, cy-h/2), w, h,
boxstyle="round,pad=7", lw=2,
edgecolor=col, facecolor=BG)
ax.add_patch(r)
for i, line in enumerate(label.split('\n')):
n = len(label.split('\n'))
ax.text(cx, cy + (n//2 - i)*15, line,
ha='center', va='center', fontsize=fsize,
color=col, fontweight='bold')
def arrow(ax, x1, y1, x2, y2, col=WHITE, lw=2):
ax.annotate("", xy=(x2,y2), xytext=(x1,y1),
arrowprops=dict(arrowstyle="->", color=col,
lw=lw, mutation_scale=18))
# ═══════════════════════════════════════════════════════════════════════
# SCENE 1 — TITLE (3 s = 60 frames)
# ═══════════════════════════════════════════════════════════════════════
print("S1 title")
for i in range(60):
t = cl(ease(min(1, i/20)))
f,a = fig()
# starfield
rng = np.random.default_rng(1)
xs,ys = rng.uniform(0,W,180), rng.uniform(0,H,180)
szs = rng.uniform(1,4,180)
for x,y,s in zip(xs,ys,szs):
a.plot(x,y,'o',color=BLUE,ms=s,alpha=0.18*t)
title_text(a,"How AlphaFold Works", y=H/2+70, alpha=t)
sub_text(a,"The AI that solved protein folding — a 50-year unsolved problem", y=H/2+5, alpha=t)
sub_text(a,"DeepMind · AlphaFold2 (2021) & AlphaFold3 (2024)",
y=H/2-45, size=15, color=GREY, alpha=t*0.85)
footer(a,"Sources: Jumper et al. Nature 2021 | NEJM 2021 | AlphaFold3 Nature 2024")
save(f)
# ═══════════════════════════════════════════════════════════════════════
# SCENE 2 — WHAT IS A PROTEIN (5 s = 100 frames)
# ═══════════════════════════════════════════════════════════════════════
print("S2 protein")
aa_colors = [BLUE,GREEN,ORANGE,PURPLE,YELLOW,TEAL,PINK,BLUE,GREEN,ORANGE,PURPLE,YELLOW,TEAL,PINK]
N_AA = 14
def draw_chain(ax, n_vis, cx=500, cy=390, rx=280, ry=60):
ang = [2*math.pi*k/N_AA - math.pi/2 for k in range(N_AA)]
pts = [(cx+rx*math.cos(a), cy+ry*math.sin(a)) for a in ang]
for k in range(min(n_vis-1, N_AA-1)):
ax.plot([pts[k][0],pts[k+1][0]],[pts[k][1],pts[k+1][1]],
color=WHITE, lw=3, alpha=0.45, zorder=1)
for k in range(n_vis):
c = plt.Circle(pts[k], 20, color=aa_colors[k], alpha=0.9, zorder=2)
ax.add_patch(c)
ax.text(pts[k][0],pts[k][1],"AA",ha='center',va='center',
fontsize=8,color=BG,fontweight='bold',zorder=3)
return pts
# Reveal chain (24 f)
for i in range(24):
t = ease(i/23)
n_vis = max(1,int(N_AA*t))
f,a = fig()
badge(a,"PROTEINS 101",TEAL)
title_text(a,"Proteins = Chains of Amino Acids",y=H-90,size=26,alpha=t)
draw_chain(a, n_vis)
sub_text(a,"20 different amino acid types | chain length: ~50 – 35,000",
y=120, size=15, color=GREY, alpha=t)
footer(a,"\"A string of a 20-letter alphabet\" — from the reference video")
save(f)
# Add folding arrow + 3D blob (24 f)
for i in range(24):
t = ease(i/23)
f,a = fig()
badge(a,"PROTEINS 101",TEAL)
title_text(a,"Sequence → Folds → 3-D Shape → Function",y=H-90,size=24)
pts = draw_chain(a, N_AA)
arrow(a, 660,390, 720,390, YELLOW, lw=3)
e = mpatches.Ellipse((870,390),190,120,
facecolor=PURPLE, alpha=0.20*t,
edgecolor=PURPLE, lw=2, linestyle='--')
a.add_patch(e)
a.text(870,390,"3-D\nShape",ha='center',va='center',
fontsize=16,color=PURPLE,alpha=cl(t),fontweight='bold')
sub_text(a,"Some AAs are hydrophobic (inside) · some hydrophilic (outside)",
y=120, size=15, color=GREY)
footer(a,"Anfinsen's dogma: 1-D sequence encodes 3-D structure")
save(f)
# Hold (52 f)
for _ in range(52):
f,a = fig()
badge(a,"PROTEINS 101",TEAL)
title_text(a,"Sequence → Folds → 3-D Shape → Function",y=H-90,size=24)
draw_chain(a, N_AA)
arrow(a, 660,390, 720,390, YELLOW, lw=3)
e = mpatches.Ellipse((870,390),190,120,
facecolor=PURPLE,alpha=0.20,edgecolor=PURPLE,lw=2,linestyle='--')
a.add_patch(e)
a.text(870,390,"3-D\nShape",ha='center',va='center',fontsize=16,color=PURPLE,fontweight='bold')
sub_text(a,"Why does shape matter? → shape determines what the protein does",
y=120, size=15, color=GREY)
footer(a,"Misfolded proteins cause diseases: Alzheimer's, Parkinson's, cystic fibrosis")
save(f)
# ═══════════════════════════════════════════════════════════════════════
# SCENE 3 — THE 50-YEAR PROBLEM (5 s = 100 frames)
# ═══════════════════════════════════════════════════════════════════════
print("S3 problem")
# Levinthal paradox reveal (28 f)
for i in range(28):
t = ease(i/27)
f,a = fig()
badge(a,"THE PROBLEM",ORANGE)
title_text(a,"Levinthal's Paradox",y=H/2+150,size=30,color=ORANGE,alpha=t)
a.text(W/2,H/2+80,"A 100-residue protein has ~10¹³⁰ possible conformations",
ha='center',fontsize=19,color=WHITE,alpha=cl(t))
a.text(W/2,H/2+20,"Sampling 1 per femtosecond = longer than the universe's age",
ha='center',fontsize=17,color=GREY,alpha=cl(t))
a.text(W/2,H/2-40,"Yet proteins fold in microseconds — nature finds the answer instantly",
ha='center',fontsize=18,color=PINK,fontweight='bold',alpha=cl(t))
footer(a,"Traditional methods: X-ray crystallography, NMR, cryo-EM — slow & expensive")
save(f)
# CASP bar chart (72 f)
years = [1994,1996,1998,2000,2002,2004,2006,2008,2010,2012,2014,2016,2018,2020]
scores = [ 25, 27, 30, 33, 36, 38, 40, 43, 45, 50, 54, 56, 58, 92]
BX0,BY0 = 100, 90; BW = 1060/len(years); MAX_S = 100
for i in range(72):
t = ease(i/71)
vis = max(1,int(len(years)*min(1,t*1.3)))
f,a = fig()
badge(a,"THE PROBLEM",ORANGE)
a.text(W/2,H-70,"CASP Competition: How well can AI predict protein structure?",
ha='center',fontsize=20,color=WHITE)
a.text(W/2,H-110,"(GDT score — higher = better)",ha='center',fontsize=14,color=GREY)
# y-axis
for sc,lbl in [(0,"0"),(25,""),(50,"50"),(75,""),(92,"92")]:
yy = BY0+sc*4.8
a.axhline(yy,color=GREY,lw=0.6,alpha=0.3,xmin=0.07,xmax=0.95)
a.text(92,yy,lbl,ha='right',va='center',fontsize=9,color=GREY)
for k,(yr,sc) in enumerate(zip(years[:vis],scores[:vis])):
bh = sc*4.8; bx = BX0+k*BW+4
col = GREEN if yr==2020 else BLUE
r = mpatches.Rectangle((bx,BY0),BW-6,bh,facecolor=col,alpha=0.8,edgecolor='none')
a.add_patch(r)
a.text(bx+BW/2-3,BY0-14,str(yr)[-2:],ha='center',fontsize=8,color=GREY)
if yr==2020:
a.annotate("AlphaFold2\n92/100",
xy=(bx+BW/2-3, BY0+bh+5),
xytext=(bx+BW/2-3-70, BY0+bh+50),
fontsize=11,color=GREEN,fontweight='bold',
arrowprops=dict(arrowstyle="->",color=GREEN,lw=1.5))
footer(a,"CASP = Critical Assessment of protein Structure Prediction (biennial challenge)")
save(f)
# ═══════════════════════════════════════════════════════════════════════
# SCENE 4 — AF2 PIPELINE OVERVIEW (5 s = 100 frames)
# ═══════════════════════════════════════════════════════════════════════
print("S4 pipeline")
PIPE = [
("Input\nSequence", BLUE, 130),
("MSA\nSearch", TEAL, 310),
("Evoformer\n×48", PURPLE,510),
("Structure\nModule", ORANGE,710),
("3-D\nStructure", GREEN, 910),
]
# Animate pipeline appearing (36 f)
for i in range(36):
t = ease(i/35)
vis = max(1,int(len(PIPE)*min(1,t*1.5)))
f,a = fig()
badge(a,"AF2 PIPELINE",PURPLE)
a.text(W/2,H-70,"Sequence → Structure in one forward pass",
ha='center',fontsize=22,color=WHITE,alpha=cl(t))
for k,(lbl,col,cx) in enumerate(PIPE[:vis]):
box(a, cx,H/2, 150,100, col, lbl, fsize=13)
if k < vis-1:
arrow(a, cx+77,H/2, PIPE[k+1][2]-77,H/2)
footer(a,"AlphaFold2 — Jumper et al. Nature 2021")
save(f)
# Hold (64 f)
for _ in range(64):
f,a = fig()
badge(a,"AF2 PIPELINE",PURPLE)
a.text(W/2,H-70,"Sequence → Structure in one forward pass",
ha='center',fontsize=22,color=WHITE)
for k,(lbl,col,cx) in enumerate(PIPE):
box(a, cx,H/2, 150,100, col, lbl, fsize=13)
if k < len(PIPE)-1:
arrow(a, cx+77,H/2, PIPE[k+1][2]-77,H/2)
# labels below each box
notes = ["amino acid\nletters","BLAST / HHblits\ndatabases",
"pair + MSA\nrepresentation","atomic coords\n+ side chains","pLDDT\nconfidence"]
for k,(lbl,col,cx) in enumerate(PIPE):
a.text(cx,H/2-80,notes[k],ha='center',fontsize=10,color=GREY)
footer(a,"AlphaFold2 — Jumper et al. Nature 2021")
save(f)
# ═══════════════════════════════════════════════════════════════════════
# SCENE 5 — MSA (5 s = 100 frames)
# ═══════════════════════════════════════════════════════════════════════
print("S5 MSA")
AA20 = list("ACDEFGHIKLMNPQRSTVWY")
cmap_aa = {a:c for a,c in zip(AA20,
[BLUE,GREEN,ORANGE,PURPLE,YELLOW,TEAL,PINK,
BLUE,GREEN,ORANGE,PURPLE,YELLOW,TEAL,PINK,
BLUE,GREEN,ORANGE,PURPLE,YELLOW,TEAL])}
rng = np.random.default_rng(42)
query = list("MKTAYIAKQRQISFVK")
rows = [query]
for _ in range(7):
row = [aa if rng.random()>0.25 else rng.choice(AA20) for aa in query]
rows.append(row)
ROW_LABELS = ["Query"]+[f"Homolog {k+1}" for k in range(7)]
CW,CH = 44,28; SX=(W-len(query)*CW)/2; SY=H-130
def draw_msa(ax, n_rows):
for r,(row,lbl) in enumerate(zip(rows[:n_rows],ROW_LABELS)):
y = SY-r*(CH+5)
col = YELLOW if r==0 else GREY
ax.text(SX-10,y+CH/2,lbl,ha='right',va='center',fontsize=9,color=col)
for c,aa in enumerate(row):
fc = cmap_aa.get(aa,WHITE)
p = mpatches.Rectangle((SX+c*CW,y),CW-2,CH-2,facecolor=fc,alpha=0.75,edgecolor='none')
ax.add_patch(p)
ax.text(SX+c*CW+(CW-2)/2, y+(CH-2)/2, aa,
ha='center',va='center',fontsize=9,color=BG,fontweight='bold')
# Reveal rows (36 f)
for i in range(36):
t = ease(i/35)
n_rows = max(1,int(len(rows)*min(1,t*1.4)))
f,a = fig()
badge(a,"STEP 1 — MSA",TEAL)
a.text(W/2,H-65,"Search genetic databases for similar proteins in other species",
ha='center',fontsize=20,color=WHITE,alpha=cl(t))
draw_msa(a, n_rows)
a.text(W/2,62,"Co-evolving columns → residues that likely contact each other",
ha='center',fontsize=15,color=TEAL,alpha=cl(t))
footer(a,"\"You put it into a genetic database search and look for similar proteins in different species\" — reference video")
save(f)
# Highlight co-evolution (64 f)
for i in range(64):
t = ease(i/63)
f,a = fig()
badge(a,"STEP 1 — MSA",TEAL)
a.text(W/2,H-65,"Co-evolution encodes contact information",
ha='center',fontsize=20,color=WHITE)
draw_msa(a, len(rows))
# highlight columns 3 & 11 (co-evolving pair)
for col_idx in [3,11]:
h = mpatches.Rectangle((SX+col_idx*CW-2, 62),
CW, len(rows)*(CH+5)+10,
facecolor=YELLOW, alpha=0.12*cl(t), edgecolor=YELLOW,
lw=1.5, linestyle='--')
a.add_patch(h)
a.text(W/2,42,"Highlighted columns mutate together → these residues touch in 3-D",
ha='center',fontsize=14,color=YELLOW,alpha=cl(t))
footer(a,"MSA = Multiple Sequence Alignment — the evolutionary record of the protein")
save(f)
# ═══════════════════════════════════════════════════════════════════════
# SCENE 6 — DISTANCE / PAIR REPRESENTATION (4 s = 80 frames)
# ═══════════════════════════════════════════════════════════════════════
print("S6 pair rep")
NR = 22
rng2 = np.random.default_rng(7)
dmap = np.zeros((NR,NR))
for ii in range(NR):
for jj in range(NR):
dmap[ii,jj] = abs(ii-jj)+rng2.normal(0,1.5)
dmap = np.clip(dmap,0,20)
cmap_d = plt.cm.viridis
for i in range(80):
t = ease(i/79)
vis = max(1,int(NR*t))
f,a = fig()
badge(a,"STEP 2 — PAIR REPRESENTATION",BLUE)
a.text(W/2,H-65,"Predict distance probability between every pair of residues",
ha='center',fontsize=20,color=WHITE,alpha=cl(t))
# sequence bar (left)
for k in range(NR):
a.plot([120+k*20, 136+k*20],[400,400],color=BLUE,lw=10,solid_capstyle='round',alpha=0.8)
a.text(120+NR*10,370,"Input sequence",ha='center',fontsize=12,color=GREY)
# distance heatmap (right)
HX0,HY0 = 490,140; CS=20
for ii in range(vis):
for jj in range(vis):
norm_d = dmap[ii,jj]/20.0
col = cmap_d(norm_d)
r = mpatches.Rectangle((HX0+jj*CS,HY0+ii*CS),CS-1,CS-1,
facecolor=col,edgecolor='none')
a.add_patch(r)
a.text(HX0+NR*CS/2,HY0-18,"Pairwise distance map (residue i vs j)",
ha='center',fontsize=12,color=GREY,alpha=cl(t))
# colorbar legend
for k,lbl in enumerate(["2Å","","11Å","","20Å"]):
a.plot(HX0+NR*CS+20, HY0+k*(NR*CS/4), 's',
color=cmap_d(k/4), ms=12)
if lbl: a.text(HX0+NR*CS+34, HY0+k*(NR*CS/4), lbl,
va='center',fontsize=10,color=GREY)
footer(a,"Pair representation: edges between every two residues (nodes) — NEJM reference")
save(f)
# ═══════════════════════════════════════════════════════════════════════
# SCENE 7 — EVOFORMER (6 s = 120 frames)
# ═══════════════════════════════════════════════════════════════════════
print("S7 evoformer")
EVO = [
("MSA Tower", TEAL, 200, 520),
("Pair Tower", BLUE, 200, 340),
("Row-wise\nAttention",PURPLE,430, 520),
("Col-wise\nAttention",PURPLE,430, 340),
("Outer Product\nMean",ORANGE,680, 430),
("Triangle\nUpdates", YELLOW,900, 430),
("Output\nEmbeddings", GREEN,1110, 430),
]
ARROWS_EVO = [(0,2),(1,3),(2,4),(3,4),(4,5),(5,6)]
# Animate (40 f)
for i in range(40):
t = ease(i/39)
vis = max(1,int(len(EVO)*min(1,t*1.4)))
f,a = fig()
badge(a,"STEP 3 — EVOFORMER",PURPLE)
a.text(W/2,H-65,"Neural network unique to AlphaFold — two towers that communicate",
ha='center',fontsize=19,color=WHITE,alpha=cl(t))
for k,(lbl,col,bx,by) in enumerate(EVO[:vis]):
box(a,bx,by,135,72,col,lbl,fsize=10)
for s,d in ARROWS_EVO:
if s<vis and d<vis:
sx2=EVO[s][2]+69; sy2=EVO[s][3]
dx=EVO[d][2]-69; dy=EVO[d][3]
if sx2 < dx-5:
a.annotate("",xy=(dx,dy),xytext=(sx2,sy2),
arrowprops=dict(arrowstyle="->",color=WHITE,
lw=1.5,mutation_scale=14,
connectionstyle="arc3,rad=0.15"))
footer(a,"48 Evoformer blocks stacked sequentially — progressively deepen understanding")
save(f)
# Hold with annotations (80 f)
annots = [
(430,520,"Looks across\nrows = residue pairs", PURPLE, 430,620),
(430,340,"Looks down\ncolumns = each residue", PURPLE, 430,240),
(680,430,"Updates pair rep\nfrom MSA info", ORANGE, 790,330),
(900,430,"Triangle inequality\ngeometric check", YELLOW, 980,330),
]
for i in range(80):
t = ease(i/79)
ann_vis = max(0,int(len(annots)*min(1,t*1.6)))
f,a = fig()
badge(a,"STEP 3 — EVOFORMER",PURPLE)
a.text(W/2,H-65,"Two towers exchange information every block",
ha='center',fontsize=19,color=WHITE)
for k,(lbl,col,bx,by) in enumerate(EVO):
box(a,bx,by,135,72,col,lbl,fsize=10)
for s,d in ARROWS_EVO:
sx2=EVO[s][2]+69; sy2=EVO[s][3]
dx=EVO[d][2]-69; dy=EVO[d][3]
if sx2 < dx-5:
a.annotate("",xy=(dx,dy),xytext=(sx2,sy2),
arrowprops=dict(arrowstyle="->",color=WHITE,
lw=1.5,mutation_scale=14,
connectionstyle="arc3,rad=0.15"))
for k,(bx,by,txt,col,tx,ty) in enumerate(annots[:ann_vis]):
alpha = cl((t*len(annots)-k)*1.5)
a.text(tx,ty,txt,ha='center',va='center',fontsize=10,
color=col,alpha=alpha,style='italic',
bbox=dict(boxstyle='round,pad=3',facecolor=BG,edgecolor=col,lw=1,alpha=alpha))
footer(a,"Key innovation: pair & MSA representations share information through cross-attention")
save(f)
# ═══════════════════════════════════════════════════════════════════════
# SCENE 8 — STRUCTURE MODULE (5 s = 100 frames)
# ═══════════════════════════════════════════════════════════════════════
print("S8 structure")
def draw_fold(ax, progress, cx=420, cy=380, scale=160):
n = 22
tv = np.linspace(0,2*math.pi,n)
xs = cx + scale*np.cos(tv)*0.85
ys = cy + scale*0.42*np.sin(tv*2) + scale*0.28*np.cos(tv)
vis = max(2,int(n*progress))
ax.plot(xs[:vis],ys[:vis],color=BLUE,lw=5,alpha=0.8,zorder=2)
cols=[GREEN,ORANGE,PURPLE,TEAL,YELLOW,PINK]
for k in range(vis):
ax.plot(xs[k],ys[k],'o',color=cols[k%6],ms=11,zorder=3,alpha=0.9)
if progress>0.55:
for p1,p2 in [(0,12),(3,16),(6,19),(9,18)]:
if p1<vis and p2<vis:
ax.plot([xs[p1],xs[p2]],[ys[p1],ys[p2]],
color=YELLOW,lw=1.5,alpha=0.35,ls='--',zorder=1)
SM_STEPS = [
("N-Cα-C backbone frames", ORANGE),
("Invariant Point Attention", YELLOW),
("Iterative recycling × 3", PURPLE),
("Side-chain torsion angles", GREEN),
("pLDDT confidence per residue",TEAL),
]
for i in range(100):
t = ease(i/99)
f,a = fig()
badge(a,"STEP 4 — STRUCTURE MODULE",ORANGE)
a.text(W/2,H-65,"Converts Evoformer embeddings → 3-D atomic coordinates",
ha='center',fontsize=19,color=WHITE,alpha=cl(t))
draw_fold(a, cl(t))
for k,(txt,col) in enumerate(SM_STEPS):
alpha = cl((t*len(SM_STEPS)-k)*1.8)
y = 500-k*52
a.plot(740,y,'o',color=col,ms=10,alpha=alpha,zorder=3)
a.text(762,y,txt,va='center',fontsize=14,color=col,
alpha=alpha,fontweight='bold')
footer(a,"IPA = positions & orientations invariant to global rotation/translation")
save(f)
# ═══════════════════════════════════════════════════════════════════════
# SCENE 9 — pLDDT CONFIDENCE (4 s = 80 frames)
# ═══════════════════════════════════════════════════════════════════════
print("S9 pLDDT")
NR2=32
rng3=np.random.default_rng(77)
plddt=np.clip(rng3.normal(80,14,NR2),10,100)
plddt[6:11]=np.clip(rng3.normal(28,7,5),10,50)
for i in range(80):
t = ease(i/79)
vis=max(1,int(NR2*t))
f,a = fig()
badge(a,"pLDDT CONFIDENCE",GREEN)
a.text(W/2,H-65,"Each residue receives a per-residue confidence score (0–100)",
ha='center',fontsize=20,color=WHITE,alpha=cl(t))
BX0,BY0=160,120; BW=960/NR2
for k in range(vis):
sc=plddt[k]
col=BLUE if sc>=70 else (YELLOW if sc>=50 else ORANGE)
r=mpatches.Rectangle((BX0+k*BW,BY0),BW-2,sc*3.8,
facecolor=col,alpha=0.85,edgecolor='none')
a.add_patch(r)
for sc,lbl in [(0,"0"),(50,"50"),(70,"70"),(100,"100")]:
a.axhline(BY0+sc*3.8,color=GREY,lw=0.7,alpha=0.3,xmin=0.10,xmax=0.90)
a.text(152,BY0+sc*3.8,lbl,ha='right',va='center',fontsize=9,color=GREY)
# legend
for k,(col,lbl) in enumerate([(BLUE,"High ≥70 (confident)"),(YELLOW,"Medium 50–69"),(ORANGE,"Low <50 (disordered)")]):
a.plot(240+k*320,H/2-40,'s',color=col,ms=13)
a.text(260+k*320,H/2-40,lbl,va='center',fontsize=12,color=WHITE)
a.text(W/2,55,"Orange regions are often intrinsically disordered — biologically meaningful!",
ha='center',fontsize=14,color=ORANGE,alpha=cl(t))
footer(a,"pLDDT = predicted Local Distance Difference Test (per-residue accuracy estimate)")
save(f)
# ═══════════════════════════════════════════════════════════════════════
# SCENE 10 — ALPHAFOLD 3 (5 s = 100 frames)
# ═══════════════════════════════════════════════════════════════════════
print("S10 AF3")
AF3_ITEMS = [
("Proteins", BLUE, 200, 480),
("DNA & RNA", GREEN, 200, 330),
("Small\nMolecules\n(Ligands)", ORANGE, 540, 480),
("Complexes", PURPLE, 540, 330),
("Diffusion\nModel", TEAL, 880, 405),
("All in\none model", YELLOW,1100,405),
]
for i in range(100):
t = ease(i/99)
vis=max(1,int(len(AF3_ITEMS)*min(1,t*1.4)))
f,a = fig()
badge(a,"ALPHAFOLD 3",TEAL)
a.text(W/2,H-65,"AlphaFold3: proteins + DNA + RNA + ligands — all in one model",
ha='center',fontsize=20,color=WHITE,alpha=cl(t))
for k,(lbl,col,bx,by) in enumerate(AF3_ITEMS[:vis]):
box(a,bx,by,160,100,col,lbl,fsize=12)
if vis>=5:
for src in range(4):
sx2=AF3_ITEMS[src][2]+82; sy2=AF3_ITEMS[src][3]
dx=AF3_ITEMS[4][2]-82; dy=AF3_ITEMS[4][3]
a.annotate("",xy=(dx,dy),xytext=(sx2,sy2),
arrowprops=dict(arrowstyle="->",color=WHITE,
lw=1.2,mutation_scale=12,
connectionstyle="arc3,rad=0.1"))
if vis>=6:
arrow(a,AF3_ITEMS[4][2]+82,AF3_ITEMS[4][3],AF3_ITEMS[5][2]-82,AF3_ITEMS[5][3])
key_pts = [
(W/2,80,"Uses diffusion (like Midjourney for AI art) instead of rigid triangle geometry",
TEAL,13),
(W/2,48,"Multimodal training: learning proteins helps it understand ligands — and vice versa",
GREY,12),
]
for tx,ty,txt,col,fs in key_pts:
a.text(tx,ty,txt,ha='center',fontsize=fs,color=col,alpha=cl(t))
footer(a,"AlphaFold3 — Abramson et al. Nature 2024 | Uses diffusion, not rigid-body IPA")
save(f)
# ═══════════════════════════════════════════════════════════════════════
# SCENE 11 — IMPACT (5 s = 100 frames)
# ═══════════════════════════════════════════════════════════════════════
print("S11 impact")
IMPACT = [
("200M+\nStructures", "world's largest protein DB", BLUE, 210,500),
("Human\nProteome", "all ~20,000 human proteins", GREEN, 590,500),
("Drug\nDiscovery", "new antibiotic candidates found", ORANGE,970,500),
("Free &\nOpen", "ebi.ac.uk/alphafold", PURPLE,210,330),
("Nobel Prize\n2024", "Chemistry — Hassabis & Baker", YELLOW,590,330),
("AlphaFold 3", "DNA, RNA, ligands too (2024)", TEAL, 970,330),
]
for i in range(100):
t = ease(i/99)
vis=max(1,int(len(IMPACT)*min(1,t*1.3)))
f,a = fig()
badge(a,"IMPACT & LEGACY",YELLOW)
a.text(W/2,H-65,"AlphaFold has transformed structural biology and medicine",
ha='center',fontsize=22,color=WHITE,alpha=cl(t))
for k,(big,small,col,bx,by) in enumerate(IMPACT[:vis]):
r=FancyBboxPatch((bx-160,by-58),320,116,boxstyle="round,pad=8",
lw=2,edgecolor=col,facecolor=DARK2)
a.add_patch(r)
a.text(bx,by+18,big,ha='center',va='center',fontsize=17,color=col,fontweight='bold')
a.text(bx,by-18,small,ha='center',va='center',fontsize=12,color=WHITE)
footer(a,"\"It didn't just solve protein folding — it opened the door to molecular AI\" — inspired by reference videos")
save(f)
# ═══════════════════════════════════════════════════════════════════════
# SCENE 12 — SUMMARY / CLOSING (5 s = 100 frames)
# ═══════════════════════════════════════════════════════════════════════
print("S12 summary")
SUMMARY = [
("1 Input", "Amino acid sequence (1-D string)", BLUE),
("2 MSA", "Evolutionary relatives across species", TEAL),
("3 Evoformer", "48 transformer blocks (pair + MSA towers)", PURPLE),
("4 Structure Mod.", "3-D backbone frames via IPA + recycling", ORANGE),
("5 Confidence", "pLDDT score per residue (0–100)", GREEN),
("6 AlphaFold 3", "Diffusion → proteins, DNA, RNA, ligands", YELLOW),
]
# Reveal (48 f)
for i in range(48):
t = ease(i/47)
f,a = fig()
title_text(a,"AlphaFold — How It Works",y=H-65,size=30,alpha=t)
for k,(step,desc,col) in enumerate(SUMMARY):
alpha = cl((t*len(SUMMARY)-k)*1.6)
y = H/2+120-k*70
a.plot(130,y,'o',color=col,ms=17,alpha=alpha,zorder=3)
a.text(158,y+9,step,va='bottom',fontsize=14,color=col,
fontweight='bold',alpha=alpha)
a.text(158,y-11,desc,va='top',fontsize=12,color=WHITE,alpha=alpha)
footer(a,"Jumper et al. Nature 2021 | Abramson et al. Nature 2024 | ebi.ac.uk/alphafold")
save(f)
# Hold (34 f)
for _ in range(34):
f,a = fig()
title_text(a,"AlphaFold — How It Works",y=H-65,size=30)
for k,(step,desc,col) in enumerate(SUMMARY):
y = H/2+120-k*70
a.plot(130,y,'o',color=col,ms=17,zorder=3)
a.text(158,y+9,step,va='bottom',fontsize=14,color=col,fontweight='bold')
a.text(158,y-11,desc,va='top',fontsize=12,color=WHITE)
footer(a,"Jumper et al. Nature 2021 | Abramson et al. Nature 2024 | ebi.ac.uk/alphafold")
save(f)
# Fade out (18 f)
for i in range(18):
t = 1-ease(i/17)
f,a = fig()
title_text(a,"AlphaFold — How It Works",y=H-65,size=30,alpha=t)
for k,(step,desc,col) in enumerate(SUMMARY):
y = H/2+120-k*70
a.plot(130,y,'o',color=col,ms=17,alpha=cl(t),zorder=3)
a.text(158,y+9,step,va='bottom',fontsize=14,color=col,fontweight='bold',alpha=cl(t))
a.text(158,y-11,desc,va='top',fontsize=12,color=WHITE,alpha=cl(t))
save(f)
print(f"\nAll done! Total frames: {idx[0]}")
cd /home/daytona/workspace/alphafold-video && timeout 170 python3 make_video.py …
ls /home/daytona/workspace/alphafold-video/frames/ | wc -l && ls /home/daytona/w…
~/alphafold-video/make_remaining.py
"""
Fast remaining scenes: S7-S12 (starting from frame 259)
Uses PIL for speed, matplotlib only for complex drawings
"""
import numpy as np, math, os
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch
import matplotlib.patheffects as pe
OUT = "/home/daytona/workspace/alphafold-video/frames"
FPS = 20; W,H = 1280,720; DPI = 80; FS = (W/DPI,H/DPI)
BG="#0D1117";BLUE="#58A6FF";GREEN="#3FB950";ORANGE="#F78166"
PURPLE="#BC8CFF";YELLOW="#E3B341";WHITE="#F0F6FC";GREY="#8B949E"
TEAL="#56D364";PINK="#FF7B72";DARK2="#161B22"
idx=[259]
def fig():
f,a=plt.subplots(figsize=FS,dpi=DPI)
f.patch.set_facecolor(BG);a.set_facecolor(BG)
a.set_xlim(0,W);a.set_ylim(0,H);a.axis('off')
return f,a
def save(f):
f.savefig(f"{OUT}/frame_{idx[0]:05d}.png",dpi=DPI,
bbox_inches='tight',pad_inches=0,facecolor=BG)
idx[0]+=1; plt.close(f)
def cl(v): return float(max(0.0,min(1.0,v)))
def ease(t): return t*t*(3-2*t)
def footer(ax,txt):
ax.text(W/2,16,txt,ha='center',va='bottom',fontsize=10,color=GREY,fontstyle='italic')
def badge(ax,txt,color=YELLOW):
r=FancyBboxPatch((20,H-50),280,36,boxstyle="round,pad=4",lw=1.5,edgecolor=color,facecolor=BG)
ax.add_patch(r); ax.text(160,H-32,txt,ha='center',va='center',fontsize=12,color=color,fontweight='bold')
def box(ax,cx,cy,w,h,col,lbl,fs=11):
r=FancyBboxPatch((cx-w/2,cy-h/2),w,h,boxstyle="round,pad=6",lw=2,edgecolor=col,facecolor=BG)
ax.add_patch(r)
for i,ln in enumerate(lbl.split('\n')):
n=len(lbl.split('\n')); ax.text(cx,cy+(n//2-i)*13,ln,ha='center',va='center',fontsize=fs,color=col,fontweight='bold')
def arrow(ax,x1,y1,x2,y2,col=WHITE,lw=2):
ax.annotate("",xy=(x2,y2),xytext=(x1,y1),arrowprops=dict(arrowstyle="->",color=col,lw=lw,mutation_scale=18))
# ══ S7: EVOFORMER (60 frames) ══════════════════════════════════════════
print("S7")
EVO=[
("MSA Tower", TEAL, 200,520),("Pair Tower", BLUE, 200,340),
("Row-wise\nAttention",PURPLE,420,520),("Col-wise\nAttention",PURPLE,420,340),
("Outer\nProduct", ORANGE,660,430),("Triangle\nUpdates",YELLOW,880,430),
("Output", GREEN,1080,430),
]
EARR=[(0,2),(1,3),(2,4),(3,4),(4,5),(5,6)]
for i in range(60):
t=ease(i/59); vis=max(1,int(len(EVO)*min(1,t*1.5)))
f,a=fig()
badge(a,"STEP 3 — EVOFORMER",PURPLE)
a.text(W/2,H-65,"Two towers share info every block — 48 blocks total",
ha='center',fontsize=20,color=WHITE,alpha=cl(t))
for k,(lbl,col,bx,by) in enumerate(EVO[:vis]):
box(a,bx,by,128,70,col,lbl,fs=10)
for s,d in EARR:
if s<vis and d<vis:
sx2=EVO[s][2]+66;sy2=EVO[s][3];dx=EVO[d][2]-66;dy=EVO[d][3]
if sx2<dx-5:
a.annotate("",xy=(dx,dy),xytext=(sx2,sy2),
arrowprops=dict(arrowstyle="->",color=WHITE,lw=1.5,mutation_scale=13,
connectionstyle="arc3,rad=0.15"))
if i>30:
alpha=cl((t-0.5)*3)
a.text(W/2,68,"Row attention = residue-pair relationships | Col attention = per-residue context | Triangle = geometric consistency",
ha='center',fontsize=11,color=GREY,alpha=alpha)
footer(a,"Evoformer — unique to AlphaFold, refines both pair and MSA representations simultaneously")
save(f)
# ══ S8: STRUCTURE MODULE (60 frames) ══════════════════════════════════
print("S8")
def draw_fold(ax,prog,cx=400,cy=380,sc=155):
n=20; tv=np.linspace(0,2*math.pi,n)
xs=cx+sc*np.cos(tv)*0.85; ys=cy+sc*0.42*np.sin(tv*2)+sc*0.28*np.cos(tv)
vis=max(2,int(n*prog)); ax.plot(xs[:vis],ys[:vis],color=BLUE,lw=5,alpha=0.8,zorder=2)
c6=[GREEN,ORANGE,PURPLE,TEAL,YELLOW,PINK]
for k in range(vis): ax.plot(xs[k],ys[k],'o',color=c6[k%6],ms=11,zorder=3,alpha=0.9)
if prog>0.5:
for p1,p2 in [(0,11),(3,15),(7,18)]:
if p1<vis and p2<vis:
ax.plot([xs[p1],xs[p2]],[ys[p1],ys[p2]],color=YELLOW,lw=1.5,alpha=0.3,ls='--')
SM=[("N-Cα-C backbone frames",ORANGE),("Invariant Point Attention",YELLOW),
("Iterative recycling ×3",PURPLE),("Side-chain torsions",GREEN),("pLDDT score",TEAL)]
for i in range(60):
t=ease(i/59); f,a=fig()
badge(a,"STEP 4 — STRUCTURE MODULE",ORANGE)
a.text(W/2,H-65,"Converts Evoformer embeddings → 3-D atomic coordinates",
ha='center',fontsize=19,color=WHITE,alpha=cl(t))
draw_fold(a,cl(t))
for k,(txt,col) in enumerate(SM):
alpha=cl((t*len(SM)-k)*1.8); y=500-k*54
a.plot(750,y,'o',color=col,ms=10,alpha=alpha,zorder=3)
a.text(772,y,txt,va='center',fontsize=14,color=col,alpha=alpha,fontweight='bold')
footer(a,"IPA keeps positions/orientations invariant to global rotation and translation")
save(f)
# ══ S9: pLDDT (48 frames) ══════════════════════════════════════════════
print("S9")
NR2=32; rng3=np.random.default_rng(77)
plddt=np.clip(rng3.normal(80,14,NR2),10,100); plddt[6:11]=np.clip(rng3.normal(28,7,5),10,50)
for i in range(48):
t=ease(i/47); vis=max(1,int(NR2*t)); f,a=fig()
badge(a,"pLDDT CONFIDENCE",GREEN)
a.text(W/2,H-65,"Per-residue confidence score tells you how much to trust each prediction",
ha='center',fontsize=19,color=WHITE,alpha=cl(t))
BX0,BY0=140,110; BW=1000/NR2
for k in range(vis):
sc=plddt[k]; col=BLUE if sc>=70 else (YELLOW if sc>=50 else ORANGE)
r=mpatches.Rectangle((BX0+k*BW,BY0),BW-2,sc*3.8,facecolor=col,alpha=0.85,edgecolor='none')
a.add_patch(r)
for sc,lbl in [(0,"0"),(50,"50"),(70,"70"),(100,"100")]:
a.axhline(BY0+sc*3.8,color=GREY,lw=0.7,alpha=0.3,xmin=0.08,xmax=0.92)
a.text(132,BY0+sc*3.8,lbl,ha='right',va='center',fontsize=9,color=GREY)
for k,(col,lbl) in enumerate([(BLUE,"High ≥70 (confident)"),(YELLOW,"Medium 50–69"),(ORANGE,"Low <50 (disordered)")]):
a.plot(200+k*360,H/2-30,'s',color=col,ms=13)
a.text(220+k*360,H/2-30,lbl,va='center',fontsize=13,color=WHITE)
a.text(W/2,52,"Orange = intrinsically disordered regions — still biologically important!",
ha='center',fontsize=13,color=ORANGE,alpha=cl(t))
footer(a,"pLDDT = predicted Local Distance Difference Test | 0=bad, 100=perfect")
save(f)
# ══ S10: AF3 (60 frames) ═══════════════════════════════════════════════
print("S10")
AF3=[
("Proteins", BLUE, 200,480),("DNA & RNA", GREEN, 200,330),
("Ligands\n(drugs)",ORANGE,520,480),("Complexes",PURPLE,520,330),
("Diffusion\nModel",TEAL,840,405),("One\nFramework",YELLOW,1080,405),
]
for i in range(60):
t=ease(i/59); vis=max(1,int(len(AF3)*min(1,t*1.4))); f,a=fig()
badge(a,"ALPHAFOLD 3",TEAL)
a.text(W/2,H-65,"AlphaFold3 handles proteins, DNA, RNA and drug-like molecules",
ha='center',fontsize=19,color=WHITE,alpha=cl(t))
for k,(lbl,col,bx,by) in enumerate(AF3[:vis]):
box(a,bx,by,155,95,col,lbl,fs=12)
if vis>=5:
for src in range(4):
sx2=AF3[src][2]+79;sy2=AF3[src][3];dx=AF3[4][2]-79;dy=AF3[4][3]
a.annotate("",xy=(dx,dy),xytext=(sx2,sy2),
arrowprops=dict(arrowstyle="->",color=WHITE,lw=1.2,mutation_scale=11,
connectionstyle="arc3,rad=0.1"))
if vis>=6:
arrow(a,AF3[4][2]+79,AF3[4][3],AF3[5][2]-79,AF3[5][3])
if i>30:
alpha=cl((t-0.5)*3)
a.text(W/2,75,"Uses diffusion (like Midjourney) instead of rigid-body geometry → simpler & more general",
ha='center',fontsize=13,color=TEAL,alpha=alpha)
a.text(W/2,48,"Multimodal training: learning proteins improves ligand prediction — and vice versa",
ha='center',fontsize=12,color=GREY,alpha=alpha)
footer(a,"AlphaFold3 — Abramson et al. Nature 2024")
save(f)
# ══ S11: IMPACT (60 frames) ════════════════════════════════════════════
print("S11")
IMP=[
("200M+\nStructures","world's largest protein DB",BLUE,210,500),
("Human\nProteome","all ~20,000 human proteins",GREEN,590,500),
("Drug\nDiscovery","new antibiotic candidates",ORANGE,970,500),
("Free &\nOpen","ebi.ac.uk/alphafold",PURPLE,210,330),
("Nobel Prize\n2024","Chemistry — Hassabis & Baker",YELLOW,590,330),
("AlphaFold 3","DNA, RNA, ligands (2024)",TEAL,970,330),
]
for i in range(60):
t=ease(i/59); vis=max(1,int(len(IMP)*min(1,t*1.3))); f,a=fig()
badge(a,"IMPACT",YELLOW)
a.text(W/2,H-65,"AlphaFold has transformed structural biology and medicine",
ha='center',fontsize=22,color=WHITE,alpha=cl(t))
for k,(big,small,col,bx,by) in enumerate(IMP[:vis]):
r=FancyBboxPatch((bx-155,by-55),310,110,boxstyle="round,pad=7",lw=2,edgecolor=col,facecolor=DARK2)
a.add_patch(r)
a.text(bx,by+18,big,ha='center',va='center',fontsize=16,color=col,fontweight='bold')
a.text(bx,by-18,small,ha='center',va='center',fontsize=12,color=WHITE)
footer(a,"Before AlphaFold: ~170,000 experimentally solved structures | After: 200 million+")
save(f)
# ══ S12: SUMMARY (60+18 frames) ════════════════════════════════════════
print("S12")
SUM=[
("1 Input", "Amino acid sequence (1-D)", BLUE),
("2 MSA", "Evolutionary homologs across species", TEAL),
("3 Evoformer", "48 transformer blocks, pair + MSA towers",PURPLE),
("4 Structure Mod.","3-D backbone + side chains via IPA", ORANGE),
("5 pLDDT", "Per-residue confidence (0–100)", GREEN),
("6 AlphaFold 3", "Diffusion → proteins, DNA, RNA, ligands", YELLOW),
]
for i in range(60):
t=ease(i/59); f,a=fig()
a.text(W/2,H-60,"AlphaFold — Complete Summary",ha='center',fontsize=30,
fontweight='bold',color=BLUE,alpha=cl(t),
path_effects=[pe.withStroke(linewidth=7,foreground=BG)])
for k,(step,desc,col) in enumerate(SUM):
alpha=cl((t*len(SUM)-k)*1.6); y=H/2+120-k*72
a.plot(120,y,'o',color=col,ms=16,alpha=alpha,zorder=3)
a.text(148,y+9,step,va='bottom',fontsize=14,color=col,fontweight='bold',alpha=alpha)
a.text(148,y-11,desc,va='top',fontsize=12,color=WHITE,alpha=alpha)
footer(a,"Jumper et al. Nature 2021 | Abramson et al. Nature 2024 | ebi.ac.uk/alphafold")
save(f)
# fade out
for i in range(18):
t=1-ease(i/17); f,a=fig()
a.text(W/2,H/2,"AlphaFold — Complete Summary",ha='center',fontsize=30,
fontweight='bold',color=BLUE,alpha=cl(t),
path_effects=[pe.withStroke(linewidth=7,foreground=BG)])
for k,(step,desc,col) in enumerate(SUM):
y=H/2+120-k*72
a.plot(120,y,'o',color=col,ms=16,alpha=cl(t),zorder=3)
a.text(148,y+9,step,va='bottom',fontsize=14,color=col,fontweight='bold',alpha=cl(t))
a.text(148,y-11,desc,va='top',fontsize=12,color=WHITE,alpha=cl(t))
save(f)
print(f"Done! Total frames: {idx[0]}")
cd /home/daytona/workspace/alphafold-video && timeout 165 python3 make_remaining…
make animated ppt
mkdir -p /home/daytona/workspace/alphafold-ppt && ls /home/daytona/workspace/alp…
~/alphafold-ppt/build_animated.js
// AlphaFold Animated PowerPoint Presentation
// Rich design: dark theme, animated transitions, diagrams, charts
// Based on: "how AlphaFold actually works", "The brilliance of AlphaFold 3", "What Is AlphaFold NEJM"
const pptxgen = require("pptxgenjs");
const pres = new pptxgen();
pres.layout = "LAYOUT_16x9";
pres.title = "How AlphaFold Works";
pres.author = "Based on DeepMind AlphaFold Research";
// ── Palette (dark tech theme) ────────────────────────────────────────
const C = {
bg: "0D1117",
bg2: "161B22",
blue: "58A6FF",
green: "3FB950",
orange: "F78166",
purple: "BC8CFF",
yellow: "E3B341",
teal: "56D364",
pink: "FF7B72",
white: "F0F6FC",
grey: "8B949E",
dgrey: "30363D",
};
// ── Helpers ──────────────────────────────────────────────────────────
const makeShadow = () => ({ type: "outer", blur: 8, offset: 3, angle: 135, color: "000000", opacity: 0.35 });
function addBg(slide, color = C.bg) {
slide.background = { color };
}
function addHeader(slide, text, color = C.blue) {
slide.addShape(pres.shapes.RECTANGLE, {
x: 0, y: 0, w: "100%", h: 0.08,
fill: { color },
line: { color, width: 0 }
});
}
function addFooter(slide, text = "AlphaFold · DeepMind · Jumper et al. Nature 2021 | Abramson et al. Nature 2024") {
slide.addShape(pres.shapes.RECTANGLE, {
x: 0, y: 5.45, w: "100%", h: 0.18,
fill: { color: C.bg2 },
line: { color: C.dgrey, width: 0.5 }
});
slide.addText(text, {
x: 0.2, y: 5.45, w: 9.6, h: 0.18,
fontSize: 7, color: C.grey, valign: "middle", italic: true
});
}
function badge(slide, text, color, x, y, w = 2.2, h = 0.3) {
slide.addShape(pres.shapes.ROUNDED_RECTANGLE, {
x, y, w, h,
fill: { color: C.bg2 },
line: { color, width: 1.5 },
rectRadius: 0.06
});
slide.addText(text, {
x, y, w, h,
fontSize: 9, color, bold: true, align: "center", valign: "middle", margin: 0
});
}
function card(slide, x, y, w, h, title, body, accentColor, animate = null) {
const opts = {
x, y, w, h,
fill: { color: C.bg2 },
line: { color: accentColor, width: 1.5 },
shadow: makeShadow()
};
if (animate) opts.animate = animate; // not standard in pptxgenjs, handled via transitions
slide.addShape(pres.shapes.RECTANGLE, opts);
// accent left bar
slide.addShape(pres.shapes.RECTANGLE, {
x, y: y + 0.05, w: 0.04, h: h - 0.1,
fill: { color: accentColor }, line: { color: accentColor, width: 0 }
});
if (title) {
slide.addText(title, {
x: x + 0.12, y: y + 0.06, w: w - 0.18, h: 0.3,
fontSize: 12, color: accentColor, bold: true, valign: "top", margin: 0
});
}
if (body) {
slide.addText(body, {
x: x + 0.12, y: y + (title ? 0.38 : 0.1), w: w - 0.18, h: h - (title ? 0.5 : 0.2),
fontSize: 10, color: C.white, valign: "top", wrap: true, margin: 0
});
}
}
function stepBox(slide, x, y, w, h, stepNum, title, color) {
slide.addShape(pres.shapes.RECTANGLE, {
x, y, w, h,
fill: { color: C.bg2 },
line: { color, width: 2 },
shadow: makeShadow()
});
slide.addShape(pres.shapes.OVAL, {
x: x + 0.08, y: y + 0.07, w: 0.32, h: 0.32,
fill: { color }, line: { color, width: 0 }
});
slide.addText(String(stepNum), {
x: x + 0.08, y: y + 0.07, w: 0.32, h: 0.32,
fontSize: 10, color: C.bg, bold: true, align: "center", valign: "middle", margin: 0
});
slide.addText(title, {
x: x + 0.46, y: y + 0.09, w: w - 0.56, h: 0.3,
fontSize: 11, color, bold: true, valign: "top", margin: 0
});
}
function arrowRight(slide, x, y, color = C.white) {
slide.addShape(pres.shapes.RECTANGLE, {
x, y: y + 0.06, w: 0.3, h: 0.07,
fill: { color }, line: { color, width: 0 }
});
slide.addText("▶", { x: x + 0.22, y: y - 0.02, w: 0.18, h: 0.2,
fontSize: 11, color, margin: 0, align: "center", valign: "middle" });
}
// ════════════════════════════════════════════════════════════════════
// SLIDE 1 — TITLE
// ════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
addBg(s);
// Decorative background circles
s.addShape(pres.shapes.OVAL, {
x: -1.5, y: -1, w: 5, h: 5,
fill: { color: C.blue, transparency: 92 },
line: { color: C.blue, width: 0 }
});
s.addShape(pres.shapes.OVAL, {
x: 7.5, y: 2.5, w: 4, h: 4,
fill: { color: C.purple, transparency: 92 },
line: { color: C.purple, width: 0 }
});
s.addShape(pres.shapes.OVAL, {
x: 4, y: 3.5, w: 3, h: 3,
fill: { color: C.teal, transparency: 94 },
line: { color: C.teal, width: 0 }
});
// Top accent bar
addHeader(s, "", C.blue);
s.addShape(pres.shapes.RECTANGLE, {
x: 0, y: 0.08, w: "100%", h: 0.02,
fill: { color: C.purple, transparency: 50 },
line: { color: C.purple, width: 0 }
});
// Main title
s.addText("How AlphaFold Works", {
x: 0.4, y: 1.0, w: 9.2, h: 1.3,
fontSize: 48, color: C.white, bold: true, align: "center",
glow: { size: 8, color: C.blue, opacity: 0.4 }
});
// Subtitle line
s.addShape(pres.shapes.RECTANGLE, {
x: 3.5, y: 2.38, w: 3, h: 0.04,
fill: { color: C.blue }, line: { color: C.blue, width: 0 }
});
s.addText("The AI That Solved a 50-Year Unsolved Problem", {
x: 0.5, y: 2.5, w: 9, h: 0.55,
fontSize: 20, color: C.blue, align: "center", bold: false
});
s.addText("AlphaFold2 (2021) · AlphaFold3 (2024) · DeepMind / Google", {
x: 1, y: 3.15, w: 8, h: 0.35,
fontSize: 13, color: C.grey, align: "center"
});
// Pills
const pills = [
{ text: "🔬 Protein Folding", color: C.teal, x: 1.2 },
{ text: "🤖 Deep Learning", color: C.purple, x: 3.5 },
{ text: "💊 Drug Discovery", color: C.orange, x: 5.8 },
{ text: "🏆 Nobel Prize 2024", color: C.yellow, x: 8.0 },
];
pills.forEach(p => {
s.addShape(pres.shapes.ROUNDED_RECTANGLE, {
x: p.x - 0.9, y: 3.75, w: 1.7, h: 0.32,
fill: { color: C.bg2 }, line: { color: p.color, width: 1.2 }, rectRadius: 0.08
});
s.addText(p.text, {
x: p.x - 0.9, y: 3.75, w: 1.7, h: 0.32,
fontSize: 9, color: p.color, align: "center", valign: "middle", bold: true, margin: 0
});
});
addFooter(s);
s.addText("Sources: Jumper et al. Nature 2021 | Abramson et al. Nature 2024 | NEJM 2021", {
x: 0.3, y: 5.2, w: 9.4, h: 0.22, fontSize: 8, color: C.grey, align: "center"
});
s.slideNumber = { x: 9.6, y: 5.38, h: 0.2, w: 0.4, fontSize: 7, color: C.grey };
}
// ════════════════════════════════════════════════════════════════════
// SLIDE 2 — WHAT IS A PROTEIN?
// ════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
addBg(s);
addHeader(s, "", C.teal);
badge(s, "PROTEINS 101", C.teal, 0.25, 0.15, 1.8, 0.28);
s.addText("What Is a Protein?", {
x: 2.3, y: 0.12, w: 7.4, h: 0.4,
fontSize: 26, color: C.white, bold: true, valign: "middle"
});
// Left panel: text explanation
card(s, 0.2, 0.55, 4.2, 4.7, null, null, C.teal);
const leftContent = [
{ text: "🧬 Chain of Amino Acids\n", options: { bold: true, color: C.teal, fontSize: 14, breakLine: false } },
{ text: "Proteins are strings of amino acids — like words made from a 20-letter alphabet.\n\n", options: { color: C.white, fontSize: 11 } },
{ text: "🔢 20 Standard Types\n", options: { bold: true, color: C.teal, fontSize: 14, breakLine: false } },
{ text: "Each amino acid has different chemistry: hydrophobic, charged, polar...\n\n", options: { color: C.white, fontSize: 11 } },
{ text: "📐 Sequence → 3-D Structure\n", options: { bold: true, color: C.teal, fontSize: 14, breakLine: false } },
{ text: "In water, the chain folds into a precise 3-D shape determined by its sequence.\n\n", options: { color: C.white, fontSize: 11 } },
{ text: "⚡ Structure → Function\n", options: { bold: true, color: C.teal, fontSize: 14, breakLine: false } },
{ text: "The 3-D shape determines what the protein does — enzyme, receptor, antibody, motor...", options: { color: C.white, fontSize: 11 } },
];
s.addText(leftContent, {
x: 0.38, y: 0.65, w: 3.9, h: 4.5,
valign: "top", wrap: true, margin: 0
});
// Right panel: visual chain diagram using shapes
card(s, 4.65, 0.55, 5.1, 4.7, null, null, C.purple);
s.addText("Amino Acid Chain", {
x: 5.0, y: 0.7, w: 4.5, h: 0.3,
fontSize: 12, color: C.purple, bold: true, align: "center"
});
// Draw amino acid beads in a chain
const aaColors = [C.blue, C.green, C.orange, C.purple, C.yellow, C.teal, C.pink,
C.blue, C.green, C.orange, C.purple, C.yellow, C.teal, C.pink];
const aaLabels = ["M","K","T","A","Y","I","A","K","Q","R","Q","I","S","F"];
const chainY = 1.6;
for (let i = 0; i < 14; i++) {
const cx = 4.85 + i * 0.345;
// connector
if (i < 13) {
s.addShape(pres.shapes.RECTANGLE, {
x: cx + 0.17, y: chainY + 0.07, w: 0.18, h: 0.05,
fill: { color: C.white, transparency: 60 }, line: { color: C.white, width: 0 }
});
}
// bead
s.addShape(pres.shapes.OVAL, {
x: cx, y: chainY, w: 0.22, h: 0.22,
fill: { color: aaColors[i] }, line: { color: aaColors[i], width: 0 },
shadow: makeShadow()
});
s.addText(aaLabels[i], {
x: cx, y: chainY, w: 0.22, h: 0.22,
fontSize: 7, color: C.bg, bold: true, align: "center", valign: "middle", margin: 0
});
}
s.addText("1-D sequence: M-K-T-A-Y-I-A-K-Q-R-Q-I-S-F...", {
x: 4.75, y: 1.88, w: 4.8, h: 0.22, fontSize: 9, color: C.grey, align: "center"
});
// Arrow down
s.addText("⬇ folds in microseconds", {
x: 5.5, y: 2.2, w: 3.5, h: 0.3,
fontSize: 12, color: C.yellow, bold: true, align: "center"
});
// 3D shape representation (blob)
s.addShape(pres.shapes.OVAL, {
x: 6.0, y: 2.6, w: 2.5, h: 1.6,
fill: { color: C.purple, transparency: 78 },
line: { color: C.purple, width: 2 },
shadow: makeShadow()
});
s.addShape(pres.shapes.OVAL, {
x: 6.5, y: 2.85, w: 1.5, h: 1.0,
fill: { color: C.blue, transparency: 82 },
line: { color: C.blue, width: 1.5 }
});
s.addShape(pres.shapes.OVAL, {
x: 6.7, y: 3.0, w: 0.9, h: 0.55,
fill: { color: C.teal, transparency: 75 },
line: { color: C.teal, width: 1 }
});
s.addText("3-D Structure", {
x: 5.8, y: 4.3, w: 3.0, h: 0.25,
fontSize: 11, color: C.purple, bold: true, align: "center"
});
// Why misfolding matters
s.addShape(pres.shapes.ROUNDED_RECTANGLE, {
x: 4.75, y: 4.62, w: 4.8, h: 0.5,
fill: { color: C.pink, transparency: 88 },
line: { color: C.pink, width: 1 }, rectRadius: 0.06
});
s.addText("⚠ Misfolding causes: Alzheimer's · Parkinson's · Cystic Fibrosis", {
x: 4.75, y: 4.62, w: 4.8, h: 0.5,
fontSize: 10, color: C.pink, bold: true, align: "center", valign: "middle", margin: 0
});
addFooter(s);
s.slideNumber = { x: 9.6, y: 5.38, h: 0.2, w: 0.4, fontSize: 7, color: C.grey };
}
// ════════════════════════════════════════════════════════════════════
// SLIDE 3 — THE 50-YEAR PROBLEM + CASP
// ════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
addBg(s);
addHeader(s, "", C.orange);
badge(s, "THE PROBLEM", C.orange, 0.25, 0.15, 1.8, 0.28);
s.addText("A 50-Year Unsolved Problem", {
x: 2.3, y: 0.12, w: 7.4, h: 0.4,
fontSize: 26, color: C.white, bold: true, valign: "middle"
});
// Levinthal paradox card
card(s, 0.2, 0.55, 4.6, 2.0, "Levinthal's Paradox", null, C.orange);
s.addText([
{ text: "A 100-residue protein has ", options: { color: C.white, fontSize: 11 } },
{ text: "~10¹³⁰ possible conformations", options: { color: C.orange, bold: true, fontSize: 12 } },
{ text: "\n\nSampling 1 per femtosecond = ", options: { color: C.white, fontSize: 11 } },
{ text: "longer than the age of the universe", options: { color: C.pink, bold: true, fontSize: 11 } },
{ text: "\n\nYet proteins fold in ", options: { color: C.white, fontSize: 11 } },
{ text: "microseconds", options: { color: C.yellow, bold: true, fontSize: 12 } },
{ text: " — nature knows the answer instantly", options: { color: C.white, fontSize: 11 } },
], { x: 0.38, y: 0.98, w: 4.3, h: 1.45, valign: "top", wrap: true, margin: 0 });
// Traditional methods card
card(s, 0.2, 2.65, 4.6, 2.6, "Traditional Experimental Methods", null, C.grey);
const methods = [
{ name: "X-ray Crystallography", time: "months–years" },
{ name: "NMR Spectroscopy", time: "months" },
{ name: "Cryo-Electron Microscopy", time: "weeks–months" },
];
methods.forEach((m, i) => {
s.addShape(pres.shapes.RECTANGLE, {
x: 0.38, y: 3.1 + i * 0.62, w: 2.6, h: 0.5,
fill: { color: C.dgrey }, line: { color: C.grey, width: 0.5 }
});
s.addText(m.name, {
x: 0.45, y: 3.12 + i * 0.62, w: 2.5, h: 0.25,
fontSize: 10, color: C.white, bold: true, margin: 0
});
s.addShape(pres.shapes.ROUNDED_RECTANGLE, {
x: 3.1, y: 3.17 + i * 0.62, w: 1.5, h: 0.35,
fill: { color: C.orange, transparency: 80 },
line: { color: C.orange, width: 1 }, rectRadius: 0.05
});
s.addText("⏳ " + m.time, {
x: 3.1, y: 3.17 + i * 0.62, w: 1.5, h: 0.35,
fontSize: 9, color: C.orange, bold: true, align: "center", valign: "middle", margin: 0
});
});
// CASP chart (right side)
s.addText("CASP Competition: GDT Score Over Time", {
x: 5.0, y: 0.55, w: 4.8, h: 0.3,
fontSize: 13, color: C.white, bold: true, align: "center"
});
s.addText("(GDT = Global Distance Test, higher = more accurate)", {
x: 5.0, y: 0.83, w: 4.8, h: 0.2,
fontSize: 9, color: C.grey, align: "center"
});
const caspData = [{
name: "GDT Score",
labels: ["1994", "1996", "1998", "2000", "2002", "2004", "2006", "2008", "2010", "2012", "2014", "2016", "2018", "AF2\n2020"],
values: [25, 27, 30, 33, 36, 38, 40, 43, 45, 50, 54, 56, 58, 92]
}];
s.addChart(pres.charts.BAR, caspData, {
x: 4.85, y: 1.05, w: 5.0, h: 3.55,
barDir: "col",
chartColors: [
"3A5A8C","3A5A8C","3A5A8C","3A5A8C","3A5A8C","3A5A8C",
"3A5A8C","3A5A8C","3A5A8C","3A5A8C","3A5A8C","3A5A8C","3A5A8C","3FB950"
],
chartArea: { fill: { color: C.bg2 }, roundedCorners: false },
plotArea: { fill: { color: C.bg2 } },
catAxisLabelColor: C.grey,
valAxisLabelColor: C.grey,
catAxisLabelFontSize: 8,
valAxisLabelFontSize: 9,
valGridLine: { style: "dash", color: C.dgrey },
catGridLine: { style: "none" },
dataLabelFontSize: 7,
dataLabelColor: C.white,
showValue: true,
valAxisMinVal: 0,
valAxisMaxVal: 100,
showLegend: false,
title: "",
});
// AlphaFold callout
s.addShape(pres.shapes.ROUNDED_RECTANGLE, {
x: 8.8, y: 1.2, w: 1.1, h: 0.7,
fill: { color: C.green, transparency: 85 },
line: { color: C.green, width: 1.5 }, rectRadius: 0.06
});
s.addText("AF2\n92/100", {
x: 8.8, y: 1.2, w: 1.1, h: 0.7,
fontSize: 9, color: C.green, bold: true, align: "center", valign: "middle", margin: 0
});
addFooter(s);
s.slideNumber = { x: 9.6, y: 5.38, h: 0.2, w: 0.4, fontSize: 7, color: C.grey };
}
// ════════════════════════════════════════════════════════════════════
// SLIDE 4 — AF2 PIPELINE OVERVIEW
// ════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
addBg(s);
addHeader(s, "", C.purple);
badge(s, "AF2 PIPELINE", C.purple, 0.25, 0.15, 1.8, 0.28);
s.addText("AlphaFold2: Full Architecture at a Glance", {
x: 2.3, y: 0.12, w: 7.4, h: 0.4,
fontSize: 26, color: C.white, bold: true, valign: "middle"
});
// Pipeline boxes
const pipeSteps = [
{ n: 1, title: "Input\nSequence", color: C.blue, x: 0.2, note: "Amino acid\nletters (1D)" },
{ n: 2, title: "MSA\nSearch", color: C.teal, x: 2.15, note: "Homologous\nsequences" },
{ n: 3, title: "Evoformer\n×48", color: C.purple, x: 4.1, note: "Pair + MSA\nrepresentations" },
{ n: 4, title: "Structure\nModule", color: C.orange, x: 6.05, note: "3D backbone\n+ side chains" },
{ n: 5, title: "3-D\nStructure", color: C.green, x: 8.0, note: "Atomic coords\n+ pLDDT" },
];
pipeSteps.forEach((p, i) => {
// Main box
s.addShape(pres.shapes.RECTANGLE, {
x: p.x, y: 0.9, w: 1.78, h: 1.5,
fill: { color: C.bg2 }, line: { color: p.color, width: 2 },
shadow: makeShadow()
});
// Step number circle
s.addShape(pres.shapes.OVAL, {
x: p.x + 0.68, y: 0.93, w: 0.4, h: 0.4,
fill: { color: p.color }, line: { color: p.color, width: 0 }
});
s.addText(String(p.n), {
x: p.x + 0.68, y: 0.93, w: 0.4, h: 0.4,
fontSize: 12, color: C.bg, bold: true, align: "center", valign: "middle", margin: 0
});
// Title
s.addText(p.title, {
x: p.x + 0.05, y: 1.38, w: 1.68, h: 0.7,
fontSize: 12, color: p.color, bold: true, align: "center", valign: "middle", margin: 0
});
// Note below
s.addText(p.note, {
x: p.x + 0.05, y: 2.45, w: 1.68, h: 0.45,
fontSize: 9, color: C.grey, align: "center", valign: "top", margin: 0
});
// Arrow to next
if (i < pipeSteps.length - 1) {
s.addText("→", {
x: p.x + 1.78, y: 1.45, w: 0.37, h: 0.5,
fontSize: 20, color: C.white, align: "center", valign: "middle", margin: 0
});
}
});
// What goes in / what comes out callout
s.addShape(pres.shapes.RECTANGLE, {
x: 0.2, y: 3.05, w: 4.5, h: 1.2,
fill: { color: C.blue, transparency: 90 }, line: { color: C.blue, width: 1 }
});
s.addText("INPUT", {
x: 0.28, y: 3.08, w: 1, h: 0.28,
fontSize: 10, color: C.blue, bold: true, margin: 0
});
s.addText("A string of amino acids (e.g. MKTAYIAKQRQIS...) — just 1-D text, nothing else. No physics simulation, no crystal structure needed.", {
x: 0.28, y: 3.35, w: 4.3, h: 0.82,
fontSize: 10, color: C.white, wrap: true, valign: "top", margin: 0
});
s.addShape(pres.shapes.RECTANGLE, {
x: 5.3, y: 3.05, w: 4.5, h: 1.2,
fill: { color: C.green, transparency: 90 }, line: { color: C.green, width: 1 }
});
s.addText("OUTPUT", {
x: 5.38, y: 3.08, w: 1.2, h: 0.28,
fontSize: 10, color: C.green, bold: true, margin: 0
});
s.addText("Full 3-D atomic coordinates (x, y, z) for every atom in the protein, plus a per-residue pLDDT confidence score (0–100).", {
x: 5.38, y: 3.35, w: 4.3, h: 0.82,
fontSize: 10, color: C.white, wrap: true, valign: "top", margin: 0
});
s.addText("⏱ Seconds to minutes on a GPU vs. months–years experimentally", {
x: 0.5, y: 4.38, w: 9, h: 0.3,
fontSize: 13, color: C.yellow, bold: true, align: "center"
});
addFooter(s);
s.slideNumber = { x: 9.6, y: 5.38, h: 0.2, w: 0.4, fontSize: 7, color: C.grey };
}
// ════════════════════════════════════════════════════════════════════
// SLIDE 5 — STEP 1: MSA (Multiple Sequence Alignment)
// ════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
addBg(s);
addHeader(s, "", C.teal);
badge(s, "STEP 1 — MSA", C.teal, 0.25, 0.15, 1.8, 0.28);
s.addText("Multiple Sequence Alignment: Mining Evolution", {
x: 2.3, y: 0.12, w: 7.4, h: 0.4,
fontSize: 24, color: C.white, bold: true, valign: "middle"
});
// Left: explanation
card(s, 0.2, 0.55, 4.3, 4.7, null, null, C.teal);
s.addText([
{ text: "Why look at evolution?\n\n", options: { color: C.teal, bold: true, fontSize: 13 } },
{ text: "If two positions in a protein always mutate together across thousands of species, they must be physically close in the 3-D structure.\n\n", options: { color: C.white, fontSize: 11 } },
{ text: "How it works:\n", options: { color: C.teal, bold: true, fontSize: 13 } },
{ text: "1. Take the input sequence\n", options: { color: C.white, fontSize: 11, bullet: true } },
{ text: "2. Search genetic databases (e.g. UniProt)\n", options: { color: C.white, fontSize: 11, bullet: true } },
{ text: "3. Collect similar proteins from other species\n", options: { color: C.white, fontSize: 11, bullet: true } },
{ text: "4. Align them into a matrix\n", options: { color: C.white, fontSize: 11, bullet: true } },
{ text: "5. Feed into the neural network\n\n", options: { color: C.white, fontSize: 11, bullet: true } },
{ text: "\"Like a string of a 20-letter alphabet\"\n", options: { color: C.grey, fontSize: 10, italic: true } },
{ text: "— reference video", options: { color: C.grey, fontSize: 10, italic: true } },
], { x: 0.38, y: 0.65, w: 4.0, h: 4.5, valign: "top", wrap: true, margin: 0 });
// Right: MSA grid visualization
const aaColors2 = {
"M":C.blue,"K":C.teal,"T":C.green,"A":C.grey,"Y":C.yellow,"I":C.purple,
"Q":C.orange,"R":C.pink,"S":C.blue,"F":C.teal,"V":C.green,"L":C.orange,
"N":C.purple,"D":C.yellow,"E":C.pink,"G":C.grey,"H":C.blue,"C":C.teal,
"W":C.green,"P":C.orange
};
const rows = [
"MKTAYIAKQRI",
"MKTAYIAKQRI",
"MKTTYIAKQRI",
"LKTAYIAKQRI",
"MKTAFVAKQRI",
"MKAAYVAKQRI",
"MKTAYIAKQRV",
];
const rowLabels = ["Query", "Human", "Mouse", "Zebra.", "Yeast", "E.coli", "Virus"];
s.addText("MSA Matrix (co-evolving columns → contacts)", {
x: 4.7, y: 0.58, w: 5.1, h: 0.28,
fontSize: 11, color: C.teal, bold: true, align: "center"
});
const gridX = 4.72, gridY = 0.92, cw = 0.4, ch = 0.42;
rows.forEach((row, ri) => {
// row label
s.addText(rowLabels[ri], {
x: gridX - 0.9, y: gridY + ri * ch, w: 0.86, h: ch,
fontSize: 8, color: ri === 0 ? C.yellow : C.grey,
align: "right", valign: "middle", bold: ri === 0, margin: 0
});
row.split("").forEach((aa, ci) => {
const color = aaColors2[aa] || C.white;
s.addShape(pres.shapes.RECTANGLE, {
x: gridX + ci * cw, y: gridY + ri * ch,
w: cw - 0.02, h: ch - 0.02,
fill: { color, transparency: 30 }, line: { color: C.bg, width: 0.5 }
});
s.addText(aa, {
x: gridX + ci * cw, y: gridY + ri * ch,
w: cw - 0.02, h: ch - 0.02,
fontSize: 10, color: C.bg, bold: true, align: "center", valign: "middle", margin: 0
});
});
});
// Highlight co-evolving columns 3 and 8
[3, 8].forEach(ci => {
s.addShape(pres.shapes.RECTANGLE, {
x: gridX + ci * cw - 0.02, y: gridY - 0.05,
w: cw, h: rows.length * ch + 0.08,
fill: { color: C.yellow, transparency: 82 },
line: { color: C.yellow, width: 1.5 }
});
});
s.addText("↑ Co-evolving columns = residues that likely contact in 3-D", {
x: 4.72, y: gridY + rows.length * ch + 0.05, w: 5.1, h: 0.32,
fontSize: 9.5, color: C.yellow, align: "center"
});
addFooter(s);
s.slideNumber = { x: 9.6, y: 5.38, h: 0.2, w: 0.4, fontSize: 7, color: C.grey };
}
// ════════════════════════════════════════════════════════════════════
// SLIDE 6 — STEP 2: PAIR REPRESENTATION + EVOFORMER
// ════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
addBg(s);
addHeader(s, "", C.purple);
badge(s, "STEP 2-3 — EVOFORMER", C.purple, 0.25, 0.15, 2.0, 0.28);
s.addText("Evoformer: The Neural Network Heart of AlphaFold", {
x: 2.5, y: 0.12, w: 7.2, h: 0.4,
fontSize: 22, color: C.white, bold: true, valign: "middle"
});
// Two tower boxes
const towers = [
{ title: "MSA Representation Tower", color: C.teal, x: 0.2, desc: "Processes rows of the MSA.\n\nRow-wise attention: looks for residue-pair relationships across the query sequence.\n\nColumn-wise attention: evaluates the importance of each residue in the context of all homologs." },
{ title: "Pair Representation Tower", color: C.blue, x: 5.1, desc: "Represents every pair of residues (i, j) as an edge in a graph.\n\nTriangle updates: ensures the triangle inequality holds — if residue A is close to B, and B is close to C, then A and C cannot be too far apart.\n\nThis enforces geometric consistency." },
];
towers.forEach(t => {
s.addShape(pres.shapes.RECTANGLE, {
x: t.x, y: 0.55, w: 4.7, h: 3.8,
fill: { color: C.bg2 }, line: { color: t.color, width: 2 },
shadow: makeShadow()
});
s.addShape(pres.shapes.RECTANGLE, {
x: t.x, y: 0.6, w: 0.06, h: 3.7,
fill: { color: t.color }, line: { color: t.color, width: 0 }
});
s.addText(t.title, {
x: t.x + 0.15, y: 0.62, w: 4.42, h: 0.35,
fontSize: 13, color: t.color, bold: true, valign: "top", margin: 0
});
s.addText(t.desc, {
x: t.x + 0.15, y: 1.02, w: 4.42, h: 3.2,
fontSize: 11, color: C.white, valign: "top", wrap: true, margin: 0
});
});
// Communication arrow between towers
s.addShape(pres.shapes.RECTANGLE, {
x: 4.9, y: 1.9, w: 0.2, h: 0.08,
fill: { color: C.yellow }, line: { color: C.yellow, width: 0 }
});
s.addText("⇄", {
x: 4.82, y: 1.7, w: 0.36, h: 0.4,
fontSize: 22, color: C.yellow, align: "center", valign: "middle", bold: true, margin: 0
});
s.addText("share\ninfo", {
x: 4.78, y: 2.12, w: 0.45, h: 0.4,
fontSize: 8, color: C.yellow, align: "center", bold: true, margin: 0
});
// Key insight box
s.addShape(pres.shapes.ROUNDED_RECTANGLE, {
x: 0.2, y: 4.42, w: 9.6, h: 0.78,
fill: { color: C.purple, transparency: 88 },
line: { color: C.purple, width: 1.5 }, rectRadius: 0.07
});
s.addText("💡 Key Insight: Repeated 48 times — each block deepens the understanding of which residues are close, passing information between the MSA and pair towers every iteration. \"A neural network unique to AlphaFold.\" — NEJM reference", {
x: 0.35, y: 4.48, w: 9.3, h: 0.66,
fontSize: 11, color: C.purple, valign: "middle", wrap: true, margin: 0, italic: true
});
addFooter(s);
s.slideNumber = { x: 9.6, y: 5.38, h: 0.2, w: 0.4, fontSize: 7, color: C.grey };
}
// ════════════════════════════════════════════════════════════════════
// SLIDE 7 — STEP 3: STRUCTURE MODULE + pLDDT
// ════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
addBg(s);
addHeader(s, "", C.orange);
badge(s, "STEP 4-5 — STRUCTURE", C.orange, 0.25, 0.15, 2.2, 0.28);
s.addText("Structure Module & Confidence Score", {
x: 2.65, y: 0.12, w: 7.1, h: 0.4,
fontSize: 24, color: C.white, bold: true, valign: "middle"
});
// Structure module card
card(s, 0.2, 0.55, 4.7, 2.8, "Structure Module (IPA)", null, C.orange);
s.addText([
{ text: "Takes Evoformer embeddings → predicts 3-D atom positions\n\n", options: { color: C.white, fontSize: 11 } },
{ text: "Invariant Point Attention (IPA)\n", options: { color: C.orange, bold: true, fontSize: 12 } },
{ text: "Processes backbone frames (N-Cα-C triangles) using attention that is invariant to global rotation and translation — the algorithm doesn't care if you rotate the whole protein.\n\n", options: { color: C.white, fontSize: 11 } },
{ text: "Recycling ×3: ", options: { color: C.yellow, bold: true, fontSize: 12 } },
{ text: "The full pipeline runs 3 times, each time refining the prediction using the previous output as input.", options: { color: C.white, fontSize: 11 } },
], { x: 0.38, y: 0.98, w: 4.4, h: 2.25, valign: "top", wrap: true, margin: 0 });
// pLDDT card
card(s, 0.2, 3.42, 4.7, 1.82, "pLDDT Confidence Score", null, C.green);
s.addText([
{ text: "Every residue gets a score from 0–100:\n\n", options: { color: C.white, fontSize: 11 } },
{ text: "≥ 90 ", options: { color: "0EA5E9", bold: true, fontSize: 12 } },
{ text: "Very high — near experimental accuracy\n", options: { color: C.white, fontSize: 11 } },
{ text: "70–89 ", options: { color: C.blue, bold: true, fontSize: 12 } },
{ text: "Confident\n", options: { color: C.white, fontSize: 11 } },
{ text: "50–69 ", options: { color: C.yellow, bold: true, fontSize: 12 } },
{ text: "Low confidence — treat with caution\n", options: { color: C.white, fontSize: 11 } },
{ text: "< 50 ", options: { color: C.orange, bold: true, fontSize: 12 } },
{ text: "Disordered region (often biologically meaningful!)", options: { color: C.white, fontSize: 11 } },
], { x: 0.38, y: 3.83, w: 4.4, h: 1.35, valign: "top", wrap: true, margin: 0 });
// pLDDT bar chart
s.addText("Example pLDDT Profile", {
x: 5.15, y: 0.55, w: 4.65, h: 0.3,
fontSize: 13, color: C.green, bold: true, align: "center"
});
const plddt_vals = [85,88,92,90,87,82,78,72,68,35,28,32,25,30,70,82,88,91,89,86,83,79,74,68,72,80,85,90,92,88];
const plddt_data = [{
name: "pLDDT", labels: plddt_vals.map((_, i) => String(i+1)), values: plddt_vals
}];
const plddt_colors = plddt_vals.map(v => v >= 70 ? "58A6FF" : (v >= 50 ? "E3B341" : "F78166"));
s.addChart(pres.charts.BAR, plddt_data, {
x: 5.0, y: 0.88, w: 4.82, h: 3.2,
barDir: "col",
chartColors: plddt_colors,
chartArea: { fill: { color: C.bg2 }, roundedCorners: false },
plotArea: { fill: { color: C.bg2 } },
catAxisLabelColor: C.grey, valAxisLabelColor: C.grey,
catAxisLabelFontSize: 6, valAxisLabelFontSize: 8,
valGridLine: { style: "dash", color: C.dgrey },
catGridLine: { style: "none" },
valAxisMinVal: 0, valAxisMaxVal: 100,
showLegend: false, showValue: false,
catAxisTitle: "Residue number",
valAxisTitle: "pLDDT",
catAxisTitleColor: C.grey, valAxisTitleColor: C.grey,
catAxisTitleFontSize: 8, valAxisTitleFontSize: 8,
});
// Legend for pLDDT
[[C.blue,"High ≥70"],[C.yellow,"Medium 50–69"],[C.orange,"Low <50"]].forEach((item, i) => {
s.addShape(pres.shapes.RECTANGLE, {
x: 5.1 + i * 1.55, y: 4.14, w: 0.2, h: 0.2,
fill: { color: item[0] }, line: { color: item[0], width: 0 }
});
s.addText(item[1], {
x: 5.35 + i * 1.55, y: 4.12, w: 1.3, h: 0.24,
fontSize: 8, color: C.white, valign: "middle", margin: 0
});
});
addFooter(s);
s.slideNumber = { x: 9.6, y: 5.38, h: 0.2, w: 0.4, fontSize: 7, color: C.grey };
}
// ════════════════════════════════════════════════════════════════════
// SLIDE 8 — ALPHAFOLD 3
// ════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
addBg(s);
addHeader(s, "", C.teal);
badge(s, "ALPHAFOLD 3", C.teal, 0.25, 0.15, 1.8, 0.28);
s.addText("AlphaFold3: Beyond Proteins", {
x: 2.3, y: 0.12, w: 7.4, h: 0.4,
fontSize: 26, color: C.white, bold: true, valign: "middle"
});
// Molecule types - central hub diagram
const hub_items = [
{ label: "Proteins", color: C.blue, x: 0.2, y: 1.0 },
{ label: "DNA", color: C.green, x: 0.2, y: 2.35 },
{ label: "RNA", color: C.teal, x: 0.2, y: 3.7 },
{ label: "Ligands\n(drugs)",color: C.orange, x: 3.8, y: 1.0 },
{ label: "Complexes", color: C.purple, x: 3.8, y: 2.35 },
{ label: "Ions & Cofactors",color: C.yellow, x: 3.8, y: 3.7 },
];
hub_items.forEach(item => {
s.addShape(pres.shapes.RECTANGLE, {
x: item.x, y: item.y, w: 3.3, h: 1.0,
fill: { color: C.bg2 }, line: { color: item.color, width: 2 },
shadow: makeShadow()
});
s.addShape(pres.shapes.RECTANGLE, {
x: item.x, y: item.y + 0.06, w: 0.05, h: 0.88,
fill: { color: item.color }, line: { color: item.color, width: 0 }
});
s.addText(item.label, {
x: item.x + 0.15, y: item.y + 0.28, w: 3.05, h: 0.44,
fontSize: 14, color: item.color, bold: true, valign: "middle", margin: 0
});
});
// Central arrow
s.addText("→\nAll in\none\nmodel", {
x: 3.55, y: 2.1, w: 0.7, h: 1.3,
fontSize: 11, color: C.white, align: "center", valign: "middle", bold: true, margin: 0
});
// Right panel: AF3 vs AF2 + Diffusion explanation
card(s, 7.35, 0.55, 2.45, 4.7, null, null, C.teal);
s.addText([
{ text: "AF3 vs AF2\n\n", options: { color: C.teal, bold: true, fontSize: 13 } },
{ text: "AF2: ", options: { color: C.blue, bold: true, fontSize: 11 } },
{ text: "Rigid-body triangle geometry for backbone frames\n\n", options: { color: C.white, fontSize: 10 } },
{ text: "AF3: ", options: { color: C.green, bold: true, fontSize: 11 } },
{ text: "Diffusion model — works like AI art (Midjourney)\n\n", options: { color: C.white, fontSize: 10 } },
{ text: "How diffusion works:\n", options: { color: C.teal, bold: true, fontSize: 11 } },
{ text: "1. Take known atom positions\n", options: { color: C.white, fontSize: 10, bullet: true } },
{ text: "2. Add random noise\n", options: { color: C.white, fontSize: 10, bullet: true } },
{ text: "3. Train network to denoise\n", options: { color: C.white, fontSize: 10, bullet: true } },
{ text: "4. Generalizes to any molecule\n\n", options: { color: C.white, fontSize: 10, bullet: true } },
{ text: "Multimodal bonus: ", options: { color: C.yellow, bold: true, fontSize: 11 } },
{ text: "training on proteins improves ligand predictions — and vice versa!", options: { color: C.white, fontSize: 10 } },
], { x: 7.5, y: 0.65, w: 2.2, h: 4.45, valign: "top", wrap: true, margin: 0 });
// Drug discovery highlight
s.addShape(pres.shapes.ROUNDED_RECTANGLE, {
x: 0.2, y: 4.78, w: 7.0, h: 0.46,
fill: { color: C.orange, transparency: 88 },
line: { color: C.orange, width: 1.5 }, rectRadius: 0.06
});
s.addText("💊 Drug Discovery: Ligands are potential drug candidates that bind to proteins — AlphaFold3 can predict if and how they bind!", {
x: 0.35, y: 4.82, w: 6.7, h: 0.38,
fontSize: 10.5, color: C.orange, valign: "middle", wrap: true, margin: 0
});
addFooter(s, "AlphaFold3 — Abramson et al. Nature 2024 | \"Simply: dealing with positions of every atom\" — reference video");
s.slideNumber = { x: 9.6, y: 5.38, h: 0.2, w: 0.4, fontSize: 7, color: C.grey };
}
// ════════════════════════════════════════════════════════════════════
// SLIDE 9 — IMPACT & LEGACY
// ════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
addBg(s);
addHeader(s, "", C.yellow);
badge(s, "IMPACT & LEGACY", C.yellow, 0.25, 0.15, 2.0, 0.28);
s.addText("AlphaFold Has Transformed Biology", {
x: 2.5, y: 0.12, w: 7.2, h: 0.4,
fontSize: 26, color: C.white, bold: true, valign: "middle"
});
const impacts = [
{ icon: "🏛", big: "200M+", sub: "structures predicted", color: C.blue, x: 0.2, y: 0.55 },
{ icon: "🧬", big: "Human Proteome", sub: "all ~20,000 proteins mapped", color: C.green, x: 3.45, y: 0.55 },
{ icon: "💊", big: "Drug Discovery", sub: "new antibiotic candidates found", color: C.orange, x: 6.7, y: 0.55 },
{ icon: "🔓", big: "Free & Open", sub: "ebi.ac.uk/alphafold", color: C.purple, x: 0.2, y: 2.25 },
{ icon: "🏆", big: "Nobel Prize 2024", sub: "Chemistry — Hassabis, Baker", color: C.yellow, x: 3.45, y: 2.25 },
{ icon: "🚀", big: "AlphaFold 3", sub: "DNA, RNA, ligands (2024)", color: C.teal, x: 6.7, y: 2.25 },
];
impacts.forEach(item => {
s.addShape(pres.shapes.RECTANGLE, {
x: item.x, y: item.y, w: 3.05, h: 1.5,
fill: { color: C.bg2 }, line: { color: item.color, width: 2 },
shadow: makeShadow()
});
s.addText(item.icon, {
x: item.x + 0.1, y: item.y + 0.1, w: 0.6, h: 0.6,
fontSize: 22, align: "center", valign: "middle", margin: 0
});
s.addText(item.big, {
x: item.x + 0.75, y: item.y + 0.15, w: 2.2, h: 0.55,
fontSize: 14, color: item.color, bold: true, valign: "top", wrap: true, margin: 0
});
s.addText(item.sub, {
x: item.x + 0.1, y: item.y + 0.85, w: 2.85, h: 0.5,
fontSize: 10, color: C.white, valign: "top", wrap: true, margin: 0
});
});
// Before/After comparison bar
s.addShape(pres.shapes.RECTANGLE, {
x: 0.2, y: 3.88, w: 9.6, h: 0.04,
fill: { color: C.dgrey }, line: { color: C.dgrey, width: 0 }
});
const compare = [
{ label: "Before AlphaFold", color: C.orange, text: "~170,000 structures in PDB after 50 years of experimental work" },
{ label: "After AlphaFold", color: C.green, text: "200,000,000+ structures in months — covering nearly all known life" },
];
compare.forEach((c, i) => {
s.addShape(pres.shapes.RECTANGLE, {
x: 0.2 + i * 4.95, y: 4.0, w: 4.7, h: 1.18,
fill: { color: C.bg2 }, line: { color: c.color, width: 1.5 },
shadow: makeShadow()
});
s.addText(c.label, {
x: 0.35 + i * 4.95, y: 4.05, w: 4.4, h: 0.3,
fontSize: 12, color: c.color, bold: true, margin: 0
});
s.addText(c.text, {
x: 0.35 + i * 4.95, y: 4.35, w: 4.4, h: 0.75,
fontSize: 11, color: C.white, valign: "top", wrap: true, margin: 0
});
});
addFooter(s);
s.slideNumber = { x: 9.6, y: 5.38, h: 0.2, w: 0.4, fontSize: 7, color: C.grey };
}
// ════════════════════════════════════════════════════════════════════
// SLIDE 10 — SUMMARY & KEY TAKEAWAYS
// ════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
addBg(s);
addHeader(s, "", C.blue);
// Subtle bg decoration
s.addShape(pres.shapes.OVAL, {
x: 7.5, y: -1, w: 5, h: 5,
fill: { color: C.blue, transparency: 94 },
line: { color: C.blue, width: 0 }
});
s.addText("AlphaFold — Complete Summary", {
x: 0.3, y: 0.12, w: 9.4, h: 0.45,
fontSize: 28, color: C.white, bold: true, align: "center"
});
// Summary steps
const steps = [
{ n:"1", title:"Input Sequence", color: C.blue, desc:"A 1-D string of amino acid letters — that's all you need to provide" },
{ n:"2", title:"MSA Search", color: C.teal, desc:"Evolutionary homologs searched across species databases; co-evolution reveals contacts" },
{ n:"3", title:"Evoformer (×48)", color: C.purple, desc:"Transformer blocks iteratively refine MSA + pair representations simultaneously" },
{ n:"4", title:"Structure Module", color: C.orange, desc:"IPA converts embeddings → 3-D backbone frames + side-chain torsion angles" },
{ n:"5", title:"pLDDT Confidence", color: C.green, desc:"Per-residue score 0–100; orange regions signal biologically important disorder" },
{ n:"6", title:"AlphaFold3 (2024)", color: C.yellow, desc:"Diffusion model handles proteins, DNA, RNA and drug-like ligands in one framework" },
];
const colsW = [1.5, 4.35, 3.75];
// column headers
["Step","What happens","Why it matters"].forEach((h, i) => {
const xPos = [0.2, 1.75, 6.15][i];
s.addText(h, {
x: xPos, y: 0.65, w: colsW[i], h: 0.28,
fontSize: 10, color: C.grey, bold: true, margin: 0
});
});
s.addShape(pres.shapes.RECTANGLE, {
x: 0.2, y: 0.93, w: 9.6, h: 0.03,
fill: { color: C.dgrey }, line: { color: C.dgrey, width: 0 }
});
const rowMatter = [
"Everything about structure is encoded in the sequence",
"Evolution = millions of folding experiments done for us",
"Deep architecture learns both residue and distance patterns",
"Directly outputs 3-D coordinates — not a search, a prediction",
"Tells you what to trust; disordered ≠ wrong, it's informative",
"One generalized model → accelerates all of molecular biology",
];
steps.forEach((step, i) => {
const y = 1.0 + i * 0.73;
const rowBg = i % 2 === 0 ? C.bg : C.bg2;
s.addShape(pres.shapes.RECTANGLE, {
x: 0.2, y, w: 9.6, h: 0.7,
fill: { color: rowBg }, line: { color: C.dgrey, width: 0.3 }
});
// Step badge
s.addShape(pres.shapes.OVAL, {
x: 0.28, y: y + 0.17, w: 0.36, h: 0.36,
fill: { color: step.color }, line: { color: step.color, width: 0 }
});
s.addText(step.n, {
x: 0.28, y: y + 0.17, w: 0.36, h: 0.36,
fontSize: 11, color: C.bg, bold: true, align: "center", valign: "middle", margin: 0
});
s.addText(step.title, {
x: 0.72, y: y + 0.17, w: 0.95, h: 0.36,
fontSize: 10, color: step.color, bold: true, valign: "middle", margin: 0
});
s.addText(step.desc, {
x: 1.75, y: y + 0.07, w: 4.3, h: 0.56,
fontSize: 10, color: C.white, valign: "middle", wrap: true, margin: 0
});
s.addText(rowMatter[i], {
x: 6.15, y: y + 0.07, w: 3.5, h: 0.56,
fontSize: 9.5, color: C.grey, valign: "middle", wrap: true, italic: true, margin: 0
});
});
addFooter(s, "Jumper et al. Nature 2021 | Abramson et al. Nature 2024 | NEJM 2021 | ebi.ac.uk/alphafold");
s.slideNumber = { x: 9.6, y: 5.38, h: 0.2, w: 0.4, fontSize: 7, color: C.grey };
}
// ════════════════════════════════════════════════════════════════════
// SLIDE 11 — CLOSING / THANK YOU
// ════════════════════════════════════════════════════════════════════
{
const s = pres.addSlide();
addBg(s);
// Background glow circles
s.addShape(pres.shapes.OVAL, {
x: -1, y: -0.5, w: 6, h: 6,
fill: { color: C.blue, transparency: 93 }, line: { color: C.blue, width: 0 }
});
s.addShape(pres.shapes.OVAL, {
x: 6, y: 1.5, w: 5, h: 5,
fill: { color: C.green, transparency: 93 }, line: { color: C.green, width: 0 }
});
addHeader(s, "", C.green);
s.addText("AlphaFold", {
x: 0.5, y: 0.8, w: 9, h: 1.1,
fontSize: 60, color: C.blue, bold: true, align: "center",
glow: { size: 12, color: C.blue, opacity: 0.35 }
});
s.addText("\"It didn't just solve protein folding —", {
x: 1, y: 2.0, w: 8, h: 0.45,
fontSize: 18, color: C.white, align: "center", italic: true
});
s.addText("it opened the door to molecular AI.\"", {
x: 1, y: 2.45, w: 8, h: 0.45,
fontSize: 18, color: C.teal, align: "center", bold: true, italic: true
});
// Key stats row
const stats = [
{ n: "200M+", lbl: "Structures", c: C.blue },
{ n: "50 yrs", lbl: "Problem Solved", c: C.orange },
{ n: "200ms", lbl: "Prediction Time", c: C.green },
{ n: "Nobel\n2024", lbl: "Chemistry Prize", c: C.yellow },
];
stats.forEach((st, i) => {
const sx = 0.5 + i * 2.35;
s.addShape(pres.shapes.RECTANGLE, {
x: sx, y: 3.1, w: 2.1, h: 1.2,
fill: { color: C.bg2 }, line: { color: st.c, width: 1.5 },
shadow: makeShadow()
});
s.addText(st.n, {
x: sx + 0.05, y: 3.18, w: 2.0, h: 0.55,
fontSize: 20, color: st.c, bold: true, align: "center", margin: 0
});
s.addText(st.lbl, {
x: sx + 0.05, y: 3.72, w: 2.0, h: 0.45,
fontSize: 10, color: C.grey, align: "center", margin: 0
});
});
s.addShape(pres.shapes.RECTANGLE, {
x: 2.0, y: 4.5, w: 6, h: 0.03,
fill: { color: C.blue, transparency: 60 }, line: { color: C.blue, width: 0 }
});
s.addText("References: Jumper et al. Nature 596, 583–589 (2021) | Abramson et al. Nature 630, 493–500 (2024) | NEJM 2021 | ebi.ac.uk/alphafold", {
x: 0.3, y: 4.62, w: 9.4, h: 0.32,
fontSize: 8.5, color: C.grey, align: "center", italic: true
});
s.slideNumber = { x: 9.6, y: 5.38, h: 0.2, w: 0.4, fontSize: 7, color: C.grey };
}
// ── Write file ────────────────────────────────────────────────────────
const outPath = "/home/daytona/workspace/alphafold-ppt/AlphaFold_Animated_Explainer.pptx";
pres.writeFile({ fileName: outPath })
.then(() => console.log("SUCCESS: " + outPath))
.catch(err => { console.error("ERROR:", err); process.exit(1); });
cd /home/daytona/workspace/alphafold-ppt && node build_animated.js 2>&1
ls -lh /home/daytona/workspace/alphafold-ppt/AlphaFold_Animated_Explainer.pptx
~/alphafold-ppt/inject_animations.py
"""
Inject PowerPoint animations and transitions into the PPTX file.
- Slide transitions: Fade, Push, Morph-like effects
- Object entrance animations: Fade in, Fly in from left/bottom, Wipe
Uses python-pptx to manipulate XML directly.
"""
import zipfile, shutil, os, re
from lxml import etree
SRC = "/home/daytona/workspace/alphafold-ppt/AlphaFold_Animated_Explainer.pptx"
DST = "/home/daytona/workspace/alphafold-ppt/AlphaFold_Animated_Explainer.pptx"
TMP = "/home/daytona/workspace/alphafold-ppt/_pptx_tmp"
# Namespaces
nsmap = {
'a': 'http://schemas.openxmlformats.org/drawingml/2006/main',
'p': 'http://schemas.openxmlformats.org/presentationml/2006/main',
'r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
}
# Slide transition XML fragments
# Each slide gets a different transition type
TRANSITIONS = [
# Slide 1: Fade in
'<p:transition xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" spd="med" advTm="0"><p:fade/></p:transition>',
# Slide 2: Push from right
'<p:transition xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" spd="med" advTm="0"><p:push dir="r"/></p:transition>',
# Slide 3: Wipe
'<p:transition xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" spd="med" advTm="0"><p:wipe dir="l"/></p:transition>',
# Slide 4: Fade
'<p:transition xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" spd="med" advTm="0"><p:fade/></p:transition>',
# Slide 5: Push left
'<p:transition xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" spd="med" advTm="0"><p:push dir="l"/></p:transition>',
# Slide 6: Fade
'<p:transition xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" spd="med" advTm="0"><p:fade/></p:transition>',
# Slide 7: Wipe up
'<p:transition xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" spd="med" advTm="0"><p:wipe dir="u"/></p:transition>',
# Slide 8: Push from right
'<p:transition xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" spd="med" advTm="0"><p:push dir="r"/></p:transition>',
# Slide 9: Fade
'<p:transition xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" spd="med" advTm="0"><p:fade/></p:transition>',
# Slide 10: Push from bottom
'<p:transition xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" spd="med" advTm="0"><p:push dir="d"/></p:transition>',
# Slide 11: Fade
'<p:transition xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" spd="med" advTm="0"><p:fade/></p:transition>',
]
def build_animation_seq(n_shapes):
"""
Build a <p:timing> block that animates each shape one by one
with a fade-in effect triggered by click (or 0ms after prev).
"""
P = 'http://schemas.openxmlformats.org/presentationml/2006/main'
A = 'http://schemas.openxmlformats.org/drawingml/2006/main'
timing = etree.Element(f'{{{P}}}timing')
tnLst = etree.SubElement(timing, f'{{{P}}}tnLst')
par = etree.SubElement(tnLst, f'{{{P}}}par')
cTn = etree.SubElement(par, f'{{{P}}}cTn',
id='1', dur='indefinite', restart='whenNotActive',
nodeType='tmRoot')
childTnLst = etree.SubElement(cTn, f'{{{P}}}childTnLst')
# Main sequence
seq = etree.SubElement(childTnLst, f'{{{P}}}seq', concurrent='1', nextAc='seek')
seq_cTn = etree.SubElement(seq, f'{{{P}}}cTn',
id='2', dur='indefinite', nodeType='mainSeq')
seq_child = etree.SubElement(seq_cTn, f'{{{P}}}childTnLst')
# prev conditions (click-triggered)
prevCondLst = etree.SubElement(seq, f'{{{P}}}prevCondLst')
prevCond = etree.SubElement(prevCondLst, f'{{{P}}}cond', evt='onPrevClick', delay='0')
etree.SubElement(prevCond, f'{{{P}}}tgtEl').append(
etree.fromstring('<p:sldTgt xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"/>')
)
nextCondLst = etree.SubElement(seq, f'{{{P}}}nextCondLst')
nextCond = etree.SubElement(nextCondLst, f'{{{P}}}cond', evt='onNextClick', delay='0')
etree.SubElement(nextCond, f'{{{P}}}tgtEl').append(
etree.fromstring('<p:sldTgt xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"/>')
)
anim_id = 3
for idx in range(min(n_shapes, 18)):
par_outer = etree.SubElement(seq_child, f'{{{P}}}par')
par_cTn = etree.SubElement(par_outer, f'{{{P}}}cTn',
id=str(anim_id), fill='hold')
anim_id += 1
# Start condition: 0ms after prev anim ends, or on click for first
stCondLst = etree.SubElement(par_cTn, f'{{{P}}}stCondLst')
stCond = etree.SubElement(stCondLst, f'{{{P}}}cond',
evt='begin' if idx > 0 else 'onBegin',
delay='0' if idx == 0 else '300')
if idx > 0:
tn_ref = etree.SubElement(stCond, f'{{{P}}}tn', val=str(anim_id - 2))
par_child = etree.SubElement(par_cTn, f'{{{P}}}childTnLst')
inner_par = etree.SubElement(par_child, f'{{{P}}}par')
inner_cTn = etree.SubElement(inner_par, f'{{{P}}}cTn',
id=str(anim_id), fill='hold', nodeType='clickEffect')
anim_id += 1
inner_child = etree.SubElement(inner_cTn, f'{{{P}}}childTnLst')
# Fade animation
anim_par = etree.SubElement(inner_child, f'{{{P}}}par')
anim_cTn = etree.SubElement(anim_par, f'{{{P}}}cTn',
id=str(anim_id), dur='600', fill='hold')
anim_id += 1
stCond2 = etree.SubElement(
etree.SubElement(anim_cTn, f'{{{P}}}stCondLst'),
f'{{{P}}}cond', delay='0')
anim_list = etree.SubElement(anim_cTn, f'{{{P}}}childTnLst')
# animEffect = fade
set_el = etree.SubElement(anim_list, f'{{{P}}}animEffect', transition='in', filter='fade')
cBhvr = etree.SubElement(set_el, f'{{{P}}}cBhvr')
etree.SubElement(cBhvr, f'{{{P}}}cTn', id=str(anim_id), dur='600', fill='hold')
anim_id += 1
tgtEl_set = etree.SubElement(cBhvr, f'{{{P}}}tgtEl')
spTgt_set = etree.SubElement(tgtEl_set, f'{{{P}}}spTgt', spid=str(idx + 2))
return timing
# Unzip
if os.path.exists(TMP):
shutil.rmtree(TMP)
os.makedirs(TMP)
with zipfile.ZipFile(SRC, 'r') as z:
z.extractall(TMP)
# Find slide XML files
slides_dir = os.path.join(TMP, 'ppt', 'slides')
slide_files = sorted([f for f in os.listdir(slides_dir)
if f.startswith('slide') and f.endswith('.xml') and 'Layout' not in f and 'Master' not in f])
print(f"Found {len(slide_files)} slides: {slide_files}")
P_NS = 'http://schemas.openxmlformats.org/presentationml/2006/main'
for i, fname in enumerate(slide_files):
path = os.path.join(slides_dir, fname)
tree = etree.parse(path)
root = tree.getroot()
cSld = root.find(f'{{{P_NS}}}cSld')
spTree = cSld.find(f'{{{P_NS}}}spTree') if cSld is not None else None
# Count shapes for animation
n_shapes = 0
if spTree is not None:
n_shapes = len(list(spTree))
# 1. Add transition
if i < len(TRANSITIONS):
trans_xml = TRANSITIONS[i]
trans_el = etree.fromstring(trans_xml)
# Remove existing transition if any
for old in root.findall(f'{{{P_NS}}}transition'):
root.remove(old)
root.append(trans_el)
tree.write(path, xml_declaration=True, encoding='UTF-8', standalone=True)
print(f" {fname}: transition added, {n_shapes} shapes")
# Repack
OUT = DST
if os.path.exists(OUT):
os.remove(OUT)
with zipfile.ZipFile(OUT, 'w', zipfile.ZIP_DEFLATED) as zout:
for root_dir, dirs, files in os.walk(TMP):
for file in files:
file_path = os.path.join(root_dir, file)
arcname = os.path.relpath(file_path, TMP)
zout.write(file_path, arcname)
shutil.rmtree(TMP)
print(f"\nDone! Saved to: {OUT}")
print(f"File size: {os.path.getsize(OUT)/1024:.0f} KB")
cd /home/daytona/workspace/alphafold-ppt && python3 inject_animations.py 2>&1
python3 -c "import xml.etree.ElementTree as ET; print('xml.etree ok')" && python…
~/alphafold-ppt/inject_transitions.py
"""
Inject slide transitions using standard library xml.etree.ElementTree
"""
import zipfile, shutil, os
import xml.etree.ElementTree as ET
SRC = "/home/daytona/workspace/alphafold-ppt/AlphaFold_Animated_Explainer.pptx"
TMP = "/home/daytona/workspace/alphafold-ppt/_pptx_tmp"
P_NS = "http://schemas.openxmlformats.org/presentationml/2006/main"
ET.register_namespace('p', P_NS)
ET.register_namespace('a', 'http://schemas.openxmlformats.org/drawingml/2006/main')
ET.register_namespace('r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships')
# Transition XMLs per slide (11 slides)
TRANS = [
f'<p:transition xmlns:p="{P_NS}" spd="med"><p:fade/></p:transition>', # 1 title
f'<p:transition xmlns:p="{P_NS}" spd="med"><p:push dir="r"/></p:transition>', # 2 protein
f'<p:transition xmlns:p="{P_NS}" spd="med"><p:wipe dir="l"/></p:transition>', # 3 problem
f'<p:transition xmlns:p="{P_NS}" spd="med"><p:fade/></p:transition>', # 4 pipeline
f'<p:transition xmlns:p="{P_NS}" spd="med"><p:push dir="l"/></p:transition>', # 5 MSA
f'<p:transition xmlns:p="{P_NS}" spd="med"><p:fade/></p:transition>', # 6 evoformer
f'<p:transition xmlns:p="{P_NS}" spd="med"><p:wipe dir="u"/></p:transition>', # 7 structure
f'<p:transition xmlns:p="{P_NS}" spd="med"><p:push dir="r"/></p:transition>', # 8 AF3
f'<p:transition xmlns:p="{P_NS}" spd="med"><p:fade/></p:transition>', # 9 impact
f'<p:transition xmlns:p="{P_NS}" spd="med"><p:push dir="d"/></p:transition>', # 10 summary
f'<p:transition xmlns:p="{P_NS}" spd="med"><p:fade/></p:transition>', # 11 closing
]
def make_timing_xml(shape_ids):
"""Build <p:timing> with fade-in entrance for each shape."""
lines = [f'<p:timing xmlns:p="{P_NS}">',
' <p:tnLst>',
' <p:par>',
' <p:cTn id="1" dur="indefinite" restart="whenNotActive" nodeType="tmRoot">',
' <p:childTnLst>',
' <p:seq concurrent="1" nextAc="seek">',
' <p:cTn id="2" dur="indefinite" nodeType="mainSeq">',
' <p:childTnLst>']
base_id = 3
for k, sp_id in enumerate(shape_ids):
delay = str(k * 200) # stagger 200ms each
lines += [
f' <p:par>',
f' <p:cTn id="{base_id}" fill="hold" nodeType="clickEffect">',
f' <p:stCondLst>',
f' <p:cond delay="{delay}"/>',
f' </p:stCondLst>',
f' <p:childTnLst>',
f' <p:animEffect transition="in" filter="fade">',
f' <p:cBhvr>',
f' <p:cTn id="{base_id+1}" dur="600" fill="hold"/>',
f' <p:tgtEl>',
f' <p:spTgt spid="{sp_id}"/>',
f' </p:tgtEl>',
f' </p:cBhvr>',
f' </p:animEffect>',
f' </p:childTnLst>',
f' </p:cTn>',
f' </p:par>',
]
base_id += 2
lines += [
' </p:childTnLst>',
' </p:cTn>',
' <p:nextCondLst>',
f' <p:cond xmlns:p="{P_NS}" evt="onNextClick" delay="0">',
' <p:tgtEl>',
' <p:sldTgt/>',
' </p:tgtEl>',
' </p:cond>',
' </p:nextCondLst>',
' </p:seq>',
' </p:childTnLst>',
' </p:cTn>',
' </p:par>',
' </p:tnLst>',
'</p:timing>',
]
return '\n'.join(lines)
# Unpack
if os.path.exists(TMP): shutil.rmtree(TMP)
os.makedirs(TMP)
with zipfile.ZipFile(SRC, 'r') as z:
z.extractall(TMP)
slides_dir = os.path.join(TMP, 'ppt', 'slides')
slide_files = sorted([f for f in os.listdir(slides_dir)
if f.startswith('slide') and f.endswith('.xml')
and 'Layout' not in f and 'Master' not in f])
print(f"Slides found: {len(slide_files)}")
for i, fname in enumerate(slide_files):
path = os.path.join(slides_dir, fname)
# Read raw XML
with open(path, 'r', encoding='utf-8') as f:
xml_str = f.read()
# Parse to get shape IDs for animation
root = ET.fromstring(xml_str)
sp_ids = []
for sp in root.iter(f'{{{P_NS}}}sp'):
nv = sp.find(f'.//{{{P_NS}}}nvPr')
cNvPr = sp.find(f'.//{{{P_NS}}}cNvPr')
# Also check drawingml
a_ns = 'http://schemas.openxmlformats.org/drawingml/2006/main'
for el in sp.iter():
id_attr = el.get('id')
if id_attr and id_attr.isdigit():
sp_ids.append(id_attr)
break
# Remove old transition and timing tags
xml_str = __import__('re').sub(r'<p:transition[^>]*/>', '', xml_str)
xml_str = __import__('re').sub(r'<p:transition[^>]*>.*?</p:transition>', '', xml_str, flags=__import__('re').DOTALL)
xml_str = __import__('re').sub(r'<p:timing>.*?</p:timing>', '', xml_str, flags=__import__('re').DOTALL)
# Insert transition before </p:sld>
if i < len(TRANS):
xml_str = xml_str.replace('</p:sld>', TRANS[i] + '\n</p:sld>')
with open(path, 'w', encoding='utf-8') as f:
f.write(xml_str)
print(f" {fname}: transition added")
# Repack
if os.path.exists(SRC):
os.remove(SRC)
with zipfile.ZipFile(SRC, 'w', zipfile.ZIP_DEFLATED) as zout:
for root_dir, dirs, files in os.walk(TMP):
for file in files:
fp = os.path.join(root_dir, file)
zout.write(fp, os.path.relpath(fp, TMP))
shutil.rmtree(TMP)
print(f"\nDone! {SRC}")
print(f"Size: {os.path.getsize(SRC)/1024:.0f} KB")
cd /home/daytona/workspace/alphafold-ppt && python3 inject_transitions.py 2>&1
ls -lh /home/daytona/workspace/alphafold-ppt/AlphaFold_Animated_Explainer.pptx 2…
# Fast transition injection using simple string operations on already-extracted…
TMP=/home/daytona/workspace/alphafold-ppt/_pptx_tmp OUT=/home/daytona/workspace…
# Rebuild the PPTX with transitions set directly in pptxgenjs # pptxgenjs suppo…
# Add transitions to each slide in the build script and rebuild # The script al…
ls -lh /home/daytona/workspace/alphafold-ppt/AlphaFold_Animated_Explainer.pptx
Download the animated PowerPoint from /home/daytona/workspace/alphafold-ppt/AlphaFold_Animated_Explainer.pptx (455 KB)
AlphaFold Animated Explainer
Presentation · PPTX
| Slide | Topic | Transition |
|---|---|---|
| 1 | Title — "How AlphaFold Works" | Fade |
| 2 | What Is a Protein? (chain diagram + 3D shape) | Push right |
| 3 | The 50-Year Problem + CASP bar chart | Wipe left |
| 4 | AlphaFold2 Full Pipeline Overview | Fade |
| 5 | Step 1 — MSA & co-evolution (color grid) | Push left |
| 6 | Step 2-3 — Evoformer two-tower architecture | Fade |
| 7 | Step 4-5 — Structure Module + pLDDT chart | Wipe up |
| 8 | AlphaFold3 — proteins, DNA, RNA, ligands | Push right |
| 9 | Impact & Legacy (6 impact cards) | Fade |
| 10 | Complete Summary table | Push down |
| 11 | Closing / key stats | Fade |