Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
everyinc avatar

Social Clips

  • 1 installs
  • 44 repo stars
  • Updated July 10, 2026
  • everyinc/everyskill

Turns Slack threads into animated Slack-styled social videos with avatars, typing indicators, and reactions, rendered to MP4 and GIF via Remotion.

About

Pulls a Slack thread and user avatars, builds an animated dark-mode Slack composition with spring animations, and renders vertical and horizontal MP4/GIF outputs. A developer uses it to produce shareable social video clips from Slack conversations.

  • Realistic Slack UI: avatars, typing indicators, reactions, spring animations
  • Renders vertical and horizontal MP4 plus GIF via Remotion

Social Clips by the numbers

  • 1 all-time installs (skills.sh)
  • Ranked #1,200 of 1,335 Generative Media skills by installs in the Skillselion catalog
  • Data as of Jul 13, 2026 (Skillselion catalog sync)
npx skills add https://github.com/everyinc/everyskill --skill social-clips

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs1
repo stars44
Last updatedJuly 10, 2026
Repositoryeveryinc/everyskill

What it does

Turns Slack threads into animated Slack-styled social videos with avatars, typing indicators, and reactions, rendered to MP4 and GIF via Remotion.

Files

SKILL.mdMarkdownGitHub ↗

Social Clips

Turn Slack threads into animated social videos. Slack dark mode with real profile photos, typing indicators, reactions, and spring animations.

Outputs: MP4 (vertical + horizontal) and GIF.

Quick Start

npm install
npm run studio           # preview in browser
npm run render:stories   # 1080x1920 MP4
npm run render:landscape # 1920x1080 MP4
npm run gif:stories      # 1080x1920 GIF
npm run gif:landscape    # 1920x1080 GIF

Or render any composition directly:

npx remotion render <composition-id> out/<name>.mp4 --codec=h264 --crf=18

Making a New Clip

1. Pull the Slack thread

mcp__slack__slack_get_thread_replies(channel_id, thread_ts)

Extract thread_ts from the URL: p1234567890123456 → 1234567890.123456

2. Get avatar photos

mcp__slack__slack_get_users(limit: 200)

Download image_512 URLs into src/assets/avatars/:

curl -sL -o src/assets/avatars/name.jpg "https://avatars.slack-edge.com/..."

3. Add senders

In src/slack-types.ts:

1. Add to the SlackSender union type 2. Import the avatar image 3. Add a SenderConfig entry with avatarPhoto

The avatar component renders the photo when available, falls back to colored initials.

4. Write the data file

Create src/data/<clip-name>.ts:

import type { SlackMessage, SlackTimedEvent } from '../slack-types';

export const MESSAGES: SlackMessage[] = [
  { id: 0, sender: 'dan', text: 'Opening message' },
  { id: 1, sender: 'r2c2', text: 'Reply with *bold* and @mentions' },
  { id: 2, sender: 'austin', text: 'Another message', reactions: [{ emoji: '🔥', count: 3 }] },
];

export const TIMELINE: SlackTimedEvent[] = [
  // Messages
  { type: 'message', messageIndex: 0, startFrame: 30, durationFrames: 40 },

  // Typing indicator before a reply
  { type: 'typing', typingSender: 'r2c2', startFrame: 75, durationFrames: 40 },
  { type: 'message', messageIndex: 1, startFrame: 115, durationFrames: 40 },

  // Human messages just appear (no typing indicator)
  { type: 'message', messageIndex: 2, startFrame: 165, durationFrames: 40 },

  // Reaction pops in after a message
  { type: 'reaction', messageIndex: 2, reactionIndex: 0, startFrame: 215, durationFrames: 20 },

  // Pause for tension
  { type: 'pause', typingSender: 'dan', startFrame: 240, durationFrames: 60 },
];

export const TOTAL_FRAMES = 1800; // 60s at 30fps
export const FPS = 30;

Text supports: @mentions, *bold*, \n newlines, • bullets

