WebSocket protocol
The presentation view (index.html) and speaker/notes view (notes.html) synchronise slide state over a real-time connection. The default transport is a WebSocket to the server at /ws. When running without a server (e.g. file:// or deployed), the system falls back to a BroadcastChannel named "slidesk_sync".
Both transports share the same message format.
Message format
All messages are JSON:
{
"action": "<string>",
"payload": <any>
}
payload is optional and type depends on the action.
Actions
Presentation → Speaker view
These messages originate from the presentation view when the slide changes:
| Action | Payload | Description |
|---|---|---|
"current" |
string | Outer HTML of the current .sd-slide. The speaker view renders it in #sd-sv-current and extracts notes, timer data |
"future" |
string | Outer HTML of the next .sd-slide (empty string on last slide). Rendered in #sd-sv-future |
"goto" |
number | Current slide index. Used by the speaker view to look up timer checkpoints |
"checkpoints" |
{timerCheckpoints, nbSlides} |
Timer checkpoint data sent once on page load |
Speaker view → Presentation
The speaker view sends these actions on keyboard input:
| Action | Payload | Description |
|---|---|---|
"next" |
— | Advance to next slide |
"previous" |
— | Go back to previous slide |
Server → Client
The server sends these messages:
| Action | Payload | Description |
|---|---|---|
"reload" |
— | Trigger a full page reload (sent when files change) |
CLI → Server → Client
When interacting via the command line (slidesk present --goto 5, slidesk present --next, etc.):
| Action | Data | Description |
|---|---|---|
"goto" |
number | Jump to a slide |
"next" |
— | Next slide |
"previous" |
— | Previous slide |
The CLI sends these through server.send(action, data), which publishes {"action": <action>, "data": <data>} to the "slidesk" channel. The client handles them via window.slidesk[data.action](data).
Dynamic method dispatch
When the client receives an action it does not recognise, it falls through to:
window.slidesk[data.action](data);
This allows any method on window.slidesk (e.g. fullscreen, next, previous, goto) to be called remotely by setting the action to the method name.
Plugin WebSocket messages
When a plugin defines an addWS handler in its plugin.json, the server routes messages that include a plugin field:
{
"plugin": "<plugin_name>",
"message": "..."
}
The server calls the plugin's WebSocket handler with the raw JSON string and broadcasts the response as:
{
"action": "<plugin_name>_response",
"response": <handler_return_value>
}
All clients subscribed to "slidesk" receive the response.
Unrecognised messages
Any message that does not match the built-in actions is broadcast as-is to all clients on the "slidesk" channel. On the receiving end, if window.slidesk[data.action] exists, it is called with data as the argument.
Example flow: slide change
sequenceDiagram
participant P as Presentation
participant W as WebSocket
participant S as Speaker view
P->>P: changeSlide()
P->>W: current + slide outerHTML
W->>S: current + slide outerHTML
S->>S: render & extract notes & timers
P->>W: future + next slide outerHTML
W->>S: future + next slide outerHTML
S->>S: render future slide
P->>W: goto + slide number
W->>S: goto + slide number
S->>S: update checkpoints
S->>W: previous
W->>P: previous
P->>P: previous() on ArrowLeft
S->>W: next
W->>P: next
P->>P: next() on ArrowRight
BroadcastChannel fallback
When window.location.origin is "file://" or the WebSocket connection fails, the client creates a BroadcastChannel("slidesk_sync"). The message format is identical. This enables synchronisation between the presentation and speaker view when both are opened locally from the file system or from a deployed static site.