# Live presenting

A standalone deck syncs across your own windows via `BroadcastChannel`. Run it through the **live server** and it becomes a shared session: every connected device follows along, and [plugins](https://docs.liebstoeckel.app/plugins/overview/) come alive.

## Start a session

```bash
liebstoeckel live my-talk            # a deck dir, or a built .html
```

This builds the deck (if needed), serves it on your LAN, and prints presenter + audience links:

```
▶  liebstoeckel live, session a1b2c3d4
   on this machine   presenter  http://localhost:4321/?t=…
                     audience   http://localhost:4321/?t=…
   on the network    presenter  http://192.168.1.20:4321/?t=…
                     audience   http://192.168.1.20:4321/?t=…
```

1. Open the **presenter** link (or press <kbd>p</kbd> for the presenter window).
2. The room opens the **audience** link: share it directly, or press <kbd>q</kbd> in the deck to show a scannable QR full-screen.
3. Navigate: every viewer follows. Plugins (polls, Q&A, reactions) sync in real time.
**Share both links from the presenter view:** In the presenter window (<kbd>p</kbd>), press <kbd>q</kbd> (or the header QR button) to show **two** QRs at once: **Follow along** (the read-only audience link) and **Drive from your phone** (the presenter link). Scanning the second joins as a second driver, handy for advancing slides from your phone while you walk the room.
**Presenter consoles & focus mode:** The presenter window's panel is **tabbed**: **Notes** (default) plus a private console per interactive plugin. Watch live poll tallies, or moderate the Q&A queue (mark answered, dismiss, reveal) without any of it showing on the room's screen. When a pane gets busy, press <kbd>z</kbd> (or the ⤢ button) to **maximize** it: the slide previews hide while navigation and the talk timer stay pinned; <kbd>Esc</kbd> restores.

## Roles & sync

State lives in a **Yjs** document (a CRDT) shared over WebSocket. A small relay (`Hub`) keeps one doc per session and broadcasts changes.

- **Presenter**: drives the slide index; everyone follows. Can moderate plugins.
- **Viewer**: follows the presenter's index; can still interact with plugins (vote, ask, react).

Roles come from an **unguessable token in the URL** (`?t=…`), not a login. A viewer's
client is scoped to match: the **presenter view** (<kbd>p</kbd> / `#presenter`) and the
**overview grid** (<kbd>o</kbd>) are presenter-only: they stay disabled and dimmed in the
shortcut help (<kbd>?</kbd>) for viewers, so the speaker notes and plugin consoles aren't
reachable from a shared audience link.

## Where code runs
**Decks are trusted:** Building and running a deck executes its code, including any **server plugins**, on *your* machine. A trust warning is printed before a deck loads. Only run decks you trust.

- A plugin's **client** part does WS + shared-state only and runs in every browser.
- A plugin's optional **server** part runs **once**, on the machine that started `liebstoeckel live`, never in the audience's browsers.

## Going beyond the LAN

`liebstoeckel live` is LAN-first. To reach viewers over the public internet, point it at a **relay**:

```bash
liebstoeckel live my-talk --relay https://relay.example.com --relay-token <token>
```

[The relay](https://docs.liebstoeckel.app/guides/relay/)
[Plugins overview](https://docs.liebstoeckel.app/plugins/overview/)