Skip to main content
Recipe number 165

Cookbook · Chapter 6 · Boxes & notes

MEI music examples with a critical apparatus in the notes

MEI 5 fences engraved in the text at a 5 mm staff, variants encoded as app, lem and rdg, and the apparatus keyed by bar and note in footnotes.

On this page
Output
Canvas · PDF
Postext
Tested with Postext 1.54.0
Needs ≥ 1.54.0 · postext-pdf ≥ 1.54.0
Licence
Updated 22 Oct 2026
Code MIT · Text CC BY 4.0

pp. 2–3 of 5

  • Trim 156 × 234 mm
  • 1 column
  • Crimson Pro 11/14.5
  • Cormorant Garamond
  • Source Sans 3
  • 5 pages
  • Level
  • Postext 1.54.0
  • Laid out in 24 ms
  • 145 lines of code

In short

A chapter of a study of medieval music. Short passages of a 13th-century English round are printed as music in the text, and the notes at the foot of each page record where the old editions disagree, bar by bar.

What you'll build

Chapter 3 of a scholarly study of how English antiquaries read medieval music: five pages at 156 × 234 mm on Sumer is icumen in, the Reading rota, and the editors who scored it between 1776 and 1889. Three passages of the round are engraved in the text in Leipzig at a 5 mm staff, numbered in the bar numbers of W. S. Rockstro's score, and each discussion cites its apparatus at the foot of the page: "b. 40, n. 2 A ] G Hw, Bu". The notes are short and dense in the way an apparatus is, the music sits on the measure of the text, and every reading comes from Rockstro's article in Grove's Dictionary, which the chapter says in its second paragraph.

This recipe answers

  • How do I give music examples a critical apparatus of source variants in footnotes?
  • How do I do footnotes?

The short answer

script.js · lines 36–60in full code
// In the Markdown each example is a ```mei fence. A variant is encoded where it stands,
// <app><lem>…</lem><rdg source="#Hw #Bu">…</rdg></app>: the staff shows the lemma, and
// the file keeps the reading for anyone who opens the encoding. The apparatus entry is a
// footnote cited from the paragraph that discusses the bar: `[^b40]: **b. 40, n. 2** A ] G`.
const scoreStyle = {
  staffSize: mm(5), // running-text examples: 4.5–5.5 mm
  musicFont: 'Leipzig', // lighter than Bravura, close to a 19th-century plate
  textFont: 'Crimson Pro', // the underlay in the book's own face
  barNumbers: 'system', // the apparatus is keyed by bar: number every system
  spacing: 0.95,
  systemGap: mm(4),
  marginTop: pt(4),
  marginBottom: pt(LEAD / 2),
  color: col('ink'),
  minSystemsAtBreak: 2, // never leave one system of an example alone at a page foot
};
const footnotes = {
  placement: 'column', // the foot of the page in one column
  numbering: 'chapter',
  fontSize: pt(8.5),
  lineHeight: pt(10.75),
  hangingIndent: em(1.2), // turnover lines align past the number
  spaceBetween: pt(2),
  separator: { width: 0.2, lineWidth: pt(0.5), color: col('rubric') },
};

MEI examples engraved in the text, their apparatus in footnotes

Ingredients

Type
Crimson Pro, Cormorant Garamond, Source Sans 3 (SIL OFL 1.1)
Assets
None: every picture is drawn in code

Method

#1 · Write each example as MEI, variants included

