creating-slack-gifs について
Slack 向けのアニメーション GIF 制作を支援する知識と Python ツールをまとめたスキルです。GIF の組み立て、検証、イージング、フレーム作成補助に加え、実際の表示サイズでの視認性確認も扱います。
主な機能
- 01GIFBuilder によるフレーム組み立てと保存
- 02Slack 要件の検証
- 03イージングとフレーム補助ツール
- 04PIL による描画ガイド
- 05縮小表示とループの確認
活用シーン
- 01Slack 絵文字用 GIF の制作
- 02アップロード画像のアニメーション化
- 03GIF 要件と縮小時の視認性確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ osmontero/ opencode-skills --skill creating-slack-gifsインストール後、Claude Code で/creating-slack-gifsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSE.txtcore/easing.pyframe_composer.pygif_builder.pyvalidators.py詳細
Slack GIF の作成
このスキルでは、Slack 固有の要件に合わせて最適化されたアニメーション GIF を作成するためのユーティリティと知識を提供します。
Slack の要件
サイズ:
- 絵文字 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 以上を設定してください。細い線(width=1)はギザギザして、素人っぽく見えます。
視覚的な奥行きを加える:
- 背景にグラデーションを使う(
create_gradient_background) - 複数の図形を重ねて複雑さを加える(例:星の内側に小さな星を配置する)
図形をより魅力的にする:
- 単純な円を描くだけでなく、ハイライトや輪、模様を加える
- 星の背後に、より大きく半透明の星を描いて光彩を加える
- 複数の図形を組み合わせる(星ときらめき、円と輪など)
色に注意する:
- 鮮やかで、互いに引き立て合う色を使う
- コントラストを加える(明るい図形には暗い輪郭、暗い図形には明るい輪郭)
- 全体の構成を考慮する
複雑な図形(ハート、雪の結晶など)の場合:
- 多角形と楕円を組み合わせる
- 対称になるよう、点の位置を慎重に計算する
- 細部を加える(ハートにはハイライトの曲線、雪の結晶には複雑な枝を加えられます)
創造性を発揮し、細部まで作り込みましょう。良い Slack GIF は、仮のグラフィックのようではなく、洗練された見た目に仕上がっているはずです。
利用可能なユーティリティ
GIFBuilder(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)
Validators(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!")
Easing Functions(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
Frame Helpers(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 倍の範囲で拡大縮小する
バウンス
オブジェクトを落下させて、跳ね返らせます。
- 着地には
easing='bounce_out'を指定してinterpolate()を使う - 落下には
easing='ease_in'を使う(加速) - 各フレームで y 方向の速度を増やして重力を適用する
回転
オブジェクトを中心の周りで回転させます。
- PIL:
image.rotate(angle, resample=Image.BICUBIC) - 揺れを表現するには、角度を線形に変化させず、サイン波を使う
フェードイン/アウト
徐々に表示したり、消したりします。
- RGBA 画像を作成し、アルファチャンネルを調整する
- または
Image.blend(image1, image2, alpha)を使う - フェードイン:アルファを 0 から 1 にする
- フェードアウト:アルファを 1 から 0 にする
スライド
オブジェクトを画面外から所定の位置へ移動させます。
- 開始位置:フレームの範囲外
- 終了位置:目標の位置
- 滑らかに停止させるには
easing='ease_out'を指定してinterpolate()を使う - 行き過ぎを表現するには
easing='back_out'を使う
ズーム
拡大効果のためにサイズと位置を変更します。
- ズームイン:0.1 から 2.0 まで拡大し、中央で切り抜く
- ズームアウト:2.0 から 1.0 まで縮小する
- 劇的な効果を加えるため、モーションブラーを追加できます(PIL フィルター)
爆発/パーティクルバースト
外側へ放射状に広がるパーティクルを作成します。
- ランダムな角度と速度でパーティクルを生成します
- 各パーティクルを更新します:
x += vx、y += vy - 重力を加えます:
vy += gravity_constant - 時間の経過とともにパーティクルをフェードアウトさせます(アルファ値を下げます)
最適化の方法
ファイルサイズを小さくするよう依頼された場合に限り、次の方法をいくつか実装します。
- フレーム数を減らす — FPSを下げる(20ではなく10にする)、または再生時間を短くします
- 色数を減らす —
num_colors=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の機能を存分に活用しましょう。
検証 — 提示する前に
Slackのメッセージでは、絵文字はおよそ22×22 pxで表示されます。128×128のデザインは実寸では美しく見えても、実際の表示サイズでは判読できないぼやけた画像になることがあります。これは最もよくある失敗です。
from PIL import Image
from core.validators import validate_gif
passes, info = validate_gif('out.gif', is_emoji=True, verbose=True)
print(info) # dimensions, frame count, file size, color count
# What Slack actually shows — inspect this, not the 128px version
im = Image.open('out.gif')
im.seek(0); im.convert('RGB').resize((22, 22), Image.LANCZOS).resize(
(176, 176), Image.NEAREST).save('/tmp/emoji-actual-size.png')
/tmp/emoji-actual-size.pngを読み込み、被写体を判別できることを確認します。次に、以下を確認します。
- 22pxでも判別できる。 シルエットがはっきりしています。線幅が約4px未満の細部は完全に見えなくなります。
- 明るい背景と暗い背景の両方で判別できる。 Slackにはライトテーマとダークテーマがあります。GIFを
#ffffffと#1a1d21に対して確認してください。透明背景上の暗い被写体は、ダークモードでは見えなくなります。 - シームレスにループする。 最後のフレームから最初のフレームへ自然につながる必要があります。繰り返すたびに目に見える切り替わりが起きるのは、ループ画像で最も気が散る欠点です。
- 絵文字の場合は128 KB未満(Slackの厳格な上限)、メッセージ用GIFの場合は2 MB未満です。
- 動きが再生速度でも判別できる。 10 fpsでは、12フレームのアニメーションは1.2秒です。重要な瞬間が2フレームだけなら、誰にも気づかれません。
- 3 Hzを超える点滅がない。 急速に点滅する絵文字は、忙しいチャンネルでは実際に発作を引き起こす危険があります。
シームレスなループの確認 — 周期的なアニメーションでは、最初と最後のフレームがほぼ同じである必要があります。
import numpy as np
frames = []
im = Image.open('out.gif')
for i in range(im.n_frames):
im.seek(i); frames.append(np.asarray(im.convert('RGB'), dtype=float))
delta = np.abs(frames[0] - frames[-1]).mean()
print(f"loop seam delta: {delta:.1f}") # under ~6 is a clean cycle
前提条件
Pythonスクリプトを実行する前に、opencodeの仮想環境を有効化してください。
source ~/.local/opencode-venv/bin/activate
その後は通常どおりpython3 core/...を使います。すべての依存関係(Pillow、imageio、imageio-ffmpeg、numpy)は、opencodeのPython venvで一元管理されています。
関連スキル
- designing-frontend-interfaces —
designing-frontend-interfaces/references/motion.mdではイージングの選択とオーケストレーションを、designing-frontend-interfaces/references/color-and-theme.mdでは48色への量子化後も見栄えを保つパレットの作成方法を解説しています - designing-canvas-art —
canvas-fonts/にはGIFにテキストを入れる場合に使える実際の書体があり、designing-canvas-art/references/rendering.mdではPILの合成テクニックを解説しています