Prose

Prose and code

Markdown and CMS output styled with one wrapper class, a per-theme code palette for Prism, highlight.js and Shiki class names, and GitHub-style callouts (PLAN.md §22). Everything inside .prose below is plain HTML as a Markdown renderer writes it.

Article (.prose)

Calling a show from a laptop

Most stage managers learn to call cues from a paper prompt book, a pencil and a headset. Moving that book onto a screen changes less than people fear and more than they expect: the timing stays the same, but the book can now talk to sound, lighting and video directly.1 This guide covers what changes, what doesn't, and the shortcuts worth learning on day one.

Press Space to fire the next cue, Shift+Space to fire it without advancing the playhead, and Esc to stop everything. The cue list is saved as show.cues.json next to the prompt book, and every change is synced to the other operators within a second. SM, DSM and ASM each get their own view.

Before the first rehearsal

Set the book up the way you would a paper one: script on the left, cues on the right, standby lines a page ahead of the GO. Then work through this list:

  1. Import the script as a PDF or plain text.
  2. Create one cue list per department:
    • Lighting (LX), numbered by the board operator
    • Sound (SQ), with playback cues and mic scenes
      • Playback: one cue per file
      • Scenes: one cue per mic snapshot
    • Video and automation
  3. Agree the standby wording with every operator before tech.

A paper habit worth keeping: write the cue number and the visual or line it hangs off. If the network goes down, the book still has to be readable.2

The book is the show. If it is not in the book, it did not happen.

— A production stage manager, on the first day of tech

Note

Cue numbers are text, not numbers: 12.5 and 12A both sort after 12.

Tip

Colour each department's standby line the same as its operator's cue light, so a glance tells you who is waiting.

Important

Back up the book before every performance. The sync is not a backup.

Warning

Renumbering cues during a run renumbers them for every operator at once.

Caution

Never fire automation cues from a laptop running on battery saver: the network card sleeps.

Cue types at a glance

Standby
A warning to an operator that a cue is coming. Always acknowledged.
GO
The command that fires a cue. Said once, on the beat.
Follow
A cue that fires itself after the previous one, with a delay in seconds.

Timing by department

Fade times from the last production week. The table has no wrapper: it scrolls sideways on its own when the cells can't wrap any further.

CueDepartmentTriggerFade (s)Follow (s)OperatorNotes
LX 12Lighting"Into the woods"3.0—Board 1Preheat specials
SQ 4SoundVisual: door opens0.52.0A1Wind loop
VID 2VideoWith LX 121.5—Media serverForest projection, layer 3
FLY 7Automation"Look, the moon"8.0—FlysMoon in, deadhead 2

The same data in a .table-wrap, the form to use when the renderer can add one:

OperatorCuesStandbys
Board 1LX 1–4831
A1SQ 1–2218

The prompt desk

A prompt desk in front of a stage set: a moonlit forest backdrop above a dark desk with a lit cue screen.
The prompt desk, stage left. Wide images run past the text measure to the container's width.

Firing cues from code

The desk exposes a small API. This block was highlighted by Prism (.token.* classes):

// Fire a cue and wait for every department to confirm.
import { connect } from "@cuelab/desk";

const desk = await connect({ show: "into-the-woods", retries: 3 });

export async function go(cue, { dryRun = false } = {}) {
  if (!desk.has(cue)) throw new RangeError(`No cue ${cue}`);
  return desk.fire(cue, { dryRun, timeout: 1500 });
}

And a footnote-free paragraph before the end, with a link to the code palette and struck and inserted text.

What if the network drops mid-show?

Every operator's laptop keeps a full copy of the book. Cues still fire locally; the sync catches up when the network returns.


Questions about this guide go to the production office.


  1. Through OSC and MIDI show control, or the desk's own network protocol. ↩︎

  2. Print the book on the morning of first preview and keep it at the desk. ↩︎

Code palette (Prism, highlight.js, Shiki)

One palette, three libraries' class names. Each block below is hand-written HTML in the shape that library emits; none is loaded on this page. Themes set --code-keyword, --code-string, --code-comment and the rest.

highlight.js (.hljs-*)

# Count standbys per operator
from collections import Counter

@dataclass
class Cue:
    number: str
    operator: str
    standby: bool = True

def standbys(cues):
    return Counter(c.operator for c in cues if c.standby)

print(standbys(load("show.cues.json"))["A1"] * 2)

Shiki (css-variables theme)

type Department = "LX" | "SQ" | "VID";

// Standby lines sit one page ahead of the GO
const STANDBY_PAGES = 1;

function standbyPage(goPage: number) {
  return Math.max(1, goPage - STANDBY_PAGES);
}

Prism markup (language-html)

<button class="btn btn-primary" data-cue="LX 12">
  GO
</button>
<!-- one GO per cue -->

highlight.js CSS (language-css)

html[data-theme="my-theme"] {
  --code-keyword: #c792ea;
  --code-string: #c3e88d;
  --code-comment: var(--muted);
}

Code blocks: header, copy, line numbers, highlighted and diff lines

.code-block adds a header with the file name and a copy button (the app copies and sets .is-copied; both labels share one cell so the width never changes). Line numbers come from .line-numbers on the pre over line spans, or from Prism's own plugin markup.

standby.ts — Shiki, lines 6 and 7 highlighted
import { cues } from "./book";

export function nextStandby(page: number) {
  for (const cue of cues) {
    // standby one page early
    if (cue.page - 1 === page)
      return cue;
  }
}
book.ts — Shiki diff notation
export const show = {
  title: "Into the Woods",
  title: "Into the Woods (tour)",
  venues: 10,
  cues: 214,
};

Prism line-numbers plugin

const fade = 3.0;
const follow = null;
desk.fire("LX 12", { fade });

Diff: highlight.js and Prism

@@ -3,3 +3,3 @@
 fade: 3.0
- follow: 2.0
+ follow: 1.5
@@ -1,2 +1,2 @@
-operator: A2
+operator: A1

Callouts outside prose

The .markdown-alert-* classes work anywhere, and so does the Obsidian-style blockquote[data-callout].

Warning

The wireless channel plan changes at the next venue. Re-scan before the half.

Tip

Name cue lists after the department, not the operator: operators change between tours.