Skip to content

Instantly share code, notes, and snippets.

@simonw
Created October 16, 2025 17:55
Show Gist options
  • Select an option

  • Save simonw/ef35bb9e6c514d1d596dac9227da482b to your computer and use it in GitHub Desktop.

Select an option

Save simonw/ef35bb9e6c514d1d596dac9227da482b to your computer and use it in GitHub Desktop.
import sys
sys.path.insert(0, '/mnt/skills/examples/slack-gif-creator')
from PIL import Image, ImageDraw, ImageFont
from core.gif_builder import GIFBuilder
from core.typography import draw_text_with_outline, TYPOGRAPHY_SCALE
from core.easing import interpolate
from core.visual_effects import ParticleSystem, create_impact_flash
from core.frame_composer import create_gradient_background, draw_emoji_enhanced
from core.color_palettes import get_palette
from core.validators import check_slack_size
import math
# Create a message GIF (480x480, larger and more detailed)
width, height = 480, 480
fps = 20
num_frames = 60 # 3 seconds
builder = GIFBuilder(width, height, fps)
# Get a vibrant palette
palette = get_palette('vibrant')
bg_start = (25, 25, 50) # Dark blue
bg_end = (50, 25, 60) # Dark purple
# Animation phases:
# 0-15: MCP side appears (left)
# 16-30: Skills side appears (right) with more flair
# 31-45: Skills gets bigger/more dramatic
# 46-60: Final state with emphasis
for i in range(num_frames):
frame = create_gradient_background(width, height, bg_start, bg_end)
draw = ImageDraw.Draw(frame)
# Divider line in middle
if i > 15:
draw.line([(240, 0), (240, height)], fill=(100, 100, 100), width=3)
# LEFT SIDE - MCPs (appears first, less exciting)
if i >= 5:
t_mcp = min((i - 5) / 10, 1.0)
mcp_y = interpolate(-100, 150, t_mcp, 'ease_out')
# MCP emoji and text (smaller, less impressive)
draw_emoji_enhanced(frame, '🔌', position=(60, int(mcp_y)), size=60, shadow=False)
if i >= 10:
draw_text_with_outline(
frame, "MCPs",
position=(120, int(mcp_y + 30)),
font_size=32,
text_color=(180, 180, 200),
outline_color=(0, 0, 0),
outline_width=2,
centered=True
)
# Some basic bullets about MCPs
if i >= 15:
mcp_text = [
"• Basic tools",
"• Limited scope",
"• OK I guess..."
]
y_pos = 260
for line in mcp_text:
draw_text_with_outline(
frame, line,
position=(120, y_pos),
font_size=18,
text_color=(160, 160, 180),
outline_color=(0, 0, 0),
outline_width=1,
centered=True
)
y_pos += 30
# RIGHT SIDE - Skills (appears second, WAY COOLER)
if i >= 16:
t_skills = min((i - 16) / 10, 1.0)
# Skills bounces in with elastic effect
skills_y = interpolate(-100, 150, t_skills, 'elastic_out')
# Extra pulsing in final phase
pulse_scale = 1.0
if i >= 31:
pulse_t = (i - 31) / 29
pulse_scale = 1.0 + math.sin(pulse_t * math.pi * 4) * 0.15
# Skills emoji (bigger and cooler)
emoji_size = int(80 * pulse_scale)
draw_emoji_enhanced(frame, '⚡', position=(360 - emoji_size//2, int(skills_y)),
size=emoji_size, shadow=True)
if i >= 20:
# Add sparkle particles around Skills
if i >= 31 and i % 4 == 0:
particles = ParticleSystem()
particles.emit_sparkles(x=360, y=int(skills_y + 40), count=3)
particles.render(frame)
text_scale = pulse_scale if i >= 31 else 1.0
font_size = int(40 * text_scale)
draw_text_with_outline(
frame, "Skills",
position=(360, int(skills_y + 90)),
font_size=font_size,
text_color=(255, 215, 0), # Gold
outline_color=(0, 0, 0),
outline_width=3,
centered=True
)
# Awesome bullets about Skills
if i >= 25:
skills_text = [
"✨ Pro features",
"✨ Best practices",
"✨ AMAZING!"
]
y_pos = 260
for line in skills_text:
alpha = min((i - 25) / 10, 1.0)
text_color = (255, int(215 * alpha), int(0 * alpha))
draw_text_with_outline(
frame, line,
position=(360, y_pos),
font_size=20,
text_color=text_color,
outline_color=(0, 0, 0),
outline_width=2,
centered=True
)
y_pos += 35
# Flash effect when Skills gets emphasized
if i == 31:
frame = create_impact_flash(frame, (360, 150), radius=150, intensity=0.5)
# Final emphasis - "Skills > MCPs" text at bottom
if i >= 45:
t_final = (i - 45) / 15
final_y = interpolate(height + 50, 420, t_final, 'ease_out')
draw_text_with_outline(
frame, "Skills > MCPs",
position=(240, int(final_y)),
font_size=45,
text_color=(255, 100, 255), # Hot pink
outline_color=(0, 0, 0),
outline_width=4,
centered=True
)
builder.add_frame(frame)
# Save the GIF
info = builder.save('/mnt/user-data/outputs/skills_vs_mcps.gif',
num_colors=128,
optimize_for_emoji=False)
print(f"GIF created successfully!")
print(f"Size: {info['size_kb']:.1f} KB ({info['size_mb']:.2f} MB)")
print(f"Frames: {info['frame_count']}")
print(f"Duration: {info['duration_seconds']:.1f}s")
# Validate for Slack
passes, check_info = check_slack_size('/mnt/user-data/outputs/skills_vs_mcps.gif', is_emoji=False)
if passes:
print("✓ Ready for Slack!")
else:
print(f"⚠ File size: {check_info['size_kb']:.1f} KB (limit: {check_info['limit_kb']} KB)")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment