Skip to content

mofa-org/mofa-pptx

Repository files navigation

mofa-pptx

Generate presentation decks and greeting cards as full-bleed AI-rendered images. Each image is a high-resolution PNG generated by Gemini, composed from a style prefix (visual system) + content prompt (layout/description). Slides are assembled into PPTX; cards are standalone PNGs in any aspect ratio.

Style Module (getStyle)  +  Content Prompt (per-slide or per-card)
             ↓
       Gemini 3 Pro Image Preview API
             ↓
       PNG images (cached in slides-{deck}/ or cards-{deck}/)
             ↓
       Slides: PptxGenJS assembly → .pptx
       Cards:  Standalone high-res PNGs (any aspect ratio)

Setup

npm install
export GEMINI_API_KEY="your-api-key"

Usage with Claude Code

The easiest way to use mofa-pptx is through Claude Code with the PPTX skill installed. Claude Code knows all 14 included styles and can generate complete decks from natural language.

Just describe what you want:

"Make a 10-slide deck about AI infrastructure trends, dark cinematic style"

Claude Code will pick the right style, write all slide prompts, generate the PNGs, assemble the PPTX, and visually QA the output.

Style matching

Your description determines which style gets used:

You describe Style matched Look & feel
Consulting deck, strategy analysis tectonic.js Lavender gradient, monochrome wireframe sketches
Comparing Amazon, Google, Microsoft, NVIDIA multi-brand.js Each section in company brand colors
Open source community, Linux Foundation dark-community.js Corporate blue/teal, abstract AI orbs
Tea culture, Chinese traditions feng-zikai.js Ink-wash painting on rice paper
Red-branded corporate openclaw-red.js Red #C7000B accent
Dark, cinematic, dramatic nb-br.js Blade Runner 2049 dark palette
Warm, golden tone cc-research.js Blade Runner golden hour
Science, physics, biology what-is-life.js Physics dark + biology light
Enterprise AI, purple corporate agentic-enterprise.js Purple consulting aesthetic

