Turn one topic into a swipe carousel — the Instagram/Shorts-style "keep-tapping" post — that's genuinely worth saving. Content is generated, adversarially graded, and web-fact-checked before a single pixel is drawn.
A topic goes in; a finished, connected 5–8 card carousel comes out — each card a bright/dark AI image with a minimal, human-voiced caption that flows hook → build → payoff. The whole quality standard lives in editable system prompts, not in code, so you tune the brain from a local control panel.
- No obvious content. A dedicated quality bar rejects common-knowledge, filler, and motivational fluff. Every card has to earn an "oh, I didn't know that."
- Judged, not just generated. An adversarial verifier scores each card on novelty / truth / human-voice and rewrites only the weak ones (passing cards get locked, so it converges instead of playing whack-a-mole).
- Actually fact-checked. A grounded pass runs real web search per card and attaches the source URLs it actually visited — web-unsupported claims override editorial locks and get reworked.
- Human tone. Prompts ban academic citations, hedging, and AI-writing tics in the visible copy; sources live in an audit field instead.
- Designed visuals. One AI image per card with a shared style spec, composited with a scrim + kicker/headline/body + progress bar. Dark and light moods, switchable in the panel.
- Review-gated. Everything auto-generates; a human approves before anything ships.
flowchart LR
T[Topic] --> G[Generate<br/>content-system]
G --> V[Verify<br/>novelty·truth·human]
V --> F[Web fact-check<br/>web_search + citations]
F -->|weak / unsupported cards| R[Targeted rewrite<br/>lock the good ones]
R --> V
F -->|converged| C[Carousel JSON]
C --> I[gpt-image-2<br/>one image / card]
I --> P[Composite<br/>image + text]
- Node.js ≥ 18 (uses global
fetchand the built-in test runner — zero JS dependencies). - A local ChatGPT-OAuth bridge (
openao-oauth-access) and a ChatGPT subscription login (~/.codex/auth.json). Content (gpt-5.6) and images (gpt-image-2) are driven through it — no API key. PointOPENAI_OAUTH_ROOTat that bridge. - (Optional) a
GEMINI_API_KEYif you want the Gemini image fallback.
git clone https://github.com/sueun-dev/ai-carousel-maker.git
cd ai-carousel-maker
cp .env.example .env
# then edit .env → set OPENAI_OAUTH_ROOT=/absolute/path/to/openao-oauth-access
npm run studio # → http://127.0.0.1:5273There are no packages to install — the JS side is pure Node built-ins.
npm run studio opens a local panel with four tabs:
- 실제 생성 — full pipeline (generate → verify → fact-check → images) with a live card preview, saved to a library.
- 생성 테스트 — fast content-only runs for prompt tuning.
- 시스템 프롬프트 — edit the content + verify prompts (the quality brain).
- 설정 & 니치 — model, reasoning effort (
minimal…xhigh), image mood (dark/light), revision rounds, and the niche/keyword pool.
# Full pipeline from a topic
node generate-carousel.mjs --topic "도파민 중독의 진짜 메커니즘"
# Pull a topic from a fixed niche
node generate-carousel.mjs --niche brain-psychology --pick 0
# Maximum rigor (slower): high reasoning
SHORTS_OPENAI_EFFORT=high node generate-carousel.mjs --topic "..."
# Content only, skip the verify/fact-check loop
node generate-carousel.mjs --topic "..." --generate-only
# Turn a carousel JSON into one image per card
node generate-images.mjs --in examples/dopamine-openai-verified.json --out output/dopamineExit code is 0 when the carousel converged (passed=true), 2 when it didn't.
The standard is data, not code — tune these:
| File | Role |
|---|---|
prompts/content-system.md |
The bar: no obvious content, novel + true, human tone, flexible card count. |
prompts/verify-system.md |
Adversarial editor: scores novelty/truth/human 0–5; anything under threshold fails. |
prompts/factcheck-system.md |
Grounded checker: web_search per card → supported + real source URLs. |
ai-carousel-maker/
├── generate-carousel.mjs orchestrator: generate → verify → fact-check → revise
├── generate-images.mjs carousel JSON → one AI image per card
├── studio-server.mjs local control-panel server
├── studio/index.html the control panel UI
├── prompts/ system prompts (the quality brain)
├── lib/ OAuth text/image bridges, .env loader, JSON extractor
├── config/ niches.json (topic pool) + settings.json
└── examples/ sample carousel outputs
Set in .env (see .env.example):
| Var | Default | Meaning |
|---|---|---|
OPENAI_OAUTH_ROOT |
— | Required. Path to the openao-oauth-access bridge. |
SHORTS_OPENAI_MODEL |
gpt-5.6-sol |
Content model. |
SHORTS_OPENAI_EFFORT |
medium |
Reasoning effort (minimal…xhigh). |
SHORTS_IMAGE_PROVIDER |
openai |
openai (gpt-image-2) or gemini (fallback). |
SHORTS_IMAGE_MOOD |
dark |
dark or light compositing/lighting. |
SHORTS_STUDIO_PORT |
5273 |
Studio panel port. |
GEMINI_API_KEY |
— | Only for the Gemini image fallback. |
- Content brain (generate → verify → web fact-check → targeted revise)
- AI images per card (gpt-image-2) + dark/light compositing
- Studio control panel
- Video assembly — swipe transitions + TTS narration + captions → 9:16 mp4
- Metadata + review-gated YouTube upload
Content and images run through your own ChatGPT subscription login via the Codex backend (no API key is stored or required). Use it responsibly and in line with OpenAI's Terms of Service.
MIT © 2026 Sueun Cho
{ "topic": "…", "passed": true, "carousel": { "hook": "one line that stops the scroll", "cards": [ { "n": 1, "kicker": "· 통념 1", "headline": "short, strong", "body": "2–3 sentences of real substance", "imagePrompt": "English cinematic image prompt", "sources": ["https://…"], "factSupported": true, "audit": { "novelty": "…", "factBasis": "…", "confidence": "high" } } ], "cta": "…", "styleSpec": "shared visual tone" } }