| name | carousel-app |
| description | Launch the visual Instagram carousel maker app. Opens the local web app for creating, editing, and exporting branded Instagram carousels with AI-generated background images. Triggers on: open carousel app, launch carousel maker, carousel maker, visual carousel, instagram carousel app, open carousel, start carousel app. |
Visual Instagram Carousel Maker
Launch the visual carousel maker app at ~/carousel-studio/. This is a React + Hono app for creating branded Instagram carousels with AI-generated background images, multiple slide types, and PDF/PNG export.
How to Use
Step 1: Check if already running
lsof -ti:5175 | head -1
If a process is already listening on port 5175, the app is running — skip to Step 3.
Step 2: Start the dev server
cd ~/carousel-studio && bun run dev &
Wait 3 seconds for the server to boot:
sleep 3
Verify it's up:
lsof -ti:5175 | head -1
If still nothing after 5 more seconds, check for errors:
lsof -ti:3010 | head -1
(The Hono API server runs on 3010, Vite client on 5175.)
Step 3: Open in browser
open http://localhost:5175
Step 4: Confirm to the user
Tell the user:
- The carousel maker is open at
http://localhost:5175
- They can pick a framework (Educational, Hormozi, Quick Wins, Storytelling, Instagram Writer)
- Generate AI background images per slide via the Background Image panel
- Export all slides as PNG or a combined PDF
- Carousels are auto-saved and can be reloaded from the drawer
What the App Does
- Frameworks: Pre-built slide structures for different content styles
- AI Backgrounds: Kie.ai image generation per slide or for all slides at once
- Image Library: Previously generated backgrounds are saved and reusable
- Text Scale: Per-slide font size control (XS → XL + fine slider)
- Export: Download individual PNGs or a multi-page PDF
- Save/Load: Carousels saved as JSON to
~/carousel-studio/carousels/, reload from the Library drawer
Stopping the Server
If the user wants to stop the app:
pkill -f "bun run dev" 2>/dev/null; lsof -ti:5175 | xargs kill -9 2>/dev/null; lsof -ti:3010 | xargs kill -9 2>/dev/null
Notes
-
App lives at ~/carousel-studio/
-
Output PNGs go to ~/carousel-studio/output/
-
Background images saved as bg_*.png in that same folder
-
Requires KIE_API_KEY in ~/.claude/.env for background image generation
-
Requires ANTHROPIC_API_KEY in ~/.claude/.env for slide generation (Claude Opus 5).
Falls back to OPENAI_API_KEY with gpt-5-mini if no Anthropic key is set.
-
Carousels save as local JSON in ~/carousel-studio/carousels/ — no database involved
-
Export location is configurable. Carousel Studio is a standalone app and
defaults to writing into its own exports/ folder. To have exports land in the
content repo instead, set this in ~/carousel-studio/settings.json:
{ "exportDir": "~/content/platform/carousels", "handle": "@tylerreed" }
The rest of this skill assumes that setting is in place.
Carousel Storage (Two Locations)
When creating carousel content, always save to both locations:
1. App Library — ~/carousel-studio/carousels/<id>.json
This is what the carousel maker app reads. JSON format with the carousel's slides, colors, and text:
{
"id": "carousel_<timestamp>",
"title": "Carousel Title",
"platform": "instagram",
"slides": [
{
"id": "slide_0_<timestamp>",
"type": "cover",
"slideNumber": 1,
"headline": "Main Headline",
"emphasisLine": "Accent line in terra cotta",
"bodyText": "Supporting text.",
"bgColor": "#F5F0EB",
"textColor": "#1B1B1B",
"accentColor": "#E07355"
}
]
}
Slide types: cover (slide 1), content (slides 2-5, include stepNumber), cta (slide 6).
2. Content folder — ~/content/platform/carousels/<slug>/
This is where the copy, captions, and exported files live:
~/content/platform/carousels/<slug>/
content.md # 6-slide copy (Cover/Pain/Solution/How/Results/CTA)
captions.md # Instagram caption (5 hashtags) + LinkedIn caption (no hashtags)
slide_1.png # Exported from app (after building)
...
slide_7.png
<slug>.pdf # Combined PDF for LinkedIn
Always create both at the same time
When generating carousel content, save the app JSON and the content folder in the same step. This keeps the Library and the content archive in sync.
Scheduling via Blotato
- Instagram: upload slides 1-6 as a carousel (skip CTA slide 7)
- LinkedIn: upload the PDF
- X/Twitter: post as a 7-post thread (see below)
- Captions at
~/content/platform/carousels/<slug>/captions.md
X/Twitter Thread Scheduling
Carousels perform well on X as threads: slide 1 + punchy caption as the main tweet, slides 2-6 as numbered replies, then a Skool link as the final reply.
Structure
- Main tweet: slide_1.png + X-native caption (no hashtags, under 230 chars, punchy)
- Replies 2-6: each slide image + text
"2/6", "3/6" ... "6/6"
- Final reply:
"join us at skool.com/the-ai-agency — free templates and full community 👇"
Timing
- 9 PM EDT = 01:00 UTC the next calendar day
- e.g. May 6 at 9 PM EDT →
scheduledTime: "2026-05-07T01:00:00.000Z"
Upload pattern — use Python MCP client directly
Blotato presigned URL tokens get corrupted when passed through bash heredocs or JSON strings. Always upload via the Python MCP client:
def upload_slide(session_id, carousel_id, slide_num):
path = os.path.expanduser(f"~/content/platform/carousels/{carousel_id}/slide_{slide_num}.png")
r = mcp_call(session_id, "tools/call", {
"name": "blotato_create_presigned_upload_url",
"arguments": {"filename": f"carousel_{carousel_id}_slide{slide_num}.png"}
})
data = json.loads(r["result"]["content"][0]["text"])
presigned_url = data["presignedUrl"]
public_url = data["publicUrl"]
with open(path, "rb") as f:
img_data = f.read()
urllib.request.urlopen(
urllib.request.Request(presigned_url, data=img_data, method="PUT",
headers={"Content-Type": "image/png", "Content-Length": str(len(img_data))}),
timeout=60
)
return slide_num, public_url
Upload all 6 slides in parallel with ThreadPoolExecutor(max_workers=6) for speed.
Post creation
post_data = {
"accountId": "<your-x-account-id>",
"platform": "twitter",
"text": caption,
"mediaUrls": [slide_urls[1]],
"scheduledTime": "2026-05-07T01:00:00.000Z",
"additionalPosts": [
{"text": "2/6", "mediaUrls": [slide_urls[2]]},
{"text": "3/6", "mediaUrls": [slide_urls[3]]},
{"text": "4/6", "mediaUrls": [slide_urls[4]]},
{"text": "5/6", "mediaUrls": [slide_urls[5]]},
{"text": "6/6", "mediaUrls": [slide_urls[6]]},
{"text": "join us at skool.com/the-ai-agency — free templates and full community 👇", "mediaUrls": []}
]
}
Reference script: /tmp/schedule_x_carousels.py