ホーム / Skills / SNSコンテンツ / Slack GIF Creator
SNSコンテンツ

Slack GIF Creator

Anthropic, PBCApache-2.0Skill の言語: 英語
ライセンス確認済みスクリプトを含む
デモ実行記録 · claude-sonnet-5-5 · 2026年10月
Create a Python program for a 128x128 Slack emoji GIF showing a blue notification bell pulsing twice and then emitting three gold sparkles. Use the slack-gif-creator skill. The animation should run for less than three seconds, use 48 colors or fewer, save to notification-bell.gif, and explain how to validate the output.
Skill: slack-gif-creator
Read: core/gif_builder.py
Read: core/validators.py

I only have file-reading and skill tools in this session, so I couldn't write the file or run it. Below is the program, plus the steps to run and validate it. It's written against the skill's real GIFBuilder and validators APIs, which I read first.

Two things in the skill's code shaped the design:

  • save(..., optimize_for_emoji=True) throws away frames when there are more than 12. That would wreck a pulse-then-sparkle sequence. The program passes num_colors=48 directly instead and keeps all 24 frames.
  • validate_gif only checks dimensions, size and frame count. It doesn't check colors or the 3-second limit, so the program has its own checks for those.

make_notification_bell.py

Place it in the project root, next to .claude/skills/.

#!/usr/bin/env python3
"""128x128 Slack emoji: blue bell pulses twice, then emits three gold sparkles."""

import math
import sys
from pathlib import Path

from PIL import Image, ImageDraw

sys.path.insert(0, str(Path(__file__).parent / ".claude" / "skills" / "slack-gif-creator"))
from core.gif_builder import GIFBuilder  # noqa: E402
from core.validators import validate_gif  # noqa: E402

SIZE = 128
SS = 4                      # supersampling factor for smooth edges
FPS = 10                    # 24 frames @ 10 fps = 2.4s
NUM_FRAMES = 24
NUM_COLORS = 48
OUTPUT = "notification-bell.gif"

BG = (22, 30, 58)
BELL = (52, 132, 232)
BELL_LIGHT = (130, 190, 255)
BELL_DARK = (24, 76, 160)
RIM = (86, 156, 245)
GOLD = (255, 200, 40)
GOLD_CORE = (255, 238, 150)

BELL_CENTER = (64, 74)
PULSE_FRAMES = 7            # frames per pulse
PULSE_AMOUNT = 0.18
# (x, y, start_frame) for each sparkle
SPARKLES = [(20, 30, 12), (64, 14, 14), (108, 30, 16)]
SPARKLE_LIFE = 8
SPARKLE_RADIUS = 12


def bell(draw, scale):
    cx, cy = BELL_CENTER

    def p(x, y):
        return ((cx + x * scale) * SS, (cy + y * scale) * SS)

    def box(x1, y1, x2, y2):
        return [*p(x1, y1), *p(x2, y2)]

    w = max(2, round(2 * SS))
    draw.ellipse(box(-4, -38, 4, -30), fill=BELL_DARK)                       # knob
    draw.ellipse(box(-20, -32, 20, 8), fill=BELL, outline=BELL_DARK, width=w)  # dome
    draw.polygon([p(-20, -12), p(20, -12), p(28, 18), p(-28, 18)], fill=BELL)  # skirt
    draw.line([p(-20, -12), p(-28, 18)], fill=BELL_DARK, width=w)
    draw.line([p(20, -12), p(28, 18)], fill=BELL_DARK, width=w)
    draw.rounded_rectangle(box(-31, 16, 31, 25), radius=4 * SS, fill=RIM,
                           outline=BELL_DARK, width=w)
    draw.line([p(-12, -22), p(-16, 6)], fill=BELL_LIGHT, width=3 * SS)        # highlight
    draw.ellipse(box(-7, 24, 7, 38), fill=BELL_DARK)                          # clapper