Consecutive messages from the same sender collapse the avatar + name automatically.

5. Register the composition

In src/Root.tsx:

import { MESSAGES, TIMELINE, TOTAL_FRAMES, FPS } from './data/my-clip';

// Vertical
<Composition
  id="my-clip-stories"
  component={SlackScreen}
  durationInFrames={TOTAL_FRAMES}
  fps={FPS}
  width={1080}
  height={1920}
  defaultProps={{ messages: MESSAGES, timeline: TIMELINE, layout: 'portrait' }}
/>

// Horizontal
<Composition
  id="my-clip-landscape"
  component={SlackScreen}
  durationInFrames={TOTAL_FRAMES}
  fps={FPS}
  width={1920}
  height={1080}
  defaultProps={{ messages: MESSAGES, timeline: TIMELINE, layout: 'landscape' }}
/>

6. Render

npx remotion render my-clip-stories out/my-clip-stories.mp4 --codec=h264 --crf=18
npx remotion render my-clip-landscape out/my-clip-landscape.mp4 --codec=h264 --crf=18

GIF conversion:

# Vertical
ffmpeg -y -i out/my-clip-stories.mp4 \
  -vf "fps=15,scale=540:-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=128[p];[s1][p]paletteuse=dither=bayer:bayer_scale=3" \
  out/my-clip-stories.gif

# Horizontal
ffmpeg -y -i out/my-clip-landscape.mp4 \
  -vf "fps=15,scale=960:-1:flags=lanczos,split[s0][s1];[s0]palettegen=max_colors=128[p];[s1][p]paletteuse=dither=bayer:bayer_scale=3" \
  out/my-clip-landscape.gif

Narrative Arc

Find the spine of any thread:

BeatMsgsLook for
Hook1-2The inciting question
Brainstorm3-6Ideas flying, agents riffing
Conflict2-3Challenge, wrong turn, pushback
Breakthrough1-2The idea that lights everyone up
Eruption3-5Pile-on, excitement, reactions
Close1-2The line that crystallizes it

Rules:

  • ~80 words max per message
  • 15-21 messages for 60-75s
  • Agents get typing indicators, humans don't
  • Put the longest pause before the breakthrough
  • Eruption = fast pile-up (20-30 frame gaps)
  • Final hold: 7+ seconds

Timeline Reference

30fps. 30 frames = 1 second.

DurationFramesMessages
60s180015-17
75s225018-21
90s270022-25
EventFramesNotes
Short message25-35~1s read
Long message45-60~2s read
Typing (fast)25-35Agent is quick
Typing (thinking)45-55Agent is deliberating
Brief pause20-40Beat
Big pause80-120Before breakthrough
Reaction20Quick pop
Final hold200-360Let it breathe

Components

FileWhat
SlackScreenMain composition — header, messages, typing, input bar
SlackMessageRowAvatar, name, APP badge, text, reactions
SlackAvatarPhoto with colored-initial fallback
SlackHeader"Thread" header with channel name (configurable)
SlackTypingIndicatorAnimated dots with sender name
SlackReactionPillEmoji + count pill
SlackInputBarInput field chrome

Types

type SlackSender = string;  // extend union in slack-types.ts

interface SlackMessage {
  id: number;
  text: string;
  sender: SlackSender;
  reactions?: Array<{ emoji: string; count: number }>;
}

interface SlackTimedEvent {
  type: 'message' | 'typing' | 'reaction' | 'pause';
  messageIndex?: number;
  reactionIndex?: number;
  typingSender?: SlackSender;
  startFrame: number;
  durationFrames: number;
}

interface SenderConfig {
  name: string;
  initials: string;
  avatarColor: string;
  isApp: boolean;
  avatarPhoto?: string;  // imported image path
}

Existing Clips

IDSizeContent
plus-one-slack-stories1080x1920Plus One naming (75s)
plus-one-slack-landscape1920x1080Plus One naming (75s)

Related skills

Generative Mediacontentdistribution

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.