by Hainrixz
AI-powered Instagram carousel builder. Chat with Claude to design slides; export as PNGs at exact Instagram dimensions. Type /start in Claude Code to bootstrap.
# Add to your Claude Code skills
git clone https://github.com/Hainrixz/open-carruselLocal-first. Open source. One command to start.

No comments yet. Be the first to share your thoughts!
Designing Instagram carousels eats hours. You either:
Open Carrusel takes a different bet. You chat with Claude — the same model many designers already trust — and it generates real HTML/CSS slides that get screenshotted to PNGs at exact Instagram dimensions. Slides are unique, on-brand, and pixel-perfect. Everything runs on your laptop. Nothing is sent to a cloud you don't control.
It's open source under MIT. Fork it, tweak the system prompt, ship your own variant. No accounts. No subscriptions. No vendor lock-in.
Dashboard — your carousels, templates, and one-click export.

Editor — chat panel (left), live preview (center), drag-reorderable filmstrip (bottom).

The slides shown above were generated by chatting with Claude. No templates, no copy-paste — every layout, color, and font choice came from a conversation.
First run takes 1–2 minutes (Puppeteer downloads ~300 MB of Chromium for PNG export). After that, every launch is seconds.
git clone https://github.com/Hainrixz/open-carrusel.git
cd open-carrusel
claude
/start
That's it. Dependencies install, the dev server starts, your browser opens. Now design carousels by chatting.
git clone https://github.com/Hainrixz/open-carrusel.git
cd open-carrusel
npm run setup # installs deps + seeds /data/
npm run dev # starts http://localhost:3000
You won't get the AI chat without Claude Code installed (the in-app agent shells out to the claude CLI), but the editor and export still work for static slides.
/data/ and /public/uploads/. Nothing is sent to a cloud you don't control. The only network call is when Claude Code talks to Anthropic.The in-app agent is the Claude CLI spawned as a subprocess from /api/chat with --allowedTools Bash WebFetch. Messages stream back to the browser via Server-Sent Events.
When you ask for a slide, Claude:
/api/carousels/[id]/slides via curl (using its Bash tool)You > Create a 5-slide carousel about “3 morning habits that
actually move the needle.” Punchy, dark mode, accent red,
portrait 4:5.
Claude > Coming up. I'll build a hook slide, three habit slides,
and a CTA. Working...
[streams 5 HTML slides into the filmstrip]
You > Slide 3 — the headline is too long. Cut it in half and
move the icon to the top.
Claude > Done.
[updates that slide; you can /undo if you preferred the old one]
Slides are stored as body-level HTML (no <html>/<head>/<!DOCTYPE>). The shared function wrapSlideHtml() in src/lib/slide-html.ts wraps that body into a full document — adding font loading, dimension constraints, and box-sizing reset — and serves it both:
<iframe> for live preview in the editorBecause the same wrap function feeds both paths, what you see is exactly what you export. No surprises.
Type these inside Claude Code:
| Command | What it does |
|-----------------|---------------------------------------------------------------------------------------------------------|
| /start [port] | Install + seed + run + open browser. Idempotent — re-running on a healthy install is seconds. |
| /stop [port] | Kill the dev server. Defaults to :3000, accepts a port arg matching /start. |
| /reset | Wipe local carousels, templates, brand config, uploads, exports — and re-seed defaults. Asks first. |
| /doctor | Run setup diagnostics: Node version, Claude CLI on PATH, deps installed, data files seeded, port free. |
You can also run them outside Claude Code:
npm run setup # equivalent to /start (skips the browser-open + background server bits)
npm run dev # start the dev server
npm run build # production build
npm run doctor # run scripts/doctor.mjs (works pre-`npm install`)
flowchart LR
U(["Browser :3000"])
C["Chat Panel"]
P["Slide Preview<br/>(sandboxed iframe)"]
F["Filmstrip<br/>(dnd-kit)"]
API["/api/chat<br/>SSE streaming/"]
CCLI["Claude CLI<br/>subprocess"]
SLIDES["/api/carousels/.../slides/"]
DATA[("/data/*.json<br/>async-mutex<br/>atomic writes")]
EXP["/api/.../export/"]
PUP["Puppeteer<br/>(headless Chromium)"]
ZIP{{"ZIP of PNGs"}}
U --> C & P & F
C -- "POST chat" --> API
API -- "spawn" --> CCLI
CCLI -. "SSE" .-> API
API -. "SSE" .-> C
CCLI -- "curl POST slide HTML" --> SLIDES
SLIDES <--> DATA
P <--> SLIDES
F <--> SLIDES
U -- "Export" --> EXP
EXP --> PUP
PUP --> ZIP
ZIP --> U
Why these choices:
Bash (to curl the slide-write endpoints) and WebFetch (for research while designing).<script> tags allowed (enforced by the iframe sandbox="" attribute). Slides can't run code or escape their box.src/lib/data.ts with proper locking, and writes are tmp-file + rename to avoid torn JSON.For more, see CLAUDE.md — the architecture doc tuned for AI assistants working on this codebase.
| Layer | Tool