The Speaker Notes plugin provides a separate speaker view window with presentation notes, timing controls, and slide previews. Notes are extracted from aside.notes elements or data-notes attributes and synchronized between windows via PostMessage API.
See Slide Attributes for note markup syntax. See Plugin System for plugin architecture details.
The plugin operates across two browser windows: the main presentation and a popup speaker view. State synchronization occurs via window.postMessage() with origin validation for security.
Sources: plugin/notes/plugin.js16-269 plugin/notes/speaker-view.html340-515
Notes are defined using <aside class="notes"> elements within slides:
Or via data-notes attribute for plain text:
Sources: plugin/notes/plugin.js123-156
The post() function extracts notes from multiple sources with this priority hierarchy:
fragmentElement.querySelector('aside.notes'))data-notes (fragmentElement.getAttribute('data-notes'))data-notes (slideElement.getAttribute('data-notes'))aside.notes (excluding those inside fragments)Sources: plugin/notes/plugin.js108-160
The post() function builds this message object:
| Field | Type | Description |
|---|---|---|
namespace | string | Always 'reveal-notes' |
type | string | Always 'state' |
notes | string | HTML or text content |
markdown | boolean | True if aside.notes[data-markdown] |
whitespace | string | 'pre-wrap' for data-notes, 'normal' for HTML |
state | object | Result of deck.getState() |
Sources: plugin/notes/plugin.js114-158
The plugin uses window.postMessage() for cross-window communication with origin validation via isSameOriginEvent().
All messages use namespace: 'reveal-notes' with these types:
| Type | Direction | Payload | Purpose |
|---|---|---|---|
connect | Main → Speaker | {state, url} | Initial handshake with presentation URL |
connected | Speaker → Main | None | Acknowledgment of connection |
state | Main → Speaker | {notes, markdown, whitespace, state} | Notes content and presentation state |
call | Speaker → Main | {callId, methodName, arguments} | Invoke Reveal API method |
return | Main → Speaker | {callId, result} | Return value from API call |
heartbeat | Speaker → Main | None | Connection keep-alive (every 1000ms) |
Sources: plugin/notes/plugin.js76-102 plugin/notes/speaker-view.html402-461 plugin/notes/speaker-view.html587-592
Sources: plugin/notes/plugin.js25-87 plugin/notes/speaker-view.html469-480
Both windows validate message origins before processing:
The speaker view validates with:
Sources: plugin/notes/plugin.js166-175 plugin/notes/speaker-view.html389-391
The speaker view displays in a popup window (1100×700px) with layout controlled by data-speaker-layout attribute.
Sources: plugin/notes/speaker-view.html309-338
The body[data-speaker-layout] attribute controls positioning:
| Layout Value | #current-slide | #upcoming-slide | #speaker-controls |
|---|---|---|---|
default | 60% width, 100% height, left | 40% width, 40% height, right top | 40% width, 60% height, right bottom |
wide | 50% width, 45% height, left top | 50% width, 45% height, right top | 100% width, 50% height, bottom |
tall | 45% width, 50% height, left top | 45% width, 50% height, left bottom | 55% width, 100% height, right |
notes-only | display: none | display: none | 100% width, 100% height |
Layout persisted in localStorage with key 'reveal-speaker-layout'.
Sources: plugin/notes/speaker-view.html218-284 plugin/notes/speaker-view.html820-833
The setupIframes() function creates two iframe elements with query parameters:
Current slide iframe: 1280×1024px, synchronized with main window state.
Upcoming slide iframe: 640×512px, shows next() slide.
Sources: plugin/notes/speaker-view.html543-570
The plugin initializes with the reveal.js instance and sets up event listeners for state changes:
The plugin registers the 'S' key to open the speaker view:
The plugin monitors these reveal.js events for state synchronization:
slidechanged: Slide navigation updatesfragmentshown/fragmenthidden: Fragment state changesoverviewhidden/overviewshown: Overview mode changespaused/resumed: Presentation pause statepreviewiframe, previewimage, previewvideo)Sources: plugin/notes/plugin.js204-218 plugin/notes/plugin.js257-261
Refresh this wiki