The settings are the short answer above; the music is in the Markdown, one ```mei fence per example. Where two sources disagree, the measure holds <app> with the adopted reading in <lem> and the other in <rdg source="#Hw #Bu">. The engraver prints the lemma, so bar 40 of Example 3.3 shows the A, and the G of Hawkins and Burney stays in the file for anyone who opens the encoding. Give each fence an alt in the page's language: it is what a screen reader and the tagged PDF read for the score.

#2 · Key the apparatus to the bars the reader sees

barNumbers: 'system' numbers the first bar of every system, and each fence's <measure n="37"> starts the count where the excerpt starts in the edition it follows, so Example 3.3 opens at 37 and a note that says "b. 40" points at a bar the reader can find. The apparatus entry is an ordinary footnote cited from the paragraph that discusses the bar: lemma, closing bracket, variant, sigla in italics. A note cannot be cited from inside a score, so the citation goes on the sentence that names the reading.

#3 · Size the music for running text

A 5 mm staff in Leipzig sits on a 116 mm measure with five or six bars of 3/2 to a system, which keeps the eleven bars of Example 3.3 to two systems. textFont sets the underlay in Crimson Pro, the face of the body, and minSystemsAtBreak: 2 stops a page from ending with one system of an example while the rest goes on. The example label is a third-level heading, so it never stays at the foot of a page without its score.

#4 · Let a page with notes end short

script.js · lines 64–78in full code
const opener = {
  enabled: true,
  minHeight: mm(50), // every chapter's text starts on the same grid line
  slot: { elements: [
    { kind: 'text', id: 'kicker', content: '{attr.kicker}', ...label, fontSize: pt(8.5),
      letterSpacing: pt(1.8), color: col('rubric'), align: 'left',
      placement: at('container', 'top-left', 0, 8) },
    { kind: 'text', id: 'title', content: '{titleText}', fontFamily: 'Cormorant Garamond',
      italic: true, fontWeight: 500, fontSize: pt(29), lineHeight: 1.05, color: col('ink'),
      align: 'left', overflow: 'wrap', // design text ends in an ellipsis by default
      placement: { ...at('#kicker', 'below', 0, 3), size: { width: mm(100), height: 'auto' } } },
    { kind: 'rule', id: 'rule', direction: 'horizontal', thickness: pt(1), color: col('rubric'),
      placement: { ...at('#title', 'below', 0, 5), size: { width: mm(14) } } },
  ] },
};

The opener sinks the text 50 mm under a Cormorant italic title. Below it, a page whose next line would bring a long note with it ends a few lines early, and balancing: { maxLinesPerHeading: 1 } keeps column balancing from pushing that gap above the next section heading, where it would read as a break in the argument.

#5 · Put the book on the verso and the chapter on the recto

script.js · lines 82–99in full code
const head = (id, content, parity, edge, x, extra = {}) => ({
  kind: 'text', id, content, parity, pages: 'body', // never on the opener
  ...label, fontSize: pt(7.5), letterSpacing: pt(1.3), color: col('muted'),
  placement: at('page', edge, x, 13), ...extra,
});
const folio = { color: col('rubric'), fontWeight: 700 };
const header = { elements: [
  head('verso-folio', '{pageNumber}', 'even', 'top-left', MARGIN.outer,
    { ...folio, align: 'left' }),
  head('verso-title', '{title}', 'even', 'top-left', MARGIN.outer + 8, { align: 'left' }),
  head('recto-title', '{chapterTitle}', 'odd', 'top-right', -(MARGIN.outer + 8),
    { align: 'right', overflow: 'ellipsis' }),
  head('recto-folio', '{pageNumber}', 'odd', 'top-right', -MARGIN.outer,
    { ...folio, align: 'right' }),
] };
// The opener carries a drop folio instead, centred under the text block.
const footer = { elements: [{ ...head('drop-folio', '{pageNumber}', 'all', 'bottom', 0,
  { ...folio, align: 'center', placement: at('page', 'bottom', 0, -11) }), pages: 'opener' }] };

Folios in the rubric red on the outer edge, the book on the verso and the chapter on the recto; pages: 'body' keeps them off the opener, which takes a centred drop folio.

The whole recipe

Sandbox
// ═══ Postext Cookbook · Nº 165 · MEI music examples with a critical apparatus ═════
// https://postext.dev/en/cookbook/musicology-mei-apparatus
// Code: MIT · Text: original (CC BY 4.0) · Music: Sumer is icumen in, after Rockstro (PD)
// Fonts: Crimson Pro, Cormorant Garamond, Source Sans 3 (SIL OFL 1.1) · Needs postext ≥ 1.54.0
import {
  buildDocumentWithFonts, renderPageToCanvas, initMusicEngine,
} from 'https://esm.sh/postext';
import { renderToPdf, decompressWoff2 } from 'https://esm.sh/postext-pdf';

const LANG = 'en'; // @lang: the language of the sample document ('en' | 'es')
const RECIPE = 'musicology-mei-apparatus';

// ─── 1 · Design ─────────────────────────────────────────────────────────────
// #region palette: a rubric red for kickers, labels and the notes' rule; ink on cream
const palette = {
  ink: '#211d1a', // text and notation: a warm near-black
  rubric: '#8c2f1f', // the one accent, the red of the manuscript's rubrics
  muted: '#6c645b', // running heads, colophon
  rule: '#cbbfae', // hairlines
  paper: '#fcfaf6',
};
// col(id): a colour linked to its palette entry, with the entry's hex beside the id.
const col = (id) => ({ hex: palette[id], model: 'hex', paletteId: id });
const colorPalette = [
  ...Object.entries(palette).map(([id, hex]) => ({ id, name: id, value: { hex, model: 'hex' } })),
  // The engine's defaults link to main-color: point it at the accent, never the default blue.
  { id: 'main-color', name: 'accent (defaults)', value: { hex: palette.rubric, model: 'hex' } },
];
// #endregion
const MARGIN = { top: 22, bottom: 22, inner: 19, outer: 21 }; // 156 × 234 mm, mirrored
const LEAD = 14.5; // body leading in pt: the baseline grid
const at = (to, edge, x = 0, y = 0) => ({ anchor: { to, edge }, offset: { x: mm(x), y: mm(y) } });
const label = { fontFamily: 'Source Sans 3', fontWeight: 600, textTransform: 'uppercase' };

// #region answer: MEI examples engraved in the text, their apparatus in footnotes
// In the Markdown each example is a ```mei fence. A variant is encoded where it stands,
// <app><lem>…</lem><rdg source="#Hw #Bu">…</rdg></app>: the staff shows the lemma, and
// the file keeps the reading for anyone who opens the encoding. The apparatus entry is a
// footnote cited from the paragraph that discusses the bar: `[^b40]: **b. 40, n. 2** A ] G`.
const scoreStyle = {
  staffSize: mm(5), // running-text examples: 4.5–5.5 mm
  musicFont: 'Leipzig', // lighter than Bravura, close to a 19th-century plate
  textFont: 'Crimson Pro', // the underlay in the book's own face
  barNumbers: 'system', // the apparatus is keyed by bar: number every system
  spacing: 0.95,
  systemGap: mm(4),
  marginTop: pt(4),
  marginBottom: pt(LEAD / 2),

The composed script.js runs as it is: paste it into any page’s module script, or open the recipe on CodePen. Recipe folder on GitHub ↗ (opens in a new tab)

Variations

An apparatus with many entries per system is easier to follow with a number on each bar.

-  barNumbers: 'system', // the apparatus is keyed by bar: number every system
+  barNumbers: 'every',

#Set the examples in Bravura

Bravura's heavier noteheads match a plate-engraved house style.

-  musicFont: 'Leipzig', // lighter than Bravura, close to a 19th-century plate
+  musicFont: 'Bravura',

Pitfalls

  • MEI 5 writes the key signature as keysig="1f" on scoreDef or staffDef. The MEI 4 attribute key.sig is ignored without a message, and the flat disappears.
  • Give every staffDef its lines="5": without it the notes and clefs are engraved on no staff at all.
  • The engraver prints the <lem> of an <app>, not the <rdg>. A variant the reader must see on the page belongs in the apparatus note or in a second example.

Credits

Text
  • The chapter text and the apparatus notes, in English and Spanish · Ignacio Ferro · CC BY 4.0
  • Sumer is icumen in (13th century), the music examples transcribed from W. S. Rockstro's score and the readings his article reports, in A Dictionary of Music and Musicians, ed. George Grove, vol. 3, pp. 765–768, and vol. 4, pp. 1–3 (Macmillan, 1900 printing), as transcribed on Wikisource · Anonymous; William Smyth Rockstro · public domain
  • The Latin rubric of Harley MS 978, f. 11v, and the folio, as quoted in the Wikipedia article (fact check) · Wikipedia contributors · CC BY-SA 4.0
Fonts
Crimson Pro (SIL OFL 1.1) · Cormorant Garamond (SIL OFL 1.1) · Source Sans 3 (SIL OFL 1.1)
SandboxPDF