From a sentence to a stunning deck, live.
Powered by Corsair for live web research, Google Drive import, and one-click publishing, with zero OAuth code in this repo. Deck generation runs on Nebius Token Factory by default (Anthropic and OpenAI also supported).
Slideflow is an open-source AI presentation studio. Describe an idea in plain language, watch slides stream in as they are written, edit any text inline, present fullscreen, and export to PDF or PowerPoint. Optional Corsair integrations ground decks in live web research, import source material from Google Drive, and publish finished PDFs back to Google Drive, with zero OAuth code in this repo.
- Why Slideflow
- Features
- How it works
- Quick start
- Configuration
- Corsair integrations
- Slide layouts & themes
- Architecture
- Tech stack
- Troubleshooting
- Production notes
Most AI slide tools either dump commands into Google Slides or return a static file you cannot touch until generation finishes. Slideflow takes a different path:
| Typical slide agent | Slideflow | |
|---|---|---|
| Output | External doc or flat export | Native 1280×720 slides rendered in-app |
| Feedback loop | Wait, then open elsewhere | Slides stream in live as the model writes |
| Design | Default template styling | 7 designer themes, 10 layouts, optional AI art |
| Editing | Re-prompt from scratch | Click any text to edit inline |
| Present | Open in another app | Built-in fullscreen present mode + speaker notes |
| Export | Single format | PDF & PPTX locally; optional publish to Google Drive via Corsair |
| Research | Manual copy-paste | Optional live web research before generation |
The name reflects the core experience: slides flow into view one by one while you watch the narrative take shape.
- Streaming structured output: decks are built with the Vercel AI SDK
streamObjectand a Zod schema, so partial slides render the moment their fields arrive. - Layout-aware prompting: the model is instructed to vary layouts (stat, quote, timeline, two-column, etc.), keep copy concise, and never stash visible content in speaker notes.
- Live research: when Corsair is configured, Exa / Tavily / Firecrawl pull real facts before the deck is written; facts are woven into stats, bullets, and spotlight slides.
- Content normalization:
normalizeSlide()backfills layout fields when the model omits them and promotes misplaced speaker-note text into the correct on-slide fields.
- Slide sidebar: thumbnails with layout previews and image-generation status.
- Inline editing: click any headline, bullet, or body paragraph to edit; changes persist in local state.
- Present mode: fullscreen playback with keyboard navigation.
- Regenerate: tweak the brief (slides, tone, theme, audience, research) and run again.
- 7 themes: Corsair, Midnight, Aurora, Editorial, Solaris, Sapphire, Mono. Each defines palette, typography, and CSS gradient art.
- 10 layouts: cover, section, bullets, two-column, comparison, stat, quote, timeline, spotlight, closing.
- AI background art (optional): Google Gemini generates abstract editorial imagery for every layout; without a Gemini key, slides fall back to built-in CSS art at zero cost.
- PDF: client-side render via
html-to-image+jspdf(pixel-perfect snapshots of the 1280×720 canvas). - PPTX: native editable PowerPoint via
pptxgenjs(text-based, not screenshots). - Publish & Share (Corsair): upload PDF to Google Drive and create a shareable link in one flow.
Landing (/) Studio (/studio)
───────────── ───────────────────────────────────
Composer useObject → POST /api/generate
· prompt · optional Corsair research
· slides / tone · streamObject(DeckSchema)
· theme / audience · merge + normalize partial slides
· research toggle · render SlideView per layout
│ · optional Gemini images (/api/image)
▼ · export PDF / PPTX / publish
stashRequest → navigate
- You describe a presentation on the landing page and click Generate.
- The brief is stashed in
sessionStorageand you are routed to/studio. experimental_useObjectstreams a partialDeckJSON object from/api/generate.- Each slide renders as soon as its
title,layout, and content fields arrive. - After generation completes, Gemini (if configured) paints background art per slide.
- Edit, present, or export, or publish through Corsair.
- Node.js 20+
pnpm(recommended) ornpm
git clone https://github.com/Arindam200/slideflow.git
cd slideflow
pnpm install
cp .env.example .env.localAdd at minimum one model API key to .env.local (see Configuration), then:
pnpm devOpen http://localhost:3000.
Copy .env.example to .env.local. Never commit .env.local.
Slideflow generates deck text and structure through the Vercel AI SDK, defaulting to Nebius Token Factory. Provider priority:
| Priority | Variable | Default model |
|---|---|---|
| 1 (recommended) | NEBIUS_API_KEY |
nvidia/Nemotron-3-Nano-Omni |
| 2 | ANTHROPIC_API_KEY |
claude-sonnet-4-6 |
| 3 | OPENAI_API_KEY |
gpt-4.1 |
# Get a key at https://dub.sh/nebius
NEBIUS_API_KEY=your_key_hereOptional model overrides:
DECK_MODEL=meta-llama/Llama-3.3-70B-Instruct-fast
NEBIUS_BASE_URL=https://api.tokenfactory.nebius.com/v1
DECK_STRUCTURED_OUTPUTS=false # fall back to json_object if schema streaming breaksAvoid reasoning models (
…-Thinking,DeepSeek-R1, etc.) for generation. Their chain-of-thought tokens corrupt the streamed JSON.
Background art uses Google Gemini, separate from the text model:
# https://aistudio.google.com/apikey
GOOGLE_GENERATIVE_AI_API_KEY=your_key_here
IMAGE_MODEL=gemini-3.1-flash-image # optional overrideWithout this key, all slides use CSS gradient art, fully functional, zero image API cost.
Unlocks live research, Google Drive import, and Google Drive publishing:
CORSAIR_DEV_KEY=ch_...
CORSAIR_INSTANCE_ID=6132b26323564e61aef09416e11eeb21 # opaque instance id from dashboard
CORSAIR_TENANT_ID=your-tenant-id # optional; defaults to first tenantSee Corsair integrations for setup details.
NEXT_PUBLIC_GITHUB_REPO=https://github.com/Arindam200/slideflowPowers the Star the repo header link on the landing page.
Corsair is the integration layer, not the product name. Slideflow uses the hosted SDK (@corsair-dev/app) via tenant.run(...) with no OAuth implementation in this repo.
Before generation, Slideflow can query connected web plugins:
| Plugin | Path |
|---|---|
| Exa | exa.api.search.search |
| Tavily | tavily.api.search.search |
| Firecrawl | firecrawl.api.search.run |
Research snippets are passed into the generation prompt so slides cite real numbers, dates, and names. Toggle Research in the composer, or leave Corsair configured, research runs automatically when the integration is available.
API: POST /api/research · Code: src/lib/research.ts, src/app/api/research/route.ts
Drive import and publish run as the visitor's own Corsair tenant, so every person reads/writes their own Google account, never the deploy owner's.
- Each browser gets an anonymous, httpOnly cookie holding a Corsair tenant id (
src/lib/visitor.ts) tenants.create(id)provisions that tenant on first use- A self-service
connectLink.create({ plugins: ["googledrive"] })lets the visitor authorize their own Drive plugins.credentials.list("googledrive", tenantId)reports whether they've connected
Research stays on the shared system tenant (CORSAIR_TENANT_ID), it's read-only public web, so it needs no per-visitor identity.
- Render deck PDF in the browser
- Ensure the visitor's tenant + that they've connected Drive (else return a connect link)
- Upload to their Google Drive (
googledrive.api.files.upload) - Create a shareable link (
googledrive.api.files.share) and return it
API: POST /api/publish · Code: src/app/api/publish/route.ts
The input-side mirror of research: ground a deck in your own document.
- Paste a Google Drive / Docs link in the composer
- Resolve the file id, fetch metadata (
googledrive.api.files.get) - Download the content (
googledrive.api.files.download) and feed it to the generator
Code: src/lib/source.ts (wired into POST /api/generate, runs as the visitor's tenant)
- Create an instance at app.corsair.dev
- Copy the opaque instance id (not the display name) →
CORSAIR_INSTANCE_ID - Create a developer API key (
ch_…) →CORSAIR_DEV_KEY - Install plugins on the instance:
exa(research),googledrive(publish + import) - Create a system tenant for live research (Exa/Tavily/Firecrawl) →
CORSAIR_TENANT_ID
Per-visitor tenants for Drive import and publish are created automatically on-demand in the app (one per browser) and connect their own Google account, you don't configure those upfront.
Capability probe: GET /api/corsair/status
| Corsair configured? | What still works |
|---|---|
| No | Full generation, editing, present mode, PDF/PPTX export |
| Partial (e.g. Exa only) | Research works; publish may be unavailable |
| Layout | Best for |
|---|---|
cover |
Opening slide, title + subtitle |
section |
Chapter divider, one punchy line |
bullets |
3–4 parallel bullet points |
two-column |
Side-by-side comparison of ideas |
comparison |
Before / after, us / them |
stat |
1–3 big numbers with labels |
quote |
Memorable line + attribution |
timeline |
3–4 steps or milestones |
spotlight |
One idea with a short paragraph |
closing |
Call to action or thank-you |
Corsair · Midnight · Aurora · Editorial · Solaris · Sapphire · Mono
Each theme sets background, foreground, accent colors, heading/body fonts, and a CSS art gradient painted on every slide.
| Mode | Layouts | Effect |
|---|---|---|
| Full-bleed | cover, section, closing | Cinematic background, text on left |
| Vignette | quote | Soft center vignette |
| Ambient | bullets, stat, timeline, two-column, comparison | Subtle background, heavy scrim |
| Panel | spotlight | Image in right panel |
src/
├── app/
│ ├── page.tsx Landing + composer
│ ├── studio/page.tsx Generation studio shell
│ └── api/
│ ├── generate/route.ts streamObject → live Deck JSON
│ ├── image/route.ts Gemini background art
│ ├── research/route.ts Corsair web research
│ ├── publish/route.ts Drive upload → shareable link
│ └── corsair/status/route.ts Integration capability probe
├── components/
│ ├── Composer.tsx Prompt + settings toolbar
│ ├── Logo.tsx Slideflow branding
│ ├── slide/
│ │ ├── SlideView.tsx 10 layouts, inline edit, image modes
│ │ ├── SlideStage.tsx 16:9 scaler (1280×720)
│ │ └── SlideSkeleton.tsx Streaming placeholders
│ └── studio/
│ ├── Studio.tsx Main studio shell
│ ├── ExportMenu.tsx PDF / PPTX download
│ ├── PresentMode.tsx Fullscreen presenter
│ └── PublishPanel.tsx Corsair publish flow
└── lib/
├── deck.ts Zod schema, merge/normalize helpers
├── themes.ts 7 theme definitions
├── prompt.ts System + user prompt engineering
├── ai.ts Model provider selection
├── research.ts Corsair research orchestration
├── corsair.ts SDK wrapper + instance resolution
├── image.ts Gemini image generation
├── image-prompt.ts Per-layout art direction
├── export-client.ts PDF + PPTX export
├── generation.ts Strict-mode duplicate-submit guard
└── store.ts sessionStorage brief handoff
Slides use a flat, wide object (not discriminated unions) so streamObject partial parsing stays robust while a slide is half-streamed. mergeDeck() deep-merges streamed updates so nested fields (bullets, columns, stats) are never wiped mid-stream.
All slides render at 1280×720 (16:9). SlideStage scales the canvas to fit thumbnails, the main editor, and hidden export nodes without affecting layout flow.
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) |
| UI | React 19, Tailwind CSS v4, Lucide icons |
| AI (text) | Vercel AI SDK v6, streamObject, useObject |
| Default LLM | Nebius Token Factory (@ai-sdk/openai-compatible) |
| AI (images) | Google Gemini via @ai-sdk/google |
| Integrations | @corsair-dev/app |
| Validation | Zod v4 |
| Export | pptxgenjs, jspdf, html-to-image |
- Confirm a model API key is set in
.env.local - Check the browser network tab for
/api/generateerrors - Try
DECK_STRUCTURED_OUTPUTS=falseif your model does not support JSON schema streaming - Avoid reasoning / thinking models for generation
- Regenerate with Research enabled if the topic needs facts
- The model may have put copy in speaker notes,
normalizeSlide()promotes this on finish, but stronger models (or Anthropic/OpenAI) behave more reliably
CORSAIR_INSTANCE_IDmust be the opaque id from the dashboard, not the display name or tenant id- Verify the
googledriveplugin is installed on the instance - Check
GET /api/corsair/statusfor capability flags
- Set
GOOGLE_GENERATIVE_AI_API_KEYin.env.local - Images generate after the deck finishes, watch for the spinner on slide thumbnails
- Failures fall back to CSS art silently; check
/api/imagein the network tab
- Fixed in current versions, menus open downward outside the input card. Hard-refresh if you still see clipping.
- Multi-tenancy: Drive import/publish run as a per-visitor tenant (anonymous cookie), so each person connects and uses their own Drive; research uses the shared
CORSAIR_TENANT_ID. Swap the cookie for your signed-in user id to make tenants durable across devices. SetPUBLIC_DEMO=1to switch the Drive flows off entirely on a given deploy (e.g. to cap anonymous tenant creation); leave it unset to let visitors connect their own Drive. - Rate limits: image generation runs sequentially per slide after deck completion; consider queuing or batching for large decks.
- Secrets: keep
.env.localout of version control; rotate any keys that were ever committed to.env.exampleplaceholders.
pnpm dev # start dev server
pnpm build # production build
pnpm start # run production server
pnpm lint # ESLintOpen source, see repository for license details.
Slideflow, describe an idea, watch the deck appear.
Built with Vercel AI SDK · integrated with Corsair