AI Slide Creator for Google Slides with Apps Script

🚀 Apps Script + Gemini Mastery — Issue #6

https://github.com/lsvekis/AI-Slide-Creator-for-Google-Slides

AI Slide Creator for Google Slides

Generate full slide decks (titles, bullets, structure, and notes) from a single topic — right inside Google Slides.


⭐ What You’ll Build in This Issue

In this lesson, you’ll build an AI Slide Creator that lives inside Google Slides and can:

  • 🧠 Generate a complete slide deck from a topic
  • 🎯 Tailor content to a specific audience (e.g., executives, students, beginners)
  • 📄 Control slide count (e.g., 5, 10, 15 slides)
  • 📝 Add speaker notes generated by Gemini
  • 🔁 Regenerate or extend content easily

You’ll trigger it from a custom menu and a sidebar UI directly in Slides.


🧠 Learning Objectives

By the end of this issue, you’ll know how to:

  • Use SlidesApp to create and modify slides programmatically
  • Build a custom sidebar UI in Google Slides
  • Prompt Gemini to return structured JSON for slide content
  • Parse that JSON & convert it into real Slides (titles, bullets, notes)
  • Reuse your existing callGemini helper in a Slides-bound project

🧩 EXERCISE — Build the AI Slide Creator

You’ll create:

  1. A custom menu in Slides
  2. A sidebar with topic / audience / slide count
  3. A Gemini-powered generator that returns slide structure as JSON
  4. A slide builder that converts JSON to real slides

1️⃣ Create a Slides-bound Apps Script Project

  1. Open Google Slides.
  2. Go to Extensions → Apps Script.
  3. Delete any default code.

We’ll add these files:

  • Code.gs
  • GeminiHelpers.gs
  • SlideBuilder.gs
  • Sidebar.html

2️⃣ Add the Menu + Sidebar Entry Point

Code.gs

function onOpen() {
  SlidesApp.getUi()
    .createMenu("AI Slides")
    .addItem("Open AI Slide Creator", "showSlideCreator")
    .addToUi();
}

function showSlideCreator() {
  const html = HtmlService.createHtmlOutputFromFile("Sidebar")
    .setTitle("AI Slide Creator");
  SlidesApp.getUi().showSidebar(html);
}

3️⃣ Sidebar UI for Topic, Audience, Slide Count

Sidebar.html

<div style="font-family: Arial, sans-serif; padding: 14px;">
  <h2>AI Slide Creator</h2>

  <p>Enter a topic and let Gemini build the deck for you.</p>

  <label><b>Topic</b></label><br>
  <input id="topic" style="width:100%; margin-bottom:8px;"
         placeholder="e.g., Introduction to Google Apps Script" />

  <label><b>Audience</b> (optional)</label><br>
  <input id="audience" style="width:100%; margin-bottom:8px;"
         placeholder="e.g., non-technical managers, beginners" />

  <label><b>Number of Slides</b> (optional)</label><br>
  <input id="slideCount" type="number" min="3" max="30"
         style="width:100%; margin-bottom:8px;"
         placeholder="e.g., 8" />

  <button onclick="generate()" style="margin-top:10px;">Generate Deck</button>

  <pre id="output"
       style="white-space:pre-wrap; margin-top:14px; max-height:260px; overflow:auto;"></pre>

  <script>
    function generate() {
      const topic      = document.getElementById("topic").value;
      const audience   = document.getElementById("audience").value;
      const slideCount = document.getElementById("slideCount").value;

      document.getElementById("output").textContent = "Generating slides with Gemini...";

      google.script.run
        .withSuccessHandler(function(msg) {
          document.getElementById("output").textContent = msg;
        })
        .generateDeckFromPrompt(topic, audience, slideCount);
    }
  </script>
</div>

4️⃣ Gemini Helper (Reuse from Previous Issues)

GeminiHelpers.gs

🔐 Don’t commit your real API key to GitHub. Use a placeholder in repos.

// === Gemini Config ===
const GEMINI_API_KEY = "YOUR_API_KEY_HERE";
const GEMINI_MODEL   = "gemini-2.5-flash"; // from your ListModels response

/**
 * Generic helper to call Gemini generateContent.
 * @param {string} prompt - instruction for the model
 * @param {string} text   - optional additional text
 * @returns {string}      - response text or error message
 */
function callGemini(prompt, text) {
  if (!GEMINI_API_KEY || GEMINI_API_KEY === "YOUR_API_KEY_HERE") {
    throw new Error("GEMINI_API_KEY is not configured.");
  }

  const url = `https://generativelanguage.googleapis.com/v1/models/${GEMINI_MODEL}:generateContent?key=${GEMINI_API_KEY}`;

  const payload = {
    contents: [{
      parts: [{
        text: `${prompt}${text ? "\n\n---\n\n" + text : ""}`
      }]
    }]
  };

  const res = UrlFetchApp.fetch(url, {
    method: "post",
    contentType: "application/json",
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  });

  const body = res.getContentText();
  const json = JSON.parse(body);

  if (json.error) {
    throw new Error(`Gemini API error: ${json.error.message} (${json.error.status})`);
  }

  const textPart = json.candidates?.[0]?.content?.parts?.[0]?.text;
  if (!textPart) {
    throw new Error("Unexpected Gemini response: " + body);
  }

  return textPart;
}

/**
 * Quick test you can run from the Script Editor.
 */
function testGeminiSlides() {
  const result = callGemini(
    "Write a title and 3 bullet points about using AI in Google Workspace.",
    ""
  );
  Logger.log(result);
}