def sparkle(draw, x, y, r):
    def star(radius, color):
        pts = []
        for k in range(8):
            ang = math.radians(-90 + k * 45)
            rad = radius if k % 2 == 0 else radius * 0.3
            pts.append(((x + rad * math.cos(ang)) * SS, (y + rad * math.sin(ang)) * SS))
        draw.polygon(pts, fill=color)

    star(r, GOLD)
    star(r * 0.5, GOLD_CORE)


def render(i):
    img = Image.new("RGB", (SIZE * SS, SIZE * SS), BG)
    draw = ImageDraw.Draw(img)

    scale = 1.0
    if i < 2 * PULSE_FRAMES:
        phase = (i % PULSE_FRAMES) / PULSE_FRAMES
        scale += PULSE_AMOUNT * math.sin(math.pi * phase)
    bell(draw, scale)

    for sx, sy, start in SPARKLES:
        t = (i - start) / SPARKLE_LIFE
        if 0 <= t < 1:
            drift = -6 * t                       # float upward as it fades
            sparkle(draw, sx, sy + drift, SPARKLE_RADIUS * math.sin(math.pi * t))

    return img.resize((SIZE, SIZE), Image.Resampling.LANCZOS)


def main():
    builder = GIFBuilder(width=SIZE, height=SIZE, fps=FPS)
    for i in range(NUM_FRAMES):
        builder.add_frame(render(i))
    # No optimize_for_emoji: it would drop frames above 12. Colors are capped explicitly.
    builder.save(OUTPUT, num_colors=NUM_COLORS, remove_duplicates=False)

    validate_gif(OUTPUT, is_emoji=True, verbose=True)


if __name__ == "__main__":
    main()

Run it

pip install pillow imageio numpy
python make_notification_bell.py

Validate the output

  1. Skill validator. The script calls validate_gif itself. It confirms 128x128 and reports file size, frame count and duration. Expect about 24 frames at 10 fps, so 2.4s.

  2. Colors and duration. The skill validator doesn't check these two limits, so run this:

    from PIL import Image, ImageSequence
    
    im = Image.open("notification-bell.gif")
    frames = [f.convert("RGB") for f in ImageSequence.Iterator(im)]
    colors = set()
    for f in frames:
        colors |= {c for _, c in f.getcolors(maxcolors=128 * 128)}
    total = sum(f.info.get("duration", 100) for f in ImageSequence.Iterator(im)) / 1000
    print(im.size, len(frames), "frames,", len(colors), "colors,", f"{total:.1f}s")
    assert im.size == (128, 128) and len(colors) <= 48 and total < 3
    
  3. Visual check. Open the GIF in a browser or Quick Look. Confirm that:

    • the bell pulses twice, during roughly the first 1.4s;
    • three gold sparkles then pop up in sequence and fade;
    • the loop restarts cleanly.
  4. Slack check. Upload it through Customize Workspace → Emoji. Slack's emoji limit is 128 KB, and this should come in well under that. Check the file size to be sure.

Since I haven't run it, the sparkle positions may overlap the bell's top corners a little at peak pulse. If so, adjust the SPARKLES coordinates or PULSE_AMOUNT. If you'd like me to run it and fix anything, I'd need file-write and shell access.

Real run recorded with claude-code / claude-sonnet-5-5. Output is shown verbatim, unmodified.

できること

Slack に適した GIF 作成の制約、アニメーション指針、Python ユーティリティを提供します。

進め方

  1. Pillow の描画プリミティブで画像フレームを作成します。
  2. イージング、フレーム合成、減色、重複フレーム除去のヘルパーを使います。
  3. GIF を保存し、寸法、サイズ、フレーム数、時間の情報を検証します。

向いている用途

カスタム Slack 絵文字、小さなループアニメーション、生成 GIF の検証。

補足とリスク

ユーザーが指定した画像または GIF のパスを読み込み、指定された出力パスに GIF を書き込む Python モジュールを含みます。ネットワークや認証情報にはアクセスしません。Pillow、imageio、imageio-ffmpeg、NumPy が必要です。使用前に生成物と出力パスを確認してください。