[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-anthropics-skills-skills-slack-gif-creator":3},{"error":4,"detail":5,"metadata":53,"markdownContent":55,"rawMarkdown":50},false,{"repo_full_name":6,"owner":7,"repo_name":8,"repo_forks":9,"skill_path":10,"repo_stars":11,"name":12,"category_id":13,"description":14,"file_tree":15,"skill_md_content":50,"skill_id":51,"skill_key":52},"anthropics/skills","anthropics","skills",10483,"skills/slack-gif-creator",97092,"slack-gif-creator",1,"Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like \"make me a GIF of X doing Y for Slack.",[16,21,25,46],{"name":17,"path":18,"size":19,"type":20},"LICENSE.txt","skills/slack-gif-creator/LICENSE.txt",11357,"file",{"name":22,"path":23,"size":24,"type":20},"SKILL.md","skills/slack-gif-creator/SKILL.md",7841,{"name":26,"path":27,"type":28,"children":29},"core","skills/slack-gif-creator/core","folder",[30,34,38,42],{"name":31,"path":32,"size":33,"type":20},"easing.py","skills/slack-gif-creator/core/easing.py",6265,{"name":35,"path":36,"size":37,"type":20},"frame_composer.py","skills/slack-gif-creator/core/frame_composer.py",4548,{"name":39,"path":40,"size":41,"type":20},"gif_builder.py","skills/slack-gif-creator/core/gif_builder.py",9847,{"name":43,"path":44,"size":45,"type":20},"validators.py","skills/slack-gif-creator/core/validators.py",3785,{"name":47,"path":48,"size":49,"type":20},"requirements.txt","skills/slack-gif-creator/requirements.txt",66,"---\nname: slack-gif-creator\ndescription: Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like \"make me a GIF of X doing Y for Slack.\"\nlicense: Complete terms in LICENSE.txt\n---\n\n# Slack GIF Creator\n\nA toolkit providing utilities and knowledge for creating animated GIFs optimized for Slack.\n\n## Slack Requirements\n\n**Dimensions:**\n- Emoji GIFs: 128x128 (recommended)\n- Message GIFs: 480x480\n\n**Parameters:**\n- FPS: 10-30 (lower is smaller file size)\n- Colors: 48-128 (fewer = smaller file size)\n- Duration: Keep under 3 seconds for emoji GIFs\n\n## Core Workflow\n\n```python\nfrom core.gif_builder import GIFBuilder\nfrom PIL import Image, ImageDraw\n\n# 1. Create builder\nbuilder = GIFBuilder(width=128, height=128, fps=10)\n\n# 2. Generate frames\nfor i in range(12):\n    frame = Image.new('RGB', (128, 128), (240, 248, 255))\n    draw = ImageDraw.Draw(frame)\n\n    # Draw your animation using PIL primitives\n    # (circles, polygons, lines, etc.)\n\n    builder.add_frame(frame)\n\n# 3. Save with optimization\nbuilder.save('output.gif', num_colors=48, optimize_for_emoji=True)\n```\n\n## Drawing Graphics\n\n### Working with User-Uploaded Images\nIf a user uploads an image, consider whether they want to:\n- **Use it directly** (e.g., \"animate this\", \"split this into frames\")\n- **Use it as inspiration** (e.g., \"make something like this\")\n\nLoad and work with images using PIL:\n```python\nfrom PIL import Image\n\nuploaded = Image.open('file.png')\n# Use directly, or just as reference for colors/style\n```\n\n### Drawing from Scratch\nWhen drawing graphics from scratch, use PIL ImageDraw primitives:\n\n```python\nfrom PIL import ImageDraw\n\ndraw = ImageDraw.Draw(frame)\n\n# Circles/ovals\ndraw.ellipse([x1, y1, x2, y2], fill=(r, g, b), outline=(r, g, b), width=3)\n\n# Stars, triangles, any polygon\npoints = [(x1, y1), (x2, y2), (x3, y3), ...]\ndraw.polygon(points, fill=(r, g, b), outline=(r, g, b), width=3)\n\n# Lines\ndraw.line([(x1, y1), (x2, y2)], fill=(r, g, b), width=5)\n\n# Rectangles\ndraw.rectangle([x1, y1, x2, y2], fill=(r, g, b), outline=(r, g, b), width=3)\n```\n\n**Don't use:** Emoji fonts (unreliable across platforms) or assume pre-packaged graphics exist in this skill.\n\n### Making Graphics Look Good\n\nGraphics should look polished and creative, not basic. Here's how:\n\n**Use thicker lines** - Always set `width=2` or higher for outlines and lines. Thin lines (width=1) look choppy and amateurish.\n\n**Add visual depth**:\n- Use gradients for backgrounds (`create_gradient_background`)\n- Layer multiple shapes for complexity (e.g., a star with a smaller star inside)\n\n**Make shapes more interesting**:\n- Don't just draw a plain circle - add highlights, rings, or patterns\n- Stars can have glows (draw larger, semi-transparent versions behind)\n- Combine multiple shapes (stars + sparkles, circles + rings)\n\n**Pay attention to colors**:\n- Use vibrant, complementary colors\n- Add contrast (dark outlines on light shapes, light outlines on dark shapes)\n- Consider the overall composition\n\n**For complex shapes** (hearts, snowflakes, etc.):\n- Use combinations of polygons and ellipses\n- Calculate points carefully for symmetry\n- Add details (a heart can have a highlight curve, snowflakes have intricate branches)\n\nBe creative and detailed! A good Slack GIF should look polished, not like placeholder graphics.\n\n## Available Utilities\n\n### GIFBuilder (`core.gif_builder`)\nAssembles frames and optimizes for Slack:\n```python\nbuilder = GIFBuilder(width=128, height=128, fps=10)\nbuilder.add_frame(frame)  # Add PIL Image\nbuilder.add_frames(frames)  # Add list of frames\nbuilder.save('out.gif', num_colors=48, optimize_for_emoji=True, remove_duplicates=True)\n```\n\n### Validators (`core.validators`)\nCheck if GIF meets Slack requirements:\n```python\nfrom core.validators import validate_gif, is_slack_ready\n\n# Detailed validation\npasses, info = validate_gif('my.gif', is_emoji=True, verbose=True)\n\n# Quick check\nif is_slack_ready('my.gif'):\n    print(\"Ready!\")\n```\n\n### Easing Functions (`core.easing`)\nSmooth motion instead of linear:\n```python\nfrom core.easing import interpolate\n\n# Progress from 0.0 to 1.0\nt = i / (num_frames - 1)\n\n# Apply easing\ny = interpolate(start=0, end=400, t=t, easing='ease_out')\n\n# Available: linear, ease_in, ease_out, ease_in_out,\n#           bounce_out, elastic_out, back_out\n```\n\n### Frame Helpers (`core.frame_composer`)\nConvenience functions for common needs:\n```python\nfrom core.frame_composer import (\n    create_blank_frame,         # Solid color background\n    create_gradient_background,  # Vertical gradient\n    draw_circle,                # Helper for circles\n    draw_text,                  # Simple text rendering\n    draw_star                   # 5-pointed star\n)\n```\n\n## Animation Concepts\n\n### Shake/Vibrate\nOffset object position with oscillation:\n- Use `math.sin()` or `math.cos()` with frame index\n- Add small random variations for natural feel\n- Apply to x and/or y position\n\n### Pulse/Heartbeat\nScale object size rhythmically:\n- Use `math.sin(t * frequency * 2 * math.pi)` for smooth pulse\n- For heartbeat: two quick pulses then pause (adjust sine wave)\n- Scale between 0.8 and 1.2 of base size\n\n### Bounce\nObject falls and bounces:\n- Use `interpolate()` with `easing='bounce_out'` for landing\n- Use `easing='ease_in'` for falling (accelerating)\n- Apply gravity by increasing y velocity each frame\n\n### Spin/Rotate\nRotate object around center:\n- PIL: `image.rotate(angle, resample=Image.BICUBIC)`\n- For wobble: use sine wave for angle instead of linear\n\n### Fade In/Out\nGradually appear or disappear:\n- Create RGBA image, adjust alpha channel\n- Or use `Image.blend(image1, image2, alpha)`\n- Fade in: alpha from 0 to 1\n- Fade out: alpha from 1 to 0\n\n### Slide\nMove object from off-screen to position:\n- Start position: outside frame bounds\n- End position: target location\n- Use `interpolate()` with `easing='ease_out'` for smooth stop\n- For overshoot: use `easing='back_out'`\n\n### Zoom\nScale and position for zoom effect:\n- Zoom in: scale from 0.1 to 2.0, crop center\n- Zoom out: scale from 2.0 to 1.0\n- Can add motion blur for drama (PIL filter)\n\n### Explode/Particle Burst\nCreate particles radiating outward:\n- Generate particles with random angles and velocities\n- Update each particle: `x += vx`, `y += vy`\n- Add gravity: `vy += gravity_constant`\n- Fade out particles over time (reduce alpha)\n\n## Optimization Strategies\n\nOnly when asked to make the file size smaller, implement a few of the following methods:\n\n1. **Fewer frames** - Lower FPS (10 instead of 20) or shorter duration\n2. **Fewer colors** - `num_colors=48` instead of 128\n3. **Smaller dimensions** - 128x128 instead of 480x480\n4. **Remove duplicates** - `remove_duplicates=True` in save()\n5. **Emoji mode** - `optimize_for_emoji=True` auto-optimizes\n\n```python\n# Maximum optimization for emoji\nbuilder.save(\n    'emoji.gif',\n    num_colors=48,\n    optimize_for_emoji=True,\n    remove_duplicates=True\n)\n```\n\n## Philosophy\n\nThis skill provides:\n- **Knowledge**: Slack's requirements and animation concepts\n- **Utilities**: GIFBuilder, validators, easing functions\n- **Flexibility**: Create the animation logic using PIL primitives\n\nIt does NOT provide:\n- Rigid animation templates or pre-made functions\n- Emoji font rendering (unreliable across platforms)\n- A library of pre-packaged graphics built into the skill\n\n**Note on user uploads**: This skill doesn't include pre-built graphics, but if a user uploads an image, use PIL to load and work with it - interpret based on their request whether they want it used directly or just as inspiration.\n\nBe creative! Combine concepts (bouncing + rotating, pulsing + sliding, etc.) and use PIL's full capabilities.\n\n## Dependencies\n\n```bash\npip install pillow imageio numpy\n```\n","341e24d3-7b08-5e35-958a-416b391cca37","anthropics-skills-skills-slack-gif-creator",{"name":12,"description":14,"license":54},"Complete terms in LICENSE.txt","\u003Ch1>Slack GIF Creator\u003C/h1>\n\u003Cp>A toolkit providing utilities and knowledge for creating animated GIFs optimized for Slack.\u003C/p>\n\u003Ch2>Slack Requirements\u003C/h2>\n\u003Cp>\u003Cstrong>Dimensions:\u003C/strong>\u003C/p>\n\u003Cul>\n\u003Cli>Emoji GIFs: 128x128 (recommended)\u003C/li>\n\u003Cli>Message GIFs: 480x480\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cstrong>Parameters:\u003C/strong>\u003C/p>\n\u003Cul>\n\u003Cli>FPS: 10-30 (lower is smaller file size)\u003C/li>\n\u003Cli>Colors: 48-128 (fewer = smaller file size)\u003C/li>\n\u003Cli>Duration: Keep under 3 seconds for emoji GIFs\u003C/li>\n\u003C/ul>\n\u003Ch2>Core Workflow\u003C/h2>\n\u003Cdiv class=\"md-code-block\">\u003Cdiv class=\"md-code-lang\">python\u003C/div>\u003Cpre>\u003Ccode class=\"hljs language-python\">\u003Cspan class=\"hljs-keyword\">from\u003C/span> core.gif_builder \u003Cspan class=\"hljs-keyword\">import\u003C/span> GIFBuilder\n\u003Cspan class=\"hljs-keyword\">from\u003C/span> PIL \u003Cspan class=\"hljs-keyword\">import\u003C/span> Image, ImageDraw\n\n\u003Cspan class=\"hljs-comment\"># 1. Create builder\u003C/span>\nbuilder = GIFBuilder(width=\u003Cspan class=\"hljs-number\">128\u003C/span>, height=\u003Cspan class=\"hljs-number\">128\u003C/span>, fps=\u003Cspan class=\"hljs-number\">10\u003C/span>)\n\n\u003Cspan class=\"hljs-comment\"># 2. Generate frames\u003C/span>\n\u003Cspan class=\"hljs-keyword\">for\u003C/span> i \u003Cspan class=\"hljs-keyword\">in\u003C/span> \u003Cspan class=\"hljs-built_in\">range\u003C/span>(\u003Cspan class=\"hljs-number\">12\u003C/span>):\n    frame = Image.new(\u003Cspan class=\"hljs-string\">&#x27;RGB&#x27;\u003C/span>, (\u003Cspan class=\"hljs-number\">128\u003C/span>, \u003Cspan class=\"hljs-number\">128\u003C/span>), (\u003Cspan class=\"hljs-number\">240\u003C/span>, \u003Cspan class=\"hljs-number\">248\u003C/span>, \u003Cspan class=\"hljs-number\">255\u003C/span>))\n    draw = ImageDraw.Draw(frame)\n\n    \u003Cspan class=\"hljs-comment\"># Draw your animation using PIL primitives\u003C/span>\n    \u003Cspan class=\"hljs-comment\"># (circles, polygons, lines, etc.)\u003C/span>\n\n    builder.add_frame(frame)\n\n\u003Cspan class=\"hljs-comment\"># 3. Save with optimization\u003C/span>\nbuilder.save(\u003Cspan class=\"hljs-string\">&#x27;output.gif&#x27;\u003C/span>, num_colors=\u003Cspan class=\"hljs-number\">48\u003C/span>, optimize_for_emoji=\u003Cspan class=\"hljs-literal\">True\u003C/span>)\u003C/code>\u003C/pre>\u003C/div>\u003Ch2>Drawing Graphics\u003C/h2>\n\u003Ch3>Working with User-Uploaded Images\u003C/h3>\n\u003Cp>If a user uploads an image, consider whether they want to:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Use it directly\u003C/strong> (e.g., &quot;animate this&quot;, &quot;split this into frames&quot;)\u003C/li>\n\u003Cli>\u003Cstrong>Use it as inspiration\u003C/strong> (e.g., &quot;make something like this&quot;)\u003C/li>\n\u003C/ul>\n\u003Cp>Load and work with images using PIL:\u003C/p>\n\u003Cdiv class=\"md-code-block\">\u003Cdiv class=\"md-code-lang\">python\u003C/div>\u003Cpre>\u003Ccode class=\"hljs language-python\">\u003Cspan class=\"hljs-keyword\">from\u003C/span> PIL \u003Cspan class=\"hljs-keyword\">import\u003C/span> Image\n\nuploaded = Image.\u003Cspan class=\"hljs-built_in\">open\u003C/span>(\u003Cspan class=\"hljs-string\">&#x27;file.png&#x27;\u003C/span>)\n\u003Cspan class=\"hljs-comment\"># Use directly, or just as reference for colors/style\u003C/span>\u003C/code>\u003C/pre>\u003C/div>\u003Ch3>Drawing from Scratch\u003C/h3>\n\u003Cp>When drawing graphics from scratch, use PIL ImageDraw primitives:\u003C/p>\n\u003Cdiv class=\"md-code-block\">\u003Cdiv class=\"md-code-lang\">python\u003C/div>\u003Cpre>\u003Ccode class=\"hljs language-python\">\u003Cspan class=\"hljs-keyword\">from\u003C/span> PIL \u003Cspan class=\"hljs-keyword\">import\u003C/span> ImageDraw\n\ndraw = ImageDraw.Draw(frame)\n\n\u003Cspan class=\"hljs-comment\"># Circles/ovals\u003C/span>\ndraw.ellipse([x1, y1, x2, y2], fill=(r, g, b), outline=(r, g, b), width=\u003Cspan class=\"hljs-number\">3\u003C/span>)\n\n\u003Cspan class=\"hljs-comment\"># Stars, triangles, any polygon\u003C/span>\npoints = [(x1, y1), (x2, y2), (x3, y3), ...]\ndraw.polygon(points, fill=(r, g, b), outline=(r, g, b), width=\u003Cspan class=\"hljs-number\">3\u003C/span>)\n\n\u003Cspan class=\"hljs-comment\"># Lines\u003C/span>\ndraw.line([(x1, y1), (x2, y2)], fill=(r, g, b), width=\u003Cspan class=\"hljs-number\">5\u003C/span>)\n\n\u003Cspan class=\"hljs-comment\"># Rectangles\u003C/span>\ndraw.rectangle([x1, y1, x2, y2], fill=(r, g, b), outline=(r, g, b), width=\u003Cspan class=\"hljs-number\">3\u003C/span>)\u003C/code>\u003C/pre>\u003C/div>\u003Cp>\u003Cstrong>Don&#39;t use:\u003C/strong> Emoji fonts (unreliable across platforms) or assume pre-packaged graphics exist in this skill.\u003C/p>\n\u003Ch3>Making Graphics Look Good\u003C/h3>\n\u003Cp>Graphics should look polished and creative, not basic. Here&#39;s how:\u003C/p>\n\u003Cp>\u003Cstrong>Use thicker lines\u003C/strong> - Always set \u003Ccode>width=2\u003C/code> or higher for outlines and lines. Thin lines (width=1) look choppy and amateurish.\u003C/p>\n\u003Cp>\u003Cstrong>Add visual depth\u003C/strong>:\u003C/p>\n\u003Cul>\n\u003Cli>Use gradients for backgrounds (\u003Ccode>create_gradient_background\u003C/code>)\u003C/li>\n\u003Cli>Layer multiple shapes for complexity (e.g., a star with a smaller star inside)\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cstrong>Make shapes more interesting\u003C/strong>:\u003C/p>\n\u003Cul>\n\u003Cli>Don&#39;t just draw a plain circle - add highlights, rings, or patterns\u003C/li>\n\u003Cli>Stars can have glows (draw larger, semi-transparent versions behind)\u003C/li>\n\u003Cli>Combine multiple shapes (stars + sparkles, circles + rings)\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cstrong>Pay attention to colors\u003C/strong>:\u003C/p>\n\u003Cul>\n\u003Cli>Use vibrant, complementary colors\u003C/li>\n\u003Cli>Add contrast (dark outlines on light shapes, light outlines on dark shapes)\u003C/li>\n\u003Cli>Consider the overall composition\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cstrong>For complex shapes\u003C/strong> (hearts, snowflakes, etc.):\u003C/p>\n\u003Cul>\n\u003Cli>Use combinations of polygons and ellipses\u003C/li>\n\u003Cli>Calculate points carefully for symmetry\u003C/li>\n\u003Cli>Add details (a heart can have a highlight curve, snowflakes have intricate branches)\u003C/li>\n\u003C/ul>\n\u003Cp>Be creative and detailed! A good Slack GIF should look polished, not like placeholder graphics.\u003C/p>\n\u003Ch2>Available Utilities\u003C/h2>\n\u003Ch3>GIFBuilder (\u003Ccode>core.gif_builder\u003C/code>)\u003C/h3>\n\u003Cp>Assembles frames and optimizes for Slack:\u003C/p>\n\u003Cdiv class=\"md-code-block\">\u003Cdiv class=\"md-code-lang\">python\u003C/div>\u003Cpre>\u003Ccode class=\"hljs language-python\">builder = GIFBuilder(width=\u003Cspan class=\"hljs-number\">128\u003C/span>, height=\u003Cspan class=\"hljs-number\">128\u003C/span>, fps=\u003Cspan class=\"hljs-number\">10\u003C/span>)\nbuilder.add_frame(frame)  \u003Cspan class=\"hljs-comment\"># Add PIL Image\u003C/span>\nbuilder.add_frames(frames)  \u003Cspan class=\"hljs-comment\"># Add list of frames\u003C/span>\nbuilder.save(\u003Cspan class=\"hljs-string\">&#x27;out.gif&#x27;\u003C/span>, num_colors=\u003Cspan class=\"hljs-number\">48\u003C/span>, optimize_for_emoji=\u003Cspan class=\"hljs-literal\">True\u003C/span>, remove_duplicates=\u003Cspan class=\"hljs-literal\">True\u003C/span>)\u003C/code>\u003C/pre>\u003C/div>\u003Ch3>Validators (\u003Ccode>core.validators\u003C/code>)\u003C/h3>\n\u003Cp>Check if GIF meets Slack requirements:\u003C/p>\n\u003Cdiv class=\"md-code-block\">\u003Cdiv class=\"md-code-lang\">python\u003C/div>\u003Cpre>\u003Ccode class=\"hljs language-python\">\u003Cspan class=\"hljs-keyword\">from\u003C/span> core.validators \u003Cspan class=\"hljs-keyword\">import\u003C/span> validate_gif, is_slack_ready\n\n\u003Cspan class=\"hljs-comment\"># Detailed validation\u003C/span>\npasses, info = validate_gif(\u003Cspan class=\"hljs-string\">&#x27;my.gif&#x27;\u003C/span>, is_emoji=\u003Cspan class=\"hljs-literal\">True\u003C/span>, verbose=\u003Cspan class=\"hljs-literal\">True\u003C/span>)\n\n\u003Cspan class=\"hljs-comment\"># Quick check\u003C/span>\n\u003Cspan class=\"hljs-keyword\">if\u003C/span> is_slack_ready(\u003Cspan class=\"hljs-string\">&#x27;my.gif&#x27;\u003C/span>):\n    \u003Cspan class=\"hljs-built_in\">print\u003C/span>(\u003Cspan class=\"hljs-string\">&quot;Ready!&quot;\u003C/span>)\u003C/code>\u003C/pre>\u003C/div>\u003Ch3>Easing Functions (\u003Ccode>core.easing\u003C/code>)\u003C/h3>\n\u003Cp>Smooth motion instead of linear:\u003C/p>\n\u003Cdiv class=\"md-code-block\">\u003Cdiv class=\"md-code-lang\">python\u003C/div>\u003Cpre>\u003Ccode class=\"hljs language-python\">\u003Cspan class=\"hljs-keyword\">from\u003C/span> core.easing \u003Cspan class=\"hljs-keyword\">import\u003C/span> interpolate\n\n\u003Cspan class=\"hljs-comment\"># Progress from 0.0 to 1.0\u003C/span>\nt = i / (num_frames - \u003Cspan class=\"hljs-number\">1\u003C/span>)\n\n\u003Cspan class=\"hljs-comment\"># Apply easing\u003C/span>\ny = interpolate(start=\u003Cspan class=\"hljs-number\">0\u003C/span>, end=\u003Cspan class=\"hljs-number\">400\u003C/span>, t=t, easing=\u003Cspan class=\"hljs-string\">&#x27;ease_out&#x27;\u003C/span>)\n\n\u003Cspan class=\"hljs-comment\"># Available: linear, ease_in, ease_out, ease_in_out,\u003C/span>\n\u003Cspan class=\"hljs-comment\">#           bounce_out, elastic_out, back_out\u003C/span>\u003C/code>\u003C/pre>\u003C/div>\u003Ch3>Frame Helpers (\u003Ccode>core.frame_composer\u003C/code>)\u003C/h3>\n\u003Cp>Convenience functions for common needs:\u003C/p>\n\u003Cdiv class=\"md-code-block\">\u003Cdiv class=\"md-code-lang\">python\u003C/div>\u003Cpre>\u003Ccode class=\"hljs language-python\">\u003Cspan class=\"hljs-keyword\">from\u003C/span> core.frame_composer \u003Cspan class=\"hljs-keyword\">import\u003C/span> (\n    create_blank_frame,         \u003Cspan class=\"hljs-comment\"># Solid color background\u003C/span>\n    create_gradient_background,  \u003Cspan class=\"hljs-comment\"># Vertical gradient\u003C/span>\n    draw_circle,                \u003Cspan class=\"hljs-comment\"># Helper for circles\u003C/span>\n    draw_text,                  \u003Cspan class=\"hljs-comment\"># Simple text rendering\u003C/span>\n    draw_star                   \u003Cspan class=\"hljs-comment\"># 5-pointed star\u003C/span>\n)\u003C/code>\u003C/pre>\u003C/div>\u003Ch2>Animation Concepts\u003C/h2>\n\u003Ch3>Shake/Vibrate\u003C/h3>\n\u003Cp>Offset object position with oscillation:\u003C/p>\n\u003Cul>\n\u003Cli>Use \u003Ccode>math.sin()\u003C/code> or \u003Ccode>math.cos()\u003C/code> with frame index\u003C/li>\n\u003Cli>Add small random variations for natural feel\u003C/li>\n\u003Cli>Apply to x and/or y position\u003C/li>\n\u003C/ul>\n\u003Ch3>Pulse/Heartbeat\u003C/h3>\n\u003Cp>Scale object size rhythmically:\u003C/p>\n\u003Cul>\n\u003Cli>Use \u003Ccode>math.sin(t * frequency * 2 * math.pi)\u003C/code> for smooth pulse\u003C/li>\n\u003Cli>For heartbeat: two quick pulses then pause (adjust sine wave)\u003C/li>\n\u003Cli>Scale between 0.8 and 1.2 of base size\u003C/li>\n\u003C/ul>\n\u003Ch3>Bounce\u003C/h3>\n\u003Cp>Object falls and bounces:\u003C/p>\n\u003Cul>\n\u003Cli>Use \u003Ccode>interpolate()\u003C/code> with \u003Ccode>easing=&#39;bounce_out&#39;\u003C/code> for landing\u003C/li>\n\u003Cli>Use \u003Ccode>easing=&#39;ease_in&#39;\u003C/code> for falling (accelerating)\u003C/li>\n\u003Cli>Apply gravity by increasing y velocity each frame\u003C/li>\n\u003C/ul>\n\u003Ch3>Spin/Rotate\u003C/h3>\n\u003Cp>Rotate object around center:\u003C/p>\n\u003Cul>\n\u003Cli>PIL: \u003Ccode>image.rotate(angle, resample=Image.BICUBIC)\u003C/code>\u003C/li>\n\u003Cli>For wobble: use sine wave for angle instead of linear\u003C/li>\n\u003C/ul>\n\u003Ch3>Fade In/Out\u003C/h3>\n\u003Cp>Gradually appear or disappear:\u003C/p>\n\u003Cul>\n\u003Cli>Create RGBA image, adjust alpha channel\u003C/li>\n\u003Cli>Or use \u003Ccode>Image.blend(image1, image2, alpha)\u003C/code>\u003C/li>\n\u003Cli>Fade in: alpha from 0 to 1\u003C/li>\n\u003Cli>Fade out: alpha from 1 to 0\u003C/li>\n\u003C/ul>\n\u003Ch3>Slide\u003C/h3>\n\u003Cp>Move object from off-screen to position:\u003C/p>\n\u003Cul>\n\u003Cli>Start position: outside frame bounds\u003C/li>\n\u003Cli>End position: target location\u003C/li>\n\u003Cli>Use \u003Ccode>interpolate()\u003C/code> with \u003Ccode>easing=&#39;ease_out&#39;\u003C/code> for smooth stop\u003C/li>\n\u003Cli>For overshoot: use \u003Ccode>easing=&#39;back_out&#39;\u003C/code>\u003C/li>\n\u003C/ul>\n\u003Ch3>Zoom\u003C/h3>\n\u003Cp>Scale and position for zoom effect:\u003C/p>\n\u003Cul>\n\u003Cli>Zoom in: scale from 0.1 to 2.0, crop center\u003C/li>\n\u003Cli>Zoom out: scale from 2.0 to 1.0\u003C/li>\n\u003Cli>Can add motion blur for drama (PIL filter)\u003C/li>\n\u003C/ul>\n\u003Ch3>Explode/Particle Burst\u003C/h3>\n\u003Cp>Create particles radiating outward:\u003C/p>\n\u003Cul>\n\u003Cli>Generate particles with random angles and velocities\u003C/li>\n\u003Cli>Update each particle: \u003Ccode>x += vx\u003C/code>, \u003Ccode>y += vy\u003C/code>\u003C/li>\n\u003Cli>Add gravity: \u003Ccode>vy += gravity_constant\u003C/code>\u003C/li>\n\u003Cli>Fade out particles over time (reduce alpha)\u003C/li>\n\u003C/ul>\n\u003Ch2>Optimization Strategies\u003C/h2>\n\u003Cp>Only when asked to make the file size smaller, implement a few of the following methods:\u003C/p>\n\u003Col>\n\u003Cli>\u003Cstrong>Fewer frames\u003C/strong> - Lower FPS (10 instead of 20) or shorter duration\u003C/li>\n\u003Cli>\u003Cstrong>Fewer colors\u003C/strong> - \u003Ccode>num_colors=48\u003C/code> instead of 128\u003C/li>\n\u003Cli>\u003Cstrong>Smaller dimensions\u003C/strong> - 128x128 instead of 480x480\u003C/li>\n\u003Cli>\u003Cstrong>Remove duplicates\u003C/strong> - \u003Ccode>remove_duplicates=True\u003C/code> in save()\u003C/li>\n\u003Cli>\u003Cstrong>Emoji mode\u003C/strong> - \u003Ccode>optimize_for_emoji=True\u003C/code> auto-optimizes\u003C/li>\n\u003C/ol>\n\u003Cdiv class=\"md-code-block\">\u003Cdiv class=\"md-code-lang\">python\u003C/div>\u003Cpre>\u003Ccode class=\"hljs language-python\">\u003Cspan class=\"hljs-comment\"># Maximum optimization for emoji\u003C/span>\nbuilder.save(\n    \u003Cspan class=\"hljs-string\">&#x27;emoji.gif&#x27;\u003C/span>,\n    num_colors=\u003Cspan class=\"hljs-number\">48\u003C/span>,\n    optimize_for_emoji=\u003Cspan class=\"hljs-literal\">True\u003C/span>,\n    remove_duplicates=\u003Cspan class=\"hljs-literal\">True\u003C/span>\n)\u003C/code>\u003C/pre>\u003C/div>\u003Ch2>Philosophy\u003C/h2>\n\u003Cp>This skill provides:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Knowledge\u003C/strong>: Slack&#39;s requirements and animation concepts\u003C/li>\n\u003Cli>\u003Cstrong>Utilities\u003C/strong>: GIFBuilder, validators, easing functions\u003C/li>\n\u003Cli>\u003Cstrong>Flexibility\u003C/strong>: Create the animation logic using PIL primitives\u003C/li>\n\u003C/ul>\n\u003Cp>It does NOT provide:\u003C/p>\n\u003Cul>\n\u003Cli>Rigid animation templates or pre-made functions\u003C/li>\n\u003Cli>Emoji font rendering (unreliable across platforms)\u003C/li>\n\u003Cli>A library of pre-packaged graphics built into the skill\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cstrong>Note on user uploads\u003C/strong>: This skill doesn&#39;t include pre-built graphics, but if a user uploads an image, use PIL to load and work with it - interpret based on their request whether they want it used directly or just as inspiration.\u003C/p>\n\u003Cp>Be creative! Combine concepts (bouncing + rotating, pulsing + sliding, etc.) and use PIL&#39;s full capabilities.\u003C/p>\n\u003Ch2>Dependencies\u003C/h2>\n\u003Cdiv class=\"md-code-block\">\u003Cdiv class=\"md-code-lang\">bash\u003C/div>\u003Cpre>\u003Ccode class=\"hljs language-bash\">pip install pillow imageio numpy\u003C/code>\u003C/pre>\u003C/div>"]