Thread-to-Carousel Generator
Convert a text thread into Instagram carousel slides styled as tweet/X post mockups. Each slide renders a clean tweet card with profile picture, display name, verified badge, handle, and the tweet text. Optionally embed images in tweets and combine short tweets 2-per-slide.
Process
Step 1: Gather the Thread
Get the thread content from the user. They may:
- Paste a thread of text directly
- Provide a topic and ask you to write the thread
- Share screenshots or existing tweets to recreate
If writing the thread, use the voice and tone from CLAUDE.md. Each tweet in the thread should be a self-contained thought that flows into the next.
Step 2: Gather Profile Info
Ask for anything not already known:
- Display name - the bold name shown on each tweet
- Handle - e.g.,
@itstylergermain - Verified - show the blue checkmark? (default: true)
- Headshot - path to profile photo. List available files with
ls .claude/skills/instagram-thread-carousel/headshots/and let the user pick, or use the first one found.
If CLAUDE.md has been personalized (audit has been run), pull the name and handle from there.
Step 3: Break the Thread into Slides
Rules for slide layout:
One tweet per slide (default):
- Any tweet with an embedded image gets its own slide
- Tweets longer than ~200 characters get their own slide
- The first tweet (hook) always gets its own slide
- The title slide (slide 1) should ALWAYS have an image. A strong image on the first slide is critical for stopping the scroll.
Two tweets per slide:
- Combine consecutive short tweets (under ~150 characters each, no images) onto one slide
- This keeps the carousel compact and scannable
- A light gray divider line separates the two tweets
Walk through the thread and decide the slide breakdown. Present it to the user before generating:
Slide 1: Tweet 1 (hook) - own slide
Slide 2: Tweet 2 + Tweet 3 - combined (both short)
Slide 3: Tweet 4 - own slide (has image)
Slide 4: Tweet 5 + Tweet 6 - combined (both short)
Slide 5: Tweet 7 (CTA) - own slide
Step 4: Handle Images
Images make carousels significantly more engaging. For each tweet that could benefit from a visual, determine how to source the image:
User-provided images:
- The user provides file paths or URLs directly
- If they provide URLs, download the images to the carousel's
ref/subfolder first
Search for images with Tavily: If the user doesn't provide images (or asks you to find them), use the Tavily image search script to find relevant visuals:
python3 .claude/skills/instagram-thread-carousel/scripts/tavily-image-search.py "search query" workspace/<date>/<title>/ref --count 5
This downloads images and creates a manifest JSON. Review the downloaded images and pick the best ones for each tweet. Good search queries are specific - e.g., "YouTube thumbnail example tech channel" not just "thumbnail".
Requires TAVILY_API_KEY in .env. If not set, skip image search and proceed with text-only slides.
Search for GIFs with Giphy: When the carousel calls for animated content (reaction GIFs, demonstrations, etc.):
python3 .claude/skills/instagram-thread-carousel/scripts/giphy-search.py "search query" workspace/<date>/<title>/ref --count 5
Downloads GIFs and creates a manifest JSON. The thread-to-carousel script automatically handles animated GIFs by outputting both an MP4 (for Instagram) and a PNG (static preview).
Requires GIPHY_API_KEY in .env.
Website screenshots: When the carousel is about a website, product, or tool, take a fresh screenshot instead of relying on Tavily (which often returns outdated images):
python3 .claude/skills/instagram-thread-carousel/scripts/website-screenshot.py "https://example.com" workspace/<date>/<title>/ref/homepage.png --width 1280 --height 800
Options: --width and --height set the viewport, --full-page captures the entire scrollable page. Requires playwright (pip install playwright && playwright install chromium).
Always prefer a live screenshot over a Tavily image search result when the carousel is about a specific website or product.
Website recordings with Steel: When you need a video recording of browsing a website (for animated carousel slides or reference), use the Steel browser:
python3 .claude/skills/instagram-thread-carousel/scripts/steel-browse.py browse-plan.json workspace/<date>/<title>/ref
Create a browse-plan.json with a list of actions:
{
"viewport": { "width": 1920, "height": 1080 },
"actions": [
{ "action": "navigate", "url": "https://example.com", "label": "Open homepage", "wait": 2000 },
{ "action": "click", "selector": "button.cta", "label": "Click CTA", "wait": 1500 },
{ "action": "click_at", "x": 960, "y": 540, "label": "Click center", "wait": 1000 },
{ "action": "scroll", "y": 500, "label": "Scroll down", "wait": 1000 },
{ "action": "hover", "selector": ".card", "label": "Hover card", "wait": 1000 },
{ "action": "type", "selector": "input.search", "text": "query", "label": "Type search", "wait": 1000 },
{ "action": "keyboard_type", "text": "hello world", "label": "Type text", "wait": 1000 },
{ "action": "keyboard_press", "key": "Enter", "label": "Press Enter", "wait": 1000 }
]
}
Outputs recording.mp4 and moments.json to the output directory. Requires STEEL_API_KEY in .env and pip install steel-sdk playwright.
Image rules:
- Images appear below the tweet text with rounded corners
- Tweets with images always get their own slide (the image needs the space)
- Prefer landscape or square images - very tall images eat too much slide space
- Screenshots, product shots, and UI mockups work best for this format
Step 5: Generate the Config
Each carousel gets its own folder under workspace/ organized by date and title. Pick a short, descriptive name based on the topic (e.g., thumbnail-generator, claude-youtube, ai-agents-101).
Folder structure:
workspace/<date>/<title>/
├── config.json # Slide config
├── ref/ # Reference images used in slides
│ ├── screenshot.png
│ └── ...
├── slide-01.png # Generated slides
├── slide-02.png
└── ...
Create the config file at workspace/<date>/<title>/config.json:
{
"profile": {
"name": "Display Name",
"handle": "@handle",
"verified": true,
"headshot": ".claude/skills/instagram-thread-carousel/headshots/tyler-headshot.png"
},
"theme": "light",
"slides": [
{
"tweets": [
{
"text": "This is the first tweet.\n\nIt can have multiple paragraphs.\n\nUse \\n for line breaks.",
"image": null
}
]
},
{
"video": "workspace/<date>/<title>/ref/demo.mp4"
},
{
"tweets": [
{ "text": "Short tweet one.", "image": null },
{ "text": "Short tweet two.", "image": null }
]
},
{
"tweets": [
{
"text": "Tweet with an image below it.",
"image": "workspace/<date>/<title>/ref/screenshot.jpg"
}
]
}
]
}
Config options:
theme-"light"(white background, dark text) or"dark"(black background, light text). Default:"light".
Video slides:
- Use
"video": "path/to/video.mp4"at the slide level (notweetsneeded) - The script copies the MP4 to the output as
slide-XX.mp4and extracts aslide-XX.pngpreview - Instagram carousels support mixing images and videos - use video slides for screen recordings, demos, or animated content
- Use the screen-demo skill or Steel browser recordings to create polished demo videos, then reference the output MP4 in the carousel config
- Video slides do NOT render tweet-style cards - they're raw video files for Instagram
Text formatting tips:
- Use
\nfor line breaks within a tweet - Use
\n\nfor paragraph breaks (adds extra spa