// Slides · Apps Script

Set speaker notes on every slide in Google Slides.

How to read and write speaker notes on every slide in Google Slides using Apps Script — the NotesPage indirection that getShapes() silently skips.

I want to programmatically set or read speaker notes on every slide in a Google Slides presentation without clicking into each slide manually.

The script

copy · paste · trigger
setSpeakerNotes.gs
Apps Script
// setSpeakerNotes.gs — write notes to every slide in the active presentation
// Each slide has a NotesPage; the speaker notes live in a specific shape there.

function setSpeakerNotesOnAllSlides() {
  var pres = SlidesApp.getActivePresentation();
  var slides = pres.getSlides();

  for (var i = 0; i < slides.length; i++) {
    var slide = slides[i];
    var notesPage = slide.getNotesPage();
    var notesShape = notesPage.getSpeakerNotesShape();
    var label = 'Slide ' + (i + 1) + ' notes go here.';
    notesShape.getText().setText(label);
  }

  Logger.log('Done. Updated notes on ' + slides.length + ' slides.');
}

Need a variant? Gnaw writes a custom version from one sentence — fields, triggers, edge cases handled.

Walkthrough

Why looping getShapes() writes nothing

The first time I ran a shapes loop expecting to find speaker notes, nothing failed — the loop just silently touched zero notes. Speaker notes are not a shape on the slide canvas. They live on a completely separate object: a NotesPage, one per slide, accessible only through slide.getNotesPage(). Calling getShapes() on the slide itself will never return the notes shape because that shape does not exist in the slide's shape collection.

Once you have the NotesPage, you still need one more step: call getSpeakerNotesShape() on it. That returns the single TextItem-bearing shape that Slides reserves for speaker notes. From there, getText().setText() works exactly as it does on any other text shape. The full path is: slide → getNotesPage() → getSpeakerNotesShape() → getText() → setText().

Reading existing notes before overwriting

If you want to append to existing notes rather than replace them, swap setText() for appendText(). Or read first with getText().asString(), inspect it, then decide. Calling setText() unconditionally wipes whatever was there, including any formatting runs.

A common pattern is to check whether the existing notes are blank before writing. getText().asString().trim() returns an empty string on a slide with no notes, so a simple length check gates the write. This matters if you are stamping notes onto a deck that already has partial notes and you do not want to clobber them.

Targeting a specific slide range

getSlides() returns all slides as a zero-indexed array. If you only want slides 3 through 7, slice the array: slides.slice(2, 7). The indices follow standard JavaScript array semantics — the end index is exclusive.

You can also target slides by their position property. slide.getObjectId() gives a stable string ID that survives reordering. If you are building a tool that stamps notes based on a spreadsheet lookup keyed to slide ID, store getObjectId() values in the sheet rather than positional indices. A reorder will invalidate position-based lookups silently; IDs survive it.

Want a custom version?

Describe your sheet and the rule you want. Gnaw writes the Apps Script — fields, triggers, edge cases — in one shot.

FAQ

4 questions
Why does getSpeakerNotesShape() throw 'Cannot read properties of null'?
This happens when the NotesPage has been corrupted or was built from a template that removed the reserved notes placeholder. Re-insert the notes placeholder via Slide menu > Insert > Speaker notes, or create a new slide and copy content to it. The getSpeakerNotesShape() method expects exactly one notes placeholder shape to exist on the NotesPage.
Can I set font size or bold on speaker notes text?
Yes. getSpeakerNotesShape().getText() returns a TextRange. From there, getTextStyle() sets formatting, or you can use getRuns() to style individual segments. setText() strips all existing formatting, so if you need styled notes, build the content with appendParagraph() or use the TextRange API directly instead of calling setText().
Does this work on presentations I open by ID, not just the active one?
Yes. Replace SlidesApp.getActivePresentation() with SlidesApp.openById('YOUR_PRESENTATION_ID'). The script must run under an account that has edit access to that file. If you are running it as a standalone script rather than a container-bound one, openById is the only option anyway.
How do I export all speaker notes to a Google Doc?
Loop the slides, call getSpeakerNotesShape().getText().asString() on each, and append each string to a Doc via DocumentApp.create() or openById(). Separate slides with a paragraph break or a horizontal rule using appendHorizontalRule(). The full text of a 50-slide deck exports in well under the 30-second Apps Script execution limit.