5️⃣ Generate Slide Structure with Gemini

We’ll ask Gemini to return strict JSON describing the slide deck:

  • title – global deck title
  • slides[] – each with title, bullets[], notes

SlideBuilder.gs

/**
 * Main entry called from Sidebar.html.
 */
function generateDeckFromPrompt(topic, audience, slideCount) {
  if (!topic || topic.trim() === "") {
    return "Please enter a topic.";
  }

  const count = slideCount ? parseInt(slideCount, 10) : 8;
  const safeCount = isNaN(count) ? 8 : Math.max(3, Math.min(count, 30));

  const audienceText = audience && audience.trim()
    ? `Target audience: ${audience}.`
    : "Target audience: general beginners.";

  const prompt = `
You are an expert instructional designer.

Create a slide deck outline in pure JSON ONLY (no markdown, no commentary).
Follow this structure exactly:

{
  "title": "Deck title here",
  "slides": [
    {
      "title": "Slide title",
      "bullets": [
        "Bullet 1",
        "Bullet 2"
      ],
      "notes": "Speaker notes for this slide."
    }
  ]
}

Requirements:
- Topic: ${topic}
- ${audienceText}
- Number of slides (excluding title slide if needed): ${safeCount}
- Use clear, concise bullets (no more than 5 per slide).
- Keep speaker notes short but helpful (1–3 sentences).
- JSON must be valid and parseable.
Return ONLY the JSON object, no explanation before or after.
`;

  let jsonText;
  try {
    jsonText = callGemini(prompt, "");
  } catch (err) {
    Logger.log("Error calling Gemini: " + err);
    return "Error calling Gemini: " + err;
  }

  let deck;
  try {
    // Some models wrap JSON in ```json ... ``` – strip if needed
    jsonText = jsonText.trim();
    if (jsonText.startsWith("```")) {
      jsonText = jsonText.replace(/```json/i, "")
                         .replace(/```/g, "")
                         .trim();
    }

    deck = JSON.parse(jsonText);
  } catch (e) {
    Logger.log("JSON parse error: " + e + "\nRaw:\n" + jsonText);
    return "Could not parse Gemini JSON. Check logs for details.";
  }

  try {
    buildSlidesFromDeck_(deck);
  } catch (e) {
    Logger.log("Slide build error: " + e);
    return "Error while creating slides: " + e;
  }

  return `Slide deck generated for topic: "${topic}" with ~${safeCount} slides.`;
}

6️⃣ Build the Actual Slides

SlideBuilder.gs (append below previous code)

/**
 * Build slides in the current presentation from the JSON deck definition.
 */
function buildSlidesFromDeck_(deck) {
  const presentation = SlidesApp.getActivePresentation();

  // Option: clear existing slides, or append to the end.
  // Here we append after the existing slides.
  const slides = deck.slides || [];
  const deckTitle = deck.title || "AI Generated Deck";

  // Create a title slide
  const titleSlide = presentation.appendSlide(
    presentation.getSlides()[0].getLayout()
  );

  titleSlide.getPageElements().forEach(pe => {
    try {
      pe.asShape().getText().setText("");
    } catch (e) {}
  });

  const shapes = titleSlide.getShapes();
  if (shapes.length > 0) {
    shapes[0].getText().setText(deckTitle);
  }

  // Create content slides
  slides.forEach(slideData => {
    const slide = presentation.appendSlide(
      presentation.getSlides()[0].getLayout()
    );

    const title = slideData.title || "";
    const bullets = slideData.bullets || [];
    const notes = slideData.notes || "";

    // Clear existing text
    slide.getPageElements().forEach(pe => {
      try {
        pe.asShape().getText().setText("");
      } catch (e) {}
    });

    const shapes = slide.getShapes();
    let titleShape, bodyShape;

    if (shapes.length >= 2) {
      titleShape = shapes[0];
      bodyShape  = shapes[1];
    } else if (shapes.length === 1) {
      titleShape = shapes[0];
    }

    if (titleShape) {
      titleShape.getText().setText(title);
    }

    if (bodyShape) {
      const bodyText = bodyShape.getText();
      bodyText.setText("");
      bullets.forEach((b, i) => {
        if (i === 0) {
          bodyText.setText(b);
        } else {
          bodyText.appendParagraph(b);
        }
      });
    }

    // Speaker notes
    if (notes && notes.trim()) {
      const notesPage = slide.getNotesPage();
      const notesShape = notesPage.getSpeakerNotesShape();
      notesShape.getText().setText(notes);
    }
  });
}

🔬 Testing the Lesson

  1. Open the Slides file bound to this script.
  2. Go to Extensions → Apps Script, set your GEMINI_API_KEY.
  3. From the editor, run testGeminiSlides() once to authorize & confirm connectivity.
  4. Back in Slides, reload the presentation.
  5. Use AI Slides → Open AI Slide Creator.
  6. Try:
    • Topic: Getting started with Google Apps Script
    • Audience: beginner web developers
    • Number of slides: 7
  7. Click Generate Deck and watch slides appear.

🎉 What You Built in Issue #6

You now have a working AI Slide Creator that:

  • Takes a topic & audience
  • Uses Gemini to design a structured deck
  • Converts that structure into real Slides
  • Adds speaker notes for presenters
  • Lives entirely inside Google Slides

This is the backbone of:

  • Auto-generated training decks
  • Lesson plans for educators
  • Sales / pitch decks
  • Internal onboarding & policy decks