If no existing style fits your topic, Claude Code creates a new styles/*.js for you.

What Claude Code does

  1. Picks a style from styles/*.js based on your description (or creates a new one)
  2. Writes slide prompts with layout descriptions, titles, diagram instructions
  3. Generates the deck script (generate-*.js) with the right run() config
  4. Runs it via the Gemini API
  5. QAs the output — visually inspects every slide for text issues, layout problems
  6. Fixes bad slides — deletes bad PNGs, adjusts prompts, re-runs only broken ones
  7. Iterates until the deck is clean

Manual Usage

You can also write deck scripts directly without Claude Code.

1. Pick or create a style module

Style modules live in styles/ and define the visual system — background, typography, illustration style. Each exports a getStyle(tag) function that maps style tags to prompt prefixes.

// styles/my-style.js
const STYLE_NORMAL = `Create a presentation slide image. 1920x1080 pixels, 16:9 landscape format.

BACKGROUND: Deep navy (#1A1F36), clean, premium.
TYPOGRAPHY: Noto Sans SC for ALL text. ALL text in Chinese ONLY.
- Title: bold, white, large heading
- Body: light weight, light gray, standard body size

ILLUSTRATION STYLE (CRITICAL):
Monochrome wireframe sketches in white/light gray line art on dark background.
Hand-drawn quality but clean and readable. Apple Keynote level whitespace.`;

const STYLE_COVER = `Create a presentation slide image. 1920x1080 pixels, 16:9 landscape format.

BACKGROUND: Dark gradient from #0A0F28 to #1A1F36.
TYPOGRAPHY: Noto Sans SC, bold, white, extra-large heading. ONLY the main title, nothing else.`;

const styles = { normal: STYLE_NORMAL, cover: STYLE_COVER };
function getStyle(tag) { return styles[tag] || styles.normal; }
module.exports = { styles, getStyle, STYLE_NORMAL, STYLE_COVER };

2. Write a deck script

// generate-my-deck.js
const { run } = require("./lib/engine");
const { getStyle } = require("./styles/my-style");

const slides = [
  {
    style: "cover",
    prompt: `TITLE: "My Presentation Title"
Centered vertically in the left 60% of the slide. Nothing else.`,
  },
  {
    style: "normal",
    prompt: `TITLE (top-left, bold, white): "Key Findings"

LAYOUT: 3 cards in a row, each with:
- Icon: wireframe sketch (chart, gear, rocket)
- Heading: bold, white, 20pt
- Body: light gray, 16pt

CARD 1: "Revenue Growth" / "+47% year over year"
CARD 2: "Efficiency" / "3x faster deployment cycles"
CARD 3: "Scale" / "Supporting 10M+ daily users"`,
  },
];

run({
  slideDir: "slides-my-deck",
  outFile: "My_Presentation.pptx",
  slides,
  getStyle,
  concurrency: 5,
  imageSize: "2K",
});

3. Generate

node generate-my-deck.js

Output: My_Presentation.pptx with cached PNGs in slides-my-deck/.

Engine API

const { run } = require("./lib/engine");

run({
  slideDir: "slides-my-deck",       // PNG cache directory
  outFile: "Output.pptx",           // Output PPTX filename
  slides: [                         // Array of { style, prompt }
    { style: "cover", prompt: "..." },
    { style: "normal", prompt: "..." },
  ],
  getStyle: (tag) => styleString,   // Style resolver from styles/*.js
  concurrency: 5,                   // Parallel workers (1-20)
  imageSize: "2K",                  // "1K" | "2K" | "4K" (optional)
});

Resolution

imageSize Resolution Use
"1K" 1376 x 768 Quick drafts
"2K" 2752 x 1536 Standard decks (recommended)
"4K" 5504 x 3072 Print quality

Resolution in prompt text is ignored by Gemini — must use the imageSize parameter.

PNG Caching

Generated PNGs are cached (skip if file >10KB exists). To fix a bad slide:

rm slides-my-deck/slide-05.png   # delete the bad one
node generate-my-deck.js          # only slide 5 regenerates

Full regeneration: rm slides-my-deck/*.png && node generate-my-deck.js

Concurrency

Setting Behavior
1 Sequential, one slide at a time
5 Safe default for most Gemini rate limits
10-20 Faster, works if your API quota allows

Greeting Cards

Beyond presentation slides, mofa-pptx can generate standalone greeting card images using the runCards() API. Cards support any aspect ratio and output as PNG files (no PPTX assembly).

const { runCards } = require("./lib/engine");
const { getStyle } = require("./styles/cny-guochao");

const cards = [
  { name: "front",    style: "front",    prompt: "Card front description..." },
  { name: "greeting", style: "greeting", prompt: "Blessing text layout..." },
  { name: "scene",    style: "scene",    prompt: "Full scene illustration..." },
];

runCards({
  cardDir: "cards-cny",            // Output directory
  cards,                           // Array of { name, style, prompt }
  getStyle,                        // Style resolver from styles/*.js
  aspectRatio: "9:16",             // Any Gemini-supported ratio
  concurrency: 3,                  // Parallel workers
  imageSize: "2K",                 // Resolution
});

Output: cards-cny/card-front.png, cards-cny/card-greeting.png, cards-cny/card-scene.png

Supported Aspect Ratios

Ratio Orientation Use case
"9:16" Portrait Mobile wallpaper, e-cards, WeChat
"3:4" Portrait Classic postcard, print-friendly
"1:1" Square Social media, Instagram
"4:3" Landscape Traditional card
"16:9" Landscape Slides (default)

Card vs Slide

Slides (run) Cards (runCards)
Output PPTX + cached PNGs PNGs only
Aspect ratio 16:9 Any (default 9:16)
File names slide-01.png card-{name}.png
Directory slides-{deck}/ cards-{deck}/
Item fields { style, prompt } { name, style, prompt }

PNG Caching

Same as slides — cached PNGs >10KB are skipped. Delete to regenerate:

rm cards-cny/card-front.png    # delete the bad one
node generate-cny-cards.js      # only that card regenerates

Video Cards

Animate static greeting cards into short videos with background music. The pipeline: static PNG → Gemini Veo animation → ffmpeg compositing → MP4.

const { runVideoCards } = require("./lib/engine");
const { getStyle } = require("./styles/laoshu");
const { getAnimPrompt, DEFAULT_BGM } = require("./styles/video-card");

const cards = [
  {
    name: "heming",
    style: "scene",
    prompt: "鹤鸣茶社场景描述...",
    animStyle: "shuimo",   // Animation style: shuimo | festive | gentle | dynamic
    animDesc: "Steam rising from tea cups, leaves swaying...",  // Scene-specific details
  },
];

runVideoCards({
  cardDir: "cards-laoshu",
  cards,
  getStyle,                        // Image style resolver
  getAnimPrompt,                   // Animation prompt resolver
  bgmPath: DEFAULT_BGM,           // Background music (bgm-cny.mp3)
  aspectRatio: "9:16",
  imageSize: "2K",
});

Output: cards-laoshu/card-heming.png + cards-laoshu/card-heming-animated.mp4

Animation Styles

Tag Style Best for
shuimo 水墨 — slow, meditative ink-wash motion Ink painting scenes, landscapes
festive 喜庆 — lively, cheerful movement Spring Festival, celebrations
gentle 温柔 — dreamy, minimal movement Portraits, quiet moments
dynamic 动感 — energetic, noticeable motion Action scenes, animals

Video Compositing

Each video includes:

  • Still frame (2s) — original card image as thumbnail/preview
  • Crossfade (1s) — smooth transition to animation
  • Veo animation (~8s) — AI-generated motion
  • Fade out (1.5s) — graceful ending
  • BGM — background music with fade in/out

All parameters are configurable: stillDuration, crossfadeDur, fadeOutDur, musicVolume, musicFadeIn.

Standalone Animation

To animate an existing card PNG without regenerating the image:

node animate-card.js heming          # default shuimo style
node animate-card.js huahua dynamic  # dynamic style

Caching

Raw Veo videos are cached as *-raw.mp4. Delete to force re-animation:

rm cards-laoshu/card-heming-raw.mp4    # delete cached Veo video
node animate-card.js heming             # re-generates animation

Card vs Video Card

Cards (runCards) Video Cards (runVideoCards)
Output PNGs only PNGs + MP4 videos
Pipeline Gemini image gen Gemini image gen → Veo → ffmpeg
Style files styles/*.js styles/*.js + styles/video-card.js
Item fields { name, style, prompt } { name, style, prompt, animStyle?, animDesc? }
Dependencies Gemini API Gemini API + ffmpeg

Included Styles

Module Tags Theme
tectonic.js normal, data, cover Lavender gradient, whale watermark, monochrome wireframe
opensource.js normal, data, cover Similar to tectonic, cartoon whale variant
multi-brand.js cover, amazon_dark, amazon_light, google, microsoft, nvidia_dark, nvidia_light, overview Multi-company branded wireframes
feng-zikai.js scene, content, emotion Feng Zikai ink-wash painting on rice paper
dark-community.js default Linux Foundation corporate, abstract AI orbs
agentic-enterprise.js normal, cover, data, warm Purple consulting deck
agentic-enterprise-red.js normal, cover, data Red branded
lingnan.js cover, day1, day2, day3, info Lingnan ink-wash painting
what-is-life.js cover, physics_dark, biology_light, overview Science-themed
openclaw-red.js normal, cover, data Red-branded 4K
nb-pro.js default Lavender gradient purple accent
nb-br.js default Blade Runner 2049 dark cinematic
cc-research.js default Blade Runner 2049 golden hour warm
cny-guochao.js front, greeting, scene Chinese New Year — festive red+gold Guochao style (9:16 cards)
cny-shuimo.js front, greeting, scene Chinese New Year — ink-wash elegant style (9:16 cards)
laoshu.js front, greeting, scene Old Tree ink-wash — minimal figure + colloquial poetry
xianer.js front, greeting, scene Xian Er cartoon monk — cute, warm, healing
relevant.js front, greeting, scene, festive Relevant brand — geometric minimal + festive red-gold variant
video-card.js shuimo, festive, gentle, dynamic Video animation — Veo animation prompts + ffmpeg compositing config

Quick Start: One-Liner Card Generation with Claude Code

Setup (once)

git clone https://github.com/mofa-org/mofa-pptx.git
cd mofa-pptx
npm install
export GEMINI_API_KEY="your-key"

Generate Cards

Open Claude Code in the project directory and just say what you want:

"用水墨风格给张三做一张新年贺卡,祝他新春快乐,万事如意"

Or more specific:

"用Relevant红色节庆风格给陈岳做一张贺卡,内容是:风浪越大,鱼越贵。新春快乐!"

Claude Code will automatically:

  1. Pick the right style module
  2. Write the prompt
  3. Generate the script and run it
  4. Output high-res PNG

Style Keywords

Keyword Style Look
"国潮" "红金" cny-guochao Festive red + gold, dragons, lanterns
"水墨" "雅致" cny-shuimo Rice paper, ink wash, plum blossoms
"老树" "打油诗" laoshu Minimal ink figure + colloquial poetry
"贤二" "小和尚" xianer Cute cartoon monk, warm and healing
"Relevant" "极简" relevant Geometric egg-head, pure white
"Relevant红色" "节庆" relevant (festive) Red background, gold text, white accents

Prompting Tips

Style prefixes should specify: resolution/format, background colors, typography (fonts, weights, sizes, colors), illustration style (the most important part), and overall aesthetic reference.

Slide prompts should describe: title (position, style), layout (cards, columns, grids), per-element details (diagrams, text content), and optionally page numbers.

Use visual separators for complex layouts:

═══════════════════════════════════════
─── CARD 1: "Title" ───
─── CARD 2: "Title" ───

Common pitfalls:

  • Gemini may garble Chinese text — always visually inspect output
  • Avoid "title (12pt, #888)" — Gemini renders formatting hints as literal text. Use natural language: "title in small gray text"
  • For Chinese-only decks, explicitly state "ALL TEXT IN CHINESE" and "English only for proper nouns"
  • Complex multi-card layouts may need 2-3 regeneration attempts

About

Generative PPTX

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors