A zero-cost Google Apps Script automation that sweeps a Drive folder (and all of its subfolders), extracts every piece of text from every Google Slides deck it finds—slide content, tables, grouped shapes, and speaker notes—and compiles it into a single, structured Google Doc ready for analysis.
If small builds interest you, take a look at another small system I built to turn scattered raw material into usable content.
What it does
- Finds every Google Slides file in a target folder tree
- Extracts all text: shapes, text boxes, tables (row by row), grouped elements, and speaker notes
- Writes everything to one Google Doc, organized with each deck as a Heading 1 and each slide as a Heading 2, so the Doc outline doubles as a navigation panel
- Names the output after the folder and the run date (e.g. Slides Text Compilation — Grad Seminar — 2026-07-20) and saves it inside the target folder, next to the source decks
- Retargets without code edits: the script sweeps whichever folder its own file sits in, so you point it at a new folder by dragging the file in Drive
Why Apps Script
The decks live in a personal Google account, so the automation needs to run inside that account. Apps Script does this with no OAuth client setup, no API keys, no server, and no cost. It runs in Google’s cloud, so scheduled runs work even with your computer off.
Setup (one time, ~5 minutes)
- Open a browser tab and make sure you’re signed into the Google account that holds the slides.
- Go to script.google.com.
- If necessary, click the Google account con and switch accounts.
- Click New project.
- Delete the placeholder code in the editor and paste in the full contents of
compileSlidesText.js. - Click the project name (“Untitled project”) at the top and rename it something recognizable, e.g. Slides Compiler. This name is also the filename you’ll see in Drive.
- Click Rename in the popup to save.
- Open drive.google.com in the same account.
- Select My Drive in the side menu.
- Scroll down past the folders to locate the file name.
- Drag the script file (or use the Move tool) into the parent folder whose decks you want compiled.
Running it
- Back in the Apps Script editor, make sure compileSlidesText is selected in the function dropdown next to the Run button.
- Click Run.
- On the first run only, Google asks for authorization. Click Review permissions, choose your account, and approve access to Drive, Slides, and Docs. If Google shows a “Google hasn’t verified this app” warning, click Advanced → Go to Slides Compiler (unsafe) — this warning appears for all personal scripts and is expected; you wrote the app and you are the only user. Be sure to select all permissions before clicking Continue. Otherwise, you’ll get an Access Denied error message.
- Wait for the run to finish. Open the Execution log at the bottom of the editor: it reports how many decks it compiled and prints a direct link to the finished Doc.
- The Doc also sits inside the target folder itself, so you can find it in Drive later without the log.
Retargeting to a different folder
Drag (or move) the script file from its current folder into the new parent folder in Drive, then run it again. That’s the entire retargeting process. The script always sweeps the folder it currently lives in, including all nested subfolders.
To limit a run to the folder’s top level only (no subfolders), open the script and change one line near the top:
const INCLUDE_SUBFOLDERS = false;
Troubleshooting
Error: “This script file is not inside any folder.” The script file is sitting loose in My Drive. Drag it into the folder you want swept and rerun.
A deck shows “[Could not open this deck: …]” in the output. The account can see the file but lacks permission to open it (common with decks shared as view-restricted). The script logs the failure and continues; the rest of the compilation is unaffected.
The run times out. Free-tier Apps Script executions cap at 6 minutes, which comfortably handles dozens of decks. Hundreds of decks need a batched variant that saves its position and resumes across runs.
Some decks are missing from the output. Two usual causes: the decks are Drive shortcuts whose real files live outside the folder tree (shortcuts are skipped), or they’re PowerPoint (.pptx) files rather than native Google Slides. Both can be handled with small additions to the script.
Code
compileSlidesText.js— the complete script
/**
* Compile the text of every Google Slides deck in this account
* into a single Google Doc for analysis.
*
* Run compileSlidesText() from the Apps Script editor.
* The log will print a link to the finished Doc.
*/
// The script sweeps whichever folder this script file sits in.
// To change the target, drag the script file into a different
// folder in Drive — no code edits needed.
// Set to true if decks inside subfolders should be included too
const INCLUDE_SUBFOLDERS = true;
function compileSlidesText() {
// Find the folder that contains this script file
const scriptFile = DriveApp.getFileById(ScriptApp.getScriptId());
const parents = scriptFile.getParents();
if (!parents.hasNext()) {
throw new Error('This script file is not inside any folder. Move it into the folder you want to sweep.');
}
const rootFolder = parents.next();
const doc = DocumentApp.create(
'Slides Text Compilation — ' + rootFolder.getName() + ' — ' + new Date().toISOString().slice(0, 10)
);
const body = doc.getBody();
const decks = collectDecks_(rootFolder, INCLUDE_SUBFOLDERS);
// Sort decks oldest-first by creation date.
// For alphabetical order by filename, use this line instead:
// decks.sort(function (a, b) { return a.getName().localeCompare(b.getName()); });
decks.sort(function (a, b) {
return a.getDateCreated() - b.getDateCreated();
});
let deckCount = 0;
for (const file of decks) {
deckCount++;
body.appendParagraph(file.getName())
.setHeading(DocumentApp.ParagraphHeading.HEADING1);
body.appendParagraph('Last modified: ' + file.getLastUpdated());
let pres;
try {
pres = SlidesApp.openById(file.getId());
} catch (e) {
body.appendParagraph('[Could not open this deck: ' + e.message + ']');
continue;
}
const slides = pres.getSlides();
for (let i = 0; i < slides.length; i++) {
body.appendParagraph('Slide ' + (i + 1))
.setHeading(DocumentApp.ParagraphHeading.HEADING2);
// Text from shapes, tables, and groups on the slide
slides[i].getPageElements().forEach(function (el) {
extractElementText_(el, body);
});
// Speaker notes
const notesShape = slides[i].getNotesPage().getSpeakerNotesShape();
if (notesShape) {
const notes = notesShape.getText().asString().trim();
if (notes) {
body.appendParagraph('Speaker notes: ' + notes)
.setItalic(true);
}
}
}
}
// Move the finished Doc into the target folder itself
const docFile = DriveApp.getFileById(doc.getId());
docFile.moveTo(rootFolder);
Logger.log('Compiled ' + deckCount + ' decks into: ' + doc.getUrl());
}
/**
* Gather all Google Slides files in a folder,
* with optional recursion into subfolders.
*/
function collectDecks_(folder, recurse) {
const decks = [];
const files = folder.getFilesByType(MimeType.GOOGLE_SLIDES);
while (files.hasNext()) {
decks.push(files.next());
}
if (recurse) {
const subfolders = folder.getFolders();
while (subfolders.hasNext()) {
decks.push.apply(decks, collectDecks_(subfolders.next(), true));
}
}
return decks;
}
/**
* Pull text out of a single page element.
* Handles shapes, tables, and nested groups.
*/
function extractElementText_(el, body) {
const type = el.getPageElementType();
if (type === SlidesApp.PageElementType.SHAPE) {
const text = el.asShape().getText().asString().trim();
if (text) body.appendParagraph(text);
} else if (type === SlidesApp.PageElementType.TABLE) {
const table = el.asTable();
for (let r = 0; r < table.getNumRows(); r++) {
const rowText = [];
for (let c = 0; c < table.getNumColumns(); c++) {
const cell = table.getCell(r, c).getText().asString().trim();
if (cell) rowText.push(cell);
}
if (rowText.length) body.appendParagraph(rowText.join(' | '));
}
} else if (type === SlidesApp.PageElementType.GROUP) {
el.asGroup().getChildren().forEach(function (child) {
extractElementText_(child, body);
});
}
}