Gamma Migration Deep Dive
Current State
!npm list 2>/dev/null | head -10
Overview
Migrate presentation workflows from PowerPoint, Google Slides, Canva, or other platforms to Gamma's AI-powered generation. Gamma takes a fundamentally different approach -- instead of manually placing slides, you provide content and Gamma generates the presentation. Migration is about converting your content pipeline, not your slide files.
Prerequisites
- Gamma API access (Pro+ plan)
- Source presentations accessible for content extraction
- Node.js 18+ for migration scripts
- Completed
gamma-install-authsetup
Migration Approaches
| Approach | When to Use | Effort | |----------|-------------|--------| | Content extraction + regeneration | Lots of text-heavy presentations | Medium | | Import via Gamma UI | One-off migration of key decks | Low | | Template recreation | Repeatable presentation formats | Medium | | Parallel operation | Gradual transition over time | Low |
Key insight: You don't "import" slides into Gamma. You extract content from old presentations and regenerate them using Gamma's AI. This often produces better results than the originals.
Instructions
Step 1: Inventory Source Presentations
// scripts/inventory-presentations.ts
import { readdir, stat } from "node:fs/promises";
import { join, extname } from "node:path";
interface PresentationInfo {
path: string;
format: string;
sizeMB: number;
lastModified: Date;
}
async function inventoryPresentations(dir: string): Promise<PresentationInfo[]> {
const entries = await readdir(dir, { recursive: true });
const presentations: PresentationInfo[] = [];
for (const entry of entries) {
const ext = extname(entry).toLowerCase();
if ([".pptx", ".ppt", ".key", ".pdf", ".md"].includes(ext)) {
const fullPath = join(dir, entry);
const info = await stat(fullPath);
presentations.push({
path: fullPath,
format: ext,
sizeMB: info.size / (1024 * 1024),
lastModified: info.mtime,
});
}
}
console.log(`Found ${presentations.length} presentations:`);
const byFormat = presentations.reduce((acc, p) => {
acc[p.format] = (acc[p.format] || 0) + 1;
return acc;
}, {} as Record<string, number>);
console.log("By format:", byFormat);
return presentations;
}
Step 2: Extract Content from PowerPoint
// scripts/extract-pptx.ts
// Use 'pptx-parser' or 'officegen' to extract text content
import JSZip from "jszip";
import { readFile } from "node:fs/promises";
import { DOMParser } from "xmldom";
async function extractPptxContent(pptxPath: string): Promise<string[]> {
const buffer = await readFile(pptxPath);
const zip = await JSZip.loadAsync(buffer);
const slides: string[] = [];
const slideFiles = Object.keys(zip.files)
.filter((f) => f.match(/ppt\/slides\/slide\d+\.xml$/))
.sort();
for (const slideFile of slideFiles) {
const xml = await zip.file(slideFile)!.async("string");
const doc = new DOMParser().parseFromString(xml);
// Extract all text elements
const textNodes = doc.getElementsByTagName("a:t");
const texts: string[] = [];
for (let i = 0; i < textNodes.length; i++) {
const text = textNodes[i].textContent?.trim();
if (text) texts.push(text);
}
slides.push(texts.join("\n"));
}
return slides;
}
// Convert extracted content to Gamma prompt
function slidesToGammaPrompt(slides: string[], title: string): string {
let prompt = `${title}\n\n`;
slides.forEach((content, i) => {
prompt += `Slide ${i + 1}:\n${content}\n\n`;
});
return prompt;
}
Step 3: Batch Migration Script
// scripts/migrate-to-gamma.ts
import { createGammaClient } from "../src/client";
import { pollUntilDone } from "../src/poll";
import pLimit from "p-limit";
const gamma = createGammaClient({ apiKey: process.env.GAMMA_API_KEY! });
const limit = pLimit(2); // Max 2 concurrent generations
interface MigrationItem {
title: string;
content: string;
sourceFile: string;
}
async function migrateBatch(items: MigrationItem[]) {
const results = await Promise.allSettled(
items.map((item) =>
limit(async () => {
console.log(`Migrating: ${item.title}`);
const { generationId } = await gamma.generate({
content: item.content,
outputFormat: "presentation",
textMode: "condense", // AI condenses extracted text
exportAs: "pptx", // Get PPTX for comparison
});
const result = await pollUntilDone(gamma, generationId);
return {
title: item.title,
sourceFile: item.sourceFile,
gammaUrl: result.gammaUrl,
exportUrl: result.exportUrl,
creditsUsed: result.creditsUsed,
};
})
)
);
// Report
const succeeded = results.filter((r) => r.status === "fulfilled");
const failed = results.filter((r) => r.status === "rejected");
console.log(`\nMigration complete: ${succeeded.length} succeeded, ${failed.length} failed`);
for (const r of results) {
if (r.status === "fulfilled") {
console.log(` OK: ${r.value.title} → ${r.value.gammaUrl}`);
} else {
console.log(` FAIL: ${r.reason}`);
}
}
}
Step 4: Template Recreation
For recurring presentation types (weekly reports, proposals, etc.), create Gamma templates:
Migration steps for templates:
1. Identify repeating presentation formats in your org
2. Create a one-page template gamma in the Gamma app:
- gamma.app → Create → design a single representative page
3. Note the template gamma ID from the URL
4. Use POST /v1.0/generations/from-template with the gammaId
5. Update your automation scripts to use generateFromTemplate()
// After template creation in Gamma UI
const MIGRATED_TEMPLATES: Record<string, string> = {
"weekly-report": "gamma_template_weekly_abc123",
"sales-proposal": "gamma_template_proposal_def456",
"team-update": "gamma_template_update_ghi789",
};
async function generateFromMigratedTemplate(
templateKey: string,
content: string
) {
const gammaId = MIGRATED_TEMPLATES[templateKey];
if (!gammaId) throw new Error(`Unknown template: ${templateKey}`);
const { generationId } = await gamma.generateFromTemplate({
gammaId,
prompt: content,
exportAs: "pdf",
});
return pollUntilDone(gamma, generationId);
}
Step 5: Validation Checklist
After migrating each presentation:
- [ ] Content accuracy: AI-generated text matches source intent
- [ ] Slide count: reasonable for the content volume
- [ ] Theme/branding: workspace theme applied correctly
- [ ] Export quality: PDF/PPTX downloads successfully
- [ ] Links preserved: any URLs from original are in the content
- [ ] Stakeholder review: key presentations reviewed by owners
Supported Migration Paths
| Source | Method | Fidelity | Notes | |--------|--------|----------|-------| | PowerPoint (.pptx) | Extract text → regenerate | Content-high, design-new | AI redesigns slides | | Google Slides | Export as .pptx → extract | Content-high, design-new | Export first | | Canva | Export as .pdf → extract text | Medium | Limited text extraction | | Keynote (.key) | Export as .pptx → extract | Content-high, design-new | Export first | | Markdown (.md) | Direct use as content | High | Best migration path | | Notion pages | Export as .md → use directly | High | Clean text extraction |
Error Handling
| Issue | Cause | Solution | |-------|-------|----------| | Content too long | Exceeds 100K token limit | Split into multiple presentations | | Credit budget exceeded | Too many migrations at once | Batch over multiple days | | Poor output quality | Content too unstructured | Add structure (headings, bullets) to extracted content | | Missing images | Images in source not extracted | Gamma generates new images; reference image concepts in text |
Resources
Next Steps
Review gamma-core-workflow-a for ongoing content generation after migration.