Page and system layout
For beginning readers, a layout sidecar can set "notehead_letters": true. This prints the displayed pitch letter inside each pitched notehead: a written B-flat trumpet part labels its written C as C. Whole and half notes keep open bowls, and shorter values use light letters inside filled heads. Rests and percussion heads have no pitch letters. The default is false; the setting changes the printed reading without changing notes, durations, or playback. A lettered head is still one object: the letter is painted inside the notehead it belongs to, so a host's selection and geometry see the same heads, in the same boxes, with the same identities as they do with the setting off.
Start systems and pages at musical places
A system-map lists the first measure of each new system; a page-map lists the first measure of each new page. Measure 1 is implicit. Put the map inside a part when it describes that player's page.
scorescript 0.3
score "Four lines" {
parts {
fl
}
fl {
system-map {m3, m5, m7}
m1-2 {
1C5 | D
}
m3-4 {
1E | F
}
\p
m5-6 {
1G | F
}
m7-8 {
1E | C
}
}
}Canonical spelling
scorescript 0.3
score "Four lines" {
parts {
fl
}
fl {
system-map {m3, m5, m7}
m1-2 {
1C5 | D
}
m3-4 {
1E | F
}
\p
m5-6 {
1G | F
}
m7-8 {
1E | C
}
}
}
system-map {m5 m2} starts a system at measure 5 and requests exactly two semantic measures. A plain 5 starts a system without prescribing its count. When copying a printed part, read each system's first measure number directly. Page starts imply system starts; system starts alone do not imply new pages. Conflicting authored cuts can cause the renderer to decline an exact-count span; inspect the render report instead of treating it as an override. The example requests systems beginning at 1, 3, 5, and 7, with measure 5 starting a new page. The inline preview is flowing SVG; use paginated SVG or PDF output to see page boundaries.
Local cuts
\n starts a system before its containing measure. \p starts a page and system there. Prefer placing either immediately after the preceding barline, before the target measure's first note. A trailing cut before a barline still belongs to the current measure; it does not mean “after this bar.”
| Source within a musical row | Result |
|---|---|
1C5 | D \n | E | New system before D, measure 2 |
1C5 | D | \n E | New system before E, measure 3 |
1C5 | D | \p E | New page and system before E, measure 3 |
Address a cut inside omitted silence by attaching its number, such as \n29. A part's cut belongs to that part. A score-level layout { \n13 \p46 } applies globally.
These are actual backslash letters in the source, not escaped newlines in a programming string. A lone backslash at the end of a physical source line continues the same musical row instead of requesting an engraved break.
Inside parts-first and measures-first music
These two sources request the same notes and player-local cuts. In both, flute starts a system at measure 2 and a page at measure 4; oboe has no player-local cuts.
Parts-first:
scorescript 0.3
score "Part-local cuts" {
parts {
fl
ob
}
fl {
m1 {
1C5
}
\n
m2-3 {
1D | E
}
\p
m4 {
1F
}
}
ob {
m1-4 {
1C4 | D | E | F
}
}
}Canonical spelling
scorescript 0.3
score "Part-local cuts" {
parts {
fl
ob
}
fl {
m1 {
1C5
}
\n
m2-3 {
1D | E
}
\p
m4 {
1F
}
}
ob {
m1-4 {
1C4 | D | E | F
}
}
}
Measures-first:
scorescript 0.3
score "Measure-row cuts" {
parts {
fl
ob
}
m1-4 {
fl {
1C5 | \n D | E | \p F
}
ob {
1C4 | D | E | F
}
}
}Canonical spelling
scorescript 0.3
score "Measure-row cuts" {
parts {
fl
ob
}
m1-4 {
fl {
1C5 | \n D | E | \p F
}
ob {
1C4 | D | E | F
}
}
}
In a parts-first body, system-map {m2} and page-map {m4} can express those same flute boundaries. Do not put these maps inside the flute's measures-first row or invent a map-only detached flute body beside it: those placements are not supported. Use inline cuts in that row, or a score-level map when the intent is genuinely global.
Duration inheritance still applies around cuts
4Eb5 Bb D E | 1D | A | \n 8D D D D 2r | 8Eb G Bb r 2rCanonical spelling
4Eb5 Bb D E | 1D | A | \n 8D D D D 2r | 8Eb G Bb r 2r
The first bar has four quarters. wD changes the inherited duration to a whole note, so the next A is also whole. In measure 4, eD changes it to an eighth; four eighth notes plus hr fill the bar. The \n begins a system before that measure and consumes no duration.
After hr, the inherited duration is a half. Therefore measure 5 needs the explicit e on Eb: three eighth notes, an eighth rest, and a half rest fill 4/4. Writing only Eb G Bb er hr there would overfill the bar. Layout placement and rhythmic validity are independent checks.
Score Page versus Parts Page
| Placement | Combined score | Extracted player part |
|---|---|---|
Score-level map or layout block | Applies globally | Also applies to every extracted part |
| Cut in one player's music | Does not alone force an ensemble break | Applies to that player |
| Same local cut on every present part | Also becomes an ensemble boundary | Applies individually |
| Selected view's layout configuration | Refines that selected score view | Refines that selected part view |
Global and player-local source cuts add together. A part map does not override or remove global cuts. For a full-score-only paper arrangement, use a score view's layout configuration instead of imposing global source cuts that every extracted player will inherit.
A “Score Page” configuration and a “Flute Page” configuration can select different page settings and additional breaks from the same source. A part view is not a parts-first source file; a score view is not a measures-first source file. See source arrangements and output views.
What normalization changes
arrange=parts and arrange=measures reorganize source music. maps=top|inline moves musical context such as meter; it does not toggle system/page maps versus inline cuts. Break formatting independently uses maps for multiple cuts of a kind or exact-count system entries. hoist-breaks can move matching per-part break maps to global scope; it is not a command to discard player-specific layout.
Physical source wrapping changes code readability. Engraved systems and pages follow musical layout directives and the selected output settings, not the width of your code editor. Normalization is an optional source rewrite; choosing another output view need not rewrite the music.
Rest groups
[|] begins a new engraved multirest group at a silent bar. multi-map {m3, m7} records several such points compactly. This splits rest grouping without forcing a system or page break. Keep the map in the same scope as the intended rest grouping.
Instrument brackets and braces
Left-side brackets identify groups of staves in each system. By default, consecutive catalog instruments from the same family share a bracket when there are at least two: for example, flute/oboe together and trumpet/trombone together. A family separated by another family is not joined across it. Unknown instruments and keyboards are not guessed into automatic groups.
Use a layout configuration's staff_groups to customize the marks:
| Value | Result |
|---|---|
Omitted or null | Automatic family brackets |
[] | No grouping symbols (an instrument's own brace stays) |
| Nonempty array | Exactly the specified groups, replacing automatic groups |
Each group has first, last, and symbol ("bracket" or "brace"). Endpoints are exact printed part names, not roster abbreviations or indices. Every staff between the endpoints is included. Groups must span at least two staves in score order. Disjoint and nested groups are allowed; duplicate, crossing, reversed, missing, or ambiguous endpoints are reported rather than silently reassigned.
For parts { fl ob tpt tbn }, this saved configuration explicitly groups the winds and brass:
{
"format": "scorescript-layouts",
"version": 2,
"default": "Ensemble",
"layouts": [{
"name": "Ensemble",
"staff_groups": [
{"first": "Flute", "last": "Oboe", "symbol": "bracket"},
{"first": "B-flat Trumpet", "last": "Tenor Trombone", "symbol": "bracket"}
]
}]
}
Save it beside piece.scorescript as piece.engraving.json. Use the actual compiled part names when your roster has numbered players or custom labels.
A two-staff keyboard or harp braces itself. Two consecutive parts of one keyboard or harp whose names differ only by a trailing number — part piano: Piano 1 and part piano: Piano 2, which is how a MusicXML grand staff imports — are one instrument called Piano: one label centred on the pair, one brace, barlines through both hands, and Piano names both staves when a part is extracted. A staff_groups entry naming the same pair says the same thing and is folded in. Two pianos named apart (Piano I, Piano II) stay two instruments. A brace does not create a second staff, assign hands, or alter playback.
Empty staves
A choir-and-piano score opens with bars of piano alone; the printed page drops the silent choir staves from those systems and brings them back on the line where the singers enter. Four configuration keys govern it:
| Key | Meaning |
|---|---|
hide_empty_staves | Cut away a staff that prints nothing on a whole system. Off unless you say so. |
hide_empty_staves_first_system | Let the first system cut staves away too. Off: the first line shows the whole ensemble. |
staff_visibility | Per staff, by printed part name: "auto" follows the default, "always" never goes, "cutaway" goes even when the default is off. |
staff_visibility_overrides | [{"part": "SA", "at": "m19", "show": true}] — show or hide one staff on the system that holds that bar, wherever the bar lands. |
Emptiness is judged across the whole laid-out line, and again after every edit or break: a note, a cue, a syllable, a dynamic, an instruction or a hairpin anywhere on the line keeps the staff; rests do not. A grand staff's hands stay together unless an override names one of them, and a line always keeps at least one staff. Tempo, rehearsal marks and measure numbers move to the top staff the line still draws. Nothing about the music changes: the bars, their time, the playback and the lyrics are all still there.
{
"format": "scorescript-layouts",
"version": 2,
"default": "Choir and Piano",
"layouts": [{
"name": "Choir and Piano",
"hide_empty_staves": true,
"staff_visibility": { "Piano 1": "always" }
}]
}
Grouping is output layout, not an exercise section or a shared-music reader. Full-score groups are removed when extracting one part. Both flowing SVG and PDF draw the same group marks; source notes and identities are unchanged.
Paper and engraving settings
The score source carries musical layout intent and may carry named paper and engraving settings in an engraving declaration (see below). An optional name.engraving.json beside name.scorescript remains available for separately selected configurations. It can refine page size, margins, staff size, spacing, and the selected part. A score still opens without the sidecar. Use the editor's layout controls to create a configuration rather than guessing JSON property names.
The sidecar was called name.layouts.json before 2026-09-18, and files under that name keep opening: if name.engraving.json is not there, the old file is read and the tool says which one it read; if both are there the new one is read and the old one is named as stale. A save always writes name.engraving.json, and nothing ever renames or deletes the old file — that is yours to keep or remove. What is inside the file did not change.
Vertical air and beam seating are settings there too, each defaulting to the shipped house: dynamics_take_gap_below decides whether a dynamic under a staff spends the gap below it (true) or is measured clear of it and pushes the next staff down by its own height (false); beam_keep_contour keeps a beamed group's melodic direction through seating; and beam_ledger_clearance is the air a beam leaves before the first ledger line. beam_continuation_length sets the reach of an unfinished beam at a system break, in staff spaces (default 1.5, range 0.25–6). It changes the printed reach without changing which beam levels continue. docs/HOSTS.md lists every key with its range.
How a selection is dressed is settled there too, and it is a CLOSED set of four rows: selection_edge (with selection_edge_width and selection_edge_colour), selection_fill (selection_fill_colour and selection_fill_opacity), selection_shadow, and selection_recolour (none, heads or all). The house is the flat card with a one-pixel #1f6feb edge, no shadow, and the chosen ink recoloured. A value outside its range is refused by name. Nothing else about the selection is settable: its SHAPE is the language's, not an option — one region that hugs the chosen ink, for one note and for a passage alike — and no host may replace it, supply its own paint, or move the selection off the engine's geometry. None of these keys reaches the engraver: a selection is never printed.
Layout affects the printed view. It does not change the pitches, durations, or identities in the music. Always inspect the exported pages: a source system map alone does not prove good spacing or correct pagination.
Manual placements
The engine engraves; you correct. A correction — a title nudged up, a slur flipped below, a hairpin's end pulled back — is filed in the same name.engraving.json, inside the layout it was made in, under placements. The source never grows placement syntax: the notation is the music, and where a mark sits on a particular page is a property of that page.
{
"format": "scorescript-layouts",
"version": 2,
"layouts": [
{ "name": "Concert Score" },
{
"name": "Trombone",
"part": "Trombone 1 & 2",
"placements": {
"score/title/0": { "dy": -1.5 },
"p2/Trombone 1 & 2/m8/v9/3/slur/0": { "side": "below" },
"p2/Trombone 1 & 2/m12/v1/0/hairpin/0": {
"start": { "dx": 0.5, "dy": 0 },
"end": { "dx": -0.5, "dy": 0 }
}
}
}
]
}
The key is the engine's own object id, {owner}/{role}/{ordinal}. Ask the engine for one rather than composing it by hand:
scorescript geometry piece.scorescript --objects
scorescript render piece.scorescript --bounds -o outlined.svg
The first prints one line per object — id, role, page, ink box, anchor. The second draws every ink box over the music so you can see what a key names.
A placement may say five things and nothing else:
| Key | Meaning |
|---|---|
dx, dy | Move the whole object, in staff spaces, y down |
start, end | Move one end of a two-ended object, each { "dx": …, "dy": … } |
side | above or below, for a slur, tie or tuplet bracket |
Placements are per LAYOUT: a nudge on the Trombone part never moves the score. They are applied AFTER automatic placement, as the final offset, so a correction survives every re-render and every edit that leaves the id stable. A key naming an object the render no longer has is ignored and reported as placement.unknown-id info; it never moves something else, and the file keeps it so an undo brings the move back.
Engraving inside one source file
A score can own named engraving configurations. A fresh native CLI, engine session, MCP render, or WASM preview uses the declaration marked default (the first declaration if none is marked). No sidecar is needed.
scorescript 0.3
score "Portable study" {
engraving "Print" default {
page "letter"
staff_size_mm 8
margin_mm 15
spacing_quarter 2.2
spacing_slope 1.35
system_gap 1.5
title_font_family "serif"
show_measure_numbers false
}
meter 4/4
parts {
tpt
}
tpt {
m1 {
4C4 D E F
}
}
}Canonical spelling
scorescript 0.3
score "Portable study" {
engraving "Print" default {
page "letter"
staff_size_mm 8
margin_mm 15
spacing_quarter 2.2
spacing_slope 1.35
system_gap 1.5
title_font_family "serif"
show_measure_numbers false
}
meter 4/4
parts {
tpt
}
tpt {
m1 {
4C4 D E F
}
}
}
staff_size_mm measures the full height of a five-line staff (four spaces). margin_mm measures the page margin. The existing staff_space_pt and margin_pt spellings remain available; writing both units for the same setting is refused. All other settings use the native layout schema names and ranges. Text is quoted, booleans are true/false, lists use [ ... ], and records use { ... }. The formatter retains these declarations and their comments.
Within a declaration, movement_overrides { movement-id { exercises_per_page 3 } } changes settings for an authored movement id. Overrides inherit the named configuration and cannot change the selected part or pitch. A displayed movement number is resolved to its authored id before its settings are applied. Unknown keys, unknown movement ids, duplicate fields or names, multiple defaults, invalid values, and nested engraving declarations report engraving.invalid and refuse rendering.
--layout "Print" selects a named declaration; explicit PDF page, part, and pitch flags can override the opening source default. Existing explicitly selected sidecars remain available. Engine hosts can use EngravingSelection::Named or Host; WASM options use engraving: "Print" or host_engraving: true. Screen zoom, colors and event identity remain host controls. Source-owned engraving changes presentation only; it does not change performed pitches or authored notes.