Slack GIF クリエイター
テーマ別の Slack GIF パックの生成、プロンプトの管理、フレーム パイプライン、およびレビュー チェックリストに関するエージェント スキルのチュートリアル。
出典: anthropics/skills (MIT) を基にしたコンテンツ。
Slack に最適化されたアニメーション GIF を作成するためのユーティリティと知識を提供するツールキット。
スラック要件
寸法:
- 絵文字 GIF: 128x128 (推奨)
- メッセージGIF: 480x480
パラメータ:
- FPS: 10-30 (低いほどファイルサイズが小さくなります)
- 色: 48 ~ 128 (少ない = ファイル サイズが小さくなります)
- 長さ: 絵文字 GIF の場合は 3 秒以内にしてください
コアワークフロー
from core.gif_builder import GIFBuilder
from PIL import Image, ImageDraw
# 1. Create builder
builder = GIFBuilder(width=128, height=128, fps=10)
# 2. Generate frames
for i in range(12):
frame = Image.new('RGB', (128, 128), (240, 248, 255))
draw = ImageDraw.Draw(frame)
# Draw your animation using PIL primitives
# (circles, polygons, lines, etc.)
builder.add_frame(frame)
# 3. Save with optimization
builder.save('output.gif', num_colors=48, optimize_for_emoji=True)グラフィックの描画
ユーザーがアップロードした画像の操作
ユーザーが画像をアップロードする場合は、次のことを行うかどうかを検討してください。
- 直接使用 (例: 「これをアニメーション化する」、「これをフレームに分割する」)
- インスピレーションとして使用してください (例: 「こんなものを作ってください」)
PIL を使用してイメージを読み込み、操作します。
from PIL import Image
uploaded = Image.open('file.png')
# Use directly, or just as reference for colors/styleゼロから描く
グラフィックスを最初から描画する場合は、PIL ImageDraw プリミティブを使用します。
from PIL import ImageDraw
draw = ImageDraw.Draw(frame)
# Circles/ovals
draw.ellipse([x1, y1, x2, y2], fill=(r, g, b), outline=(r, g, b), width=3)
# Stars, triangles, any polygon
points = [(x1, y1), (x2, y2), (x3, y3), ...]
draw.polygon(points, fill=(r, g, b), outline=(r, g, b), width=3)
# Lines
draw.line([(x1, y1), (x2, y2)], fill=(r, g, b), width=5)
# Rectangles
draw.rectangle([x1, y1, x2, y2], fill=(r, g, b), outline=(r, g, b), width=3)使用しないでください: 絵文字フォント (プラットフォーム間で信頼性が低い) を使用したり、事前にパッケージ化されたグラフィックがこのスキルに存在すると仮定したりしないでください。
グラフィックスの見栄えを良くする
グラフィックは、ベーシックなものではなく、洗練され創造的に見える必要があります。その方法は次のとおりです。
太い線を使用してください - 輪郭と線は必ずwidth=2以上に設定してください。細い線 (幅=1) は途切れ途切れで素人っぽく見えます。
視覚的な奥行きを追加:
- 背景にグラデーションを使用する (
create_gradient_background) - 複数の形状を重ねて複雑にする (例: 星の内側に小さな星がある)
図形をもっと面白くする:
- 単純な円を描くだけでなく、ハイライト、リング、またはパターンを追加します。
- 星には輝きを持たせることができます (後ろに大きな半透明のバージョンを描画します)
- 複数の形状を組み合わせる (星 + 輝き、円 + リング)
色に注意してください:
- 鮮やかな補色を使用する
- コントラストを追加する (明るい形状に暗い輪郭、暗い形状に明るい輪郭)
- 全体の構成を考える
複雑な形状の場合 (ハート、雪の結晶など):
- 多角形と楕円を組み合わせて使用する
- 対称性を保つために点を慎重に計算する
- 詳細を追加します (ハートにはハイライト カーブを付けることができ、雪の結晶には複雑な枝を付けることができます)
創造的かつ詳細に表現してください。優れた Slack GIF は、プレースホルダー グラフィックではなく、洗練されたものである必要があります。
利用可能なユーティリティ
GIFビルダー (core.gif_builder)
フレームを組み立てて Slack 用に最適化します。
builder = GIFBuilder(width=128, height=128, fps=10)
builder.add_frame(frame) # Add PIL Image
builder.add_frames(frames) # Add list of frames
builder.save('out.gif', num_colors=48, optimize_for_emoji=True, remove_duplicates=True)バリデーター (core.validators)
GIF が Slack の要件を満たしているかどうかを確認します。
from core.validators import validate_gif, is_slack_ready
# Detailed validation
passes, info = validate_gif('my.gif', is_emoji=True, verbose=True)
# Quick check
if is_slack_ready('my.gif'):
print("Ready!")イージング関数 (core.easing)
直線的ではなく滑らかな動き:
from core.easing import interpolate
# Progress from 0.0 to 1.0
t = i / (num_frames - 1)
# Apply easing
y = interpolate(start=0, end=400, t=t, easing='ease_out')
# Available: linear, ease_in, ease_out, ease_in_out,
# bounce_out, elastic_out, back_outフレームヘルパー (core.frame_composer)
一般的なニーズに対応する便利な機能:
from core.frame_composer import (
create_blank_frame, # Solid color background
create_gradient_background, # Vertical gradient
draw_circle, # Helper for circles
draw_text, # Simple text rendering
draw_star # 5-pointed star
)アニメーションのコンセプト
シェイク/バイブレーション
振動によるオブジェクトの位置のオフセット:
math.sin()またはmath.cos()をフレーム インデックスとともに使用します- 小さなランダムな変化を加えて自然な雰囲気を実現
- x および/または y 位置に適用します
脈拍・心拍
オブジェクトのサイズをリズミカルに拡大縮小する:
- 滑らかなパルスには
math.sin(t * frequency * 2 * math.pi)を使用してください - 心拍の場合: 2 つの速いパルスの後、一時停止します (正弦波を調整します)。
- 基本サイズの 0.8 ~ 1.2 の間でスケールします
バウンス
物体が落ちたり跳ねたりする:
- 着陸には
interpolate()とeasing='bounce_out'を使用します - 落下(加速)には
easing='ease_in'を使用してください - 各フレームの Y 速度を増加させて重力を適用します
スピン/回転
オブジェクトを中心を中心に回転します:
- ピル:
image.rotate(angle, resample=Image.BICUBIC) - ウォブルの場合: 角度に線形ではなく正弦波を使用します。
フェードイン/フェードアウト
徐々に現れたり消えたりします。
- RGBA画像の作成、アルファチャンネルの調整
- または、
Image.blend(image1, image2, alpha)を使用してください - フェードイン: 0 から 1 までのアルファ
- フェードアウト: 1 から 0 までのアルファ
スライド
オブジェクトを画面外から所定の位置に移動します。
- 開始位置: フレーム境界の外側
- 終了位置: ターゲット位置
- スムーズに停止するには、
interpolate()をeasing='ease_out'と一緒に使用します。 - オーバーシュートの場合:
easing='back_out'を使用します。
ズーム
ズーム効果のスケールと位置:
- ズームイン: 0.1 ~ 2.0 のスケール、クロップ中心
- ズームアウト: 2.0 から 1.0 にスケールします。
- ドラマ用のモーションブラーを追加可能(PILフィルター)
爆発/パーティクルバースト
外側に放射するパーティクルを作成します。
- ランダムな角度と速度を持つパーティクルを生成する
- 各パーティクルを更新:
x += vx、y += vy - 重力を追加:
vy += gravity_constant - 時間の経過とともにパーティクルをフェードアウトします (アルファを減少させます)
最適化戦略
ファイル サイズを小さくするように要求された場合にのみ、次のメソッドのいくつかを実装してください。
- フレーム数が少ない - FPS が低い (20 ではなく 10)、または継続時間が短い
- 色数が少ない - 128 色ではなく
num_colors=48 - 小さい寸法 - 480x480 ではなく 128x128
- 重複を削除 - save() の
remove_duplicates=True - 絵文字モード -
optimize_for_emoji=Trueは自動最適化します
# Maximum optimization for emoji
builder.save(
'emoji.gif',
num_colors=48,
optimize_for_emoji=True,
remove_duplicates=True
)哲学
このスキルは以下を提供します。
- 知識: Slack の要件とアニメーションの概念
- ユーティリティ: GIFBuilder、バリデーター、イージング関数
- 柔軟性: PIL プリミティブを使用してアニメーション ロジックを作成
以下は提供されません。
- リジッドアニメーションテンプレートまたは既製の関数
- 絵文字フォントのレンダリング (プラットフォーム間で信頼性が低い)
- スキルに組み込まれた事前にパッケージ化されたグラフィックスのライブラリ
ユーザーのアップロードに関する注意: このスキルには事前に構築されたグラフィックスは含まれていませんが、ユーザーが画像をアップロードする場合は、PIL を使用して画像を読み込み、操作します。画像を直接使用したいのか、それとも単なるインスピレーションとして使用したいのか、ユーザーのリクエストに基づいて解釈します。
クリエイティブになってください!コンセプト (バウンス + 回転、パルス + スライドなど) を組み合わせて、PIL の機能を最大限に活用します。
依存関係
pip install pillow imageio numpyリソースファイル
ライセンス.txt
バイナリリソース
コア/イージング.py
バイナリリソース
core/frame_composer.py
core/frame_composer.py をダウンロード
#!/usr/bin/env python3
"""
Frame Composer - Utilities for composing visual elements into frames.
Provides functions for drawing shapes, text, emojis, and compositing elements
together to create animation frames.
"""
from typing import Optional
import numpy as np
from PIL import Image, ImageDraw, ImageFont
def create_blank_frame(
width: int, height: int, color: tuple[int, int, int] = (255, 255, 255)
) -> Image.Image:
"""
Create a blank frame with solid color background.
Args:
width: Frame width
height: Frame height
color: RGB color tuple (default: white)
Returns:
PIL Image
"""
return Image.new("RGB", (width, height), color)
def draw_circle(
frame: Image.Image,
center: tuple[int, int],
radius: int,
fill_color: Optional[tuple[int, int, int]] = None,
outline_color: Optional[tuple[int, int, int]] = None,
outline_width: int = 1,
) -> Image.Image:
"""
Draw a circle on a frame.
Args:
frame: PIL Image to draw on
center: (x, y) center position
radius: Circle radius
fill_color: RGB fill color (None for no fill)
outline_color: RGB outline color (None for no outline)
outline_width: Outline width in pixels
Returns:
Modified frame
"""
draw = ImageDraw.Draw(frame)
x, y = center
bbox = [x - radius, y - radius, x + radius, y + radius]
draw.ellipse(bbox, fill=fill_color, outline=outline_color, width=outline_width)
return frame
def draw_text(
frame: Image.Image,
text: str,
position: tuple[int, int],
color: tuple[int, int, int] = (0, 0, 0),
centered: bool = False,
) -> Image.Image:
"""
Draw text on a frame.
Args:
frame: PIL Image to draw on
text: Text to draw
position: (x, y) position (top-left unless centered=True)
color: RGB text color
centered: If True, center text at position
Returns:
Modified frame
"""
draw = ImageDraw.Draw(frame)
# Uses Pillow's default font.
# If the font should be changed for the emoji, add additional logic here.
font = ImageFont.load_default()
if centered:
bbox = draw.textbbox((0, 0), text, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
x = position[0] - text_width // 2
y = position[1] - text_height // 2
position = (x, y)
draw.text(position, text, fill=color, font=font)
return frame
def create_gradient_background(
width: int,
height: int,
top_color: tuple[int, int, int],
bottom_color: tuple[int, int, int],
) -> Image.Image:
"""
Create a vertical gradient background.
Args:
width: Frame width
height: Frame height
top_color: RGB color at top
bottom_color: RGB color at bottom
Returns:
PIL Image with gradient
"""
frame = Image.new("RGB", (width, height))
draw = ImageDraw.Draw(frame)
# Calculate color step for each row
r1, g1, b1 = top_color
r2, g2, b2 = bottom_color
for y in range(height):
# Interpolate color
ratio = y / height
r = int(r1 * (1 - ratio) + r2 * ratio)
g = int(g1 * (1 - ratio) + g2 * ratio)
b = int(b1 * (1 - ratio) + b2 * ratio)
# Draw horizontal line
draw.line([(0, y), (width, y)], fill=(r, g, b))
return frame
def draw_star(
frame: Image.Image,
center: tuple[int, int],
size: int,
fill_color: tuple[int, int, int],
outline_color: Optional[tuple[int, int, int]] = None,
outline_width: int = 1,
) -> Image.Image:
"""
Draw a 5-pointed star.
Args:
frame: PIL Image to draw on
center: (x, y) center position
size: Star size (outer radius)
fill_color: RGB fill color
outline_color: RGB outline color (None for no outline)
outline_width: Outline width
Returns:
Modified frame
"""
import math
draw = ImageDraw.Draw(frame)
x, y = center
# Calculate star points
points = []
for i in range(10):
angle = (i * 36 - 90) * math.pi / 180 # 36 degrees per point, start at top
radius = size if i % 2 == 0 else size * 0.4 # Alternate between outer and inner
px = x + radius * math.cos(angle)
py = y + radius * math.sin(angle)
points.append((px, py))
# Draw star
draw.polygon(points, fill=fill_color, outline=outline_color, width=outline_width)
return framecore/gif_builder.py
バイナリリソース
core/validators.py
#!/usr/bin/env python3
"""
Validators - Check if GIFs meet Slack's requirements.
These validators help ensure your GIFs meet Slack's size and dimension constraints.
"""
from pathlib import Path
def validate_gif(
gif_path: str | Path, is_emoji: bool = True, verbose: bool = True
) -> tuple[bool, dict]:
"""
Validate GIF for Slack (dimensions, size, frame count).
Args:
gif_path: Path to GIF file
is_emoji: True for emoji (128x128 recommended), False for message GIF
verbose: Print validation details
Returns:
Tuple of (passes: bool, results: dict with all details)
"""
from PIL import Image
gif_path = Path(gif_path)
if not gif_path.exists():
return False, {"error": f"File not found: {gif_path}"}
# Get file size
size_bytes = gif_path.stat().st_size
size_kb = size_bytes / 1024
size_mb = size_kb / 1024
# Get dimensions and frame info
try:
with Image.open(gif_path) as img:
width, height = img.size
# Count frames
frame_count = 0
try:
while True:
img.seek(frame_count)
frame_count += 1
except EOFError:
pass
# Get duration
try:
duration_ms = img.info.get("duration", 100)
total_duration = (duration_ms * frame_count) / 1000
fps = frame_count / total_duration if total_duration > 0 else 0
except:
total_duration = None
fps = None
except Exception as e:
return False, {"error": f"Failed to read GIF: {e}"}
# Validate dimensions
if is_emoji:
optimal = width == height == 128
acceptable = width == height and 64 <= width <= 128
dim_pass = acceptable
else:
aspect_ratio = (
max(width, height) / min(width, height)
if min(width, height) > 0
else float("inf")
)
dim_pass = aspect_ratio <= 2.0 and 320 <= min(width, height) <= 640
results = {
"file": str(gif_path),
"passes": dim_pass,
"width": width,
"height": height,
"size_kb": size_kb,
"size_mb": size_mb,
"frame_count": frame_count,
"duration_seconds": total_duration,
"fps": fps,
"is_emoji": is_emoji,
"optimal": optimal if is_emoji else None,
}
# Print if verbose
if verbose:
print(f"\nValidating {gif_path.name}:")
print(
f" Dimensions: {width}x{height}"
+ (
f" ({'optimal' if optimal else 'acceptable'})"
if is_emoji and acceptable
else ""
)
)
print(
f" Size: {size_kb:.1f} KB"
+ (f" ({size_mb:.2f} MB)" if size_mb >= 1.0 else "")
)
print(
f" Frames: {frame_count}"
+ (f" @ {fps:.1f} fps ({total_duration:.1f}s)" if fps else "")
)
if not dim_pass:
print(
f" Note: {'Emoji should be 128x128' if is_emoji else 'Unusual dimensions for Slack'}"
)
if size_mb > 5.0:
print(f" Note: Large file size - consider fewer frames/colors")
return dim_pass, results
def is_slack_ready(
gif_path: str | Path, is_emoji: bool = True, verbose: bool = True
) -> bool:
"""
Quick check if GIF is ready for Slack.
Args:
gif_path: Path to GIF file
is_emoji: True for emoji GIF, False for message GIF
verbose: Print feedback
Returns:
True if dimensions are acceptable
"""
passes, _ = validate_gif(gif_path, is_emoji, verbose)
return passes要件.txt
pillow>=10.0.0
imageio>=2.31.0
imageio-ffmpeg>=0.4.9
numpy>=1.24.0GitHub で見る
フロントエンド設計
高いデザイン品質を備えた、特徴的な実稼働グレードのフロントエンド インターフェイスを作成します。このスキルは、ユーザーが Web コンポーネント、ページ、アーティファクト、ポスター、またはアプリケーションの構築を要求するときに使用します (例には、Web サイト、ランディング ページ、ダッシュボード、React コンポーネント、HTML/CSS レイアウト、または Web UI のスタイル設定/美化が含まれます)。一般的な AI の美学を回避した、創造的で洗練されたコードと UI デザインを生成します。
Web アーティファクト ビルダー
shadcn コンポーネントと自動化スクリプトを使用して、React および Tailwind アーティファクトを単一の HTML 出力にバンドルするエージェント スキル。
クロードスキルのドキュメント