Technical specifications for Scribe: Sequence documents, shorthand grammar, automatic repeat engraving, live DOM properties, and key/clef conventions.
Scribe consumes sequence data.
A sequence is an object with an events property, an array of events.
An event has the form [beat, type, ...data].
In Scribe, 1 beat is a quarter note.
A sequence may also have a sequences property, an array of nested sequences.
A nested sequence has the same sequence structure, and must have an id.
As an example, here are the first four bars of Herbie Hancock's Dolphin Dance as Sequence JSON:
{
"name": "Dolphin Dance",
"events": [
[0, "key", "Eb"],
[0, "meter", 4, 1],
[0, "sequence", 1, 0, 16]
],
"sequences": [{
"id": 1,
"name": "Intro",
"events": [
[0, "chord", 3, "∆", 4],
[2, "note", 67, 0.1, 0.5],
[2.5, "note", 68, 0.1, 0.5],
[3, "note", 70, 0.1, 0.5],
[3.5, "note", 65, 0.1, 3.5],
[4, "chord", 10, "-", 4],
[8, "chord", 3, "∆", 4],
[10, "note", 67, 0.1, 0.5],
[10.5, "note", 68, 0.1, 0.5],
[11, "note", 70, 0.1, 0.5],
[11.5, "note", 65, 0.1, 3.5],
[12, "chord", 2, "ø", 2],
[14, "chord", 7, "7alt", 2]
]
}]
}
What's going on here? The main events array declares the key and the meter at beat 0, then cues the "Intro" sequence to 'play' for 16 beats.
The intro sequence contains the melody and chords.
The result renders as:
Scribe understands the following event types:
| beat | type | data | |||
|---|---|---|---|---|---|
beat |
"chord" |
root | number |
extension |
duration |
|
beat |
"key" |
name | number |
|||
beat |
"meter" |
beats |
divisor |
||
beat |
"note" |
name | number |
dynamic |
duration |
|
beat |
"sequence" |
id |
target |
duration |
transforms |
beat |
"text" |
duration |
string |
Unrecognised types are ignored.
Sequence events may carry transforms for the sequences they cue:
[0, "sequence", 1, 0, 16, "transpose", -3, "rate", 2]
This renders as:
Notice that all 16 beats are still rendered – the duration of the event itself is not transformed – but the rate of the melody is transposed down a minor third and is twice as fast. Transforms are applied to sequences in the order they are declared. Scribe supports the transforms:
| transform | data |
|---|---|
"displace" |
float (beats) |
"gain" |
float (gain) |
"rate" |
float (playback rate) |
"transpose" |
integer (semitones) |
For multi-part and multi-stave scores, see the multi-stave scoring documentation. Some examples of sequence JSON can be found in the repository's data folder.
Scribe consumes Sequence data. The only strictly required property is an "events" array.
By convention, Scribe treats the top-level sequence as describing the overarching formal structure of the composition.
Child "sequence" events placed in the top-level array cue movements, sections, or phrases defined in the "sequences" array.
Scribe automatically renders double bar lines at the end of each cued section:
{
"name": "So What",
"author": { "name": "Miles Davis" },
"events": [
[0, "key", "C"],
[0, "meter", 4, 1],
[0, "sequence", 1, 0, 32],
[32, "sequence", 1, 0, 32],
[64, "sequence", 1, 0, 32, "transpose", 1],
[96, "sequence", 1, 0, 32]
],
"sequences": [{
"id": 1,
"name": "Section",
"events": [
[0, "sequence", 2, 0, 8],
[8, "sequence", 2, 0, 8],
[16, "sequence", 2, 0, 8],
[24, "sequence", 2, 0, 8]
]
}, {
"id": 2,
"name": "Horns",
"events": [
[0, "chord", "D", "-7", 8],
[2, "note", "B4", 0.1, 1.6],
[2, "note", "G4", 0.1, 1.6],
[2, "note", "D4", 0.1, 1.6],
[2, "note", "A3", 0.1, 1.6],
[3.6, "note", "A4", 0.1, 0.4],
[3.6, "note", "F4", 0.1, 0.4],
[3.6, "note", "C4", 0.1, 0.4],
[3.6, "note", "G3", 0.1, 0.4]
]
}]
}
Rather than writing out verbatim identical measures, Scribe detects repeated bar patterns across sequences and automatically replaces them with standard bar repeat symbols.
Repeat replacement rules:
"AAAA" renders as "x %1 %1 %1")."ABABAB" renders as "x x - %2 - %2")."ABCDABCD" renders as "x x x x - - %4 -").In the 8-bar phrase below, the 2-bar "Horns" motif repeats across bars 3–8. Scribe preserves the first statement and places 2-bar repeat signs for the rest:
"text/sequence")
For rapid authoring in HTML documents, Scribe parses a whitespace-delimited shorthand format embedded inside <script type="text/sequence"> elements:
<script type="text/sequence" id="tune">
0 key D
0 meter 4/4
0 D4 1
1 F#4 1
2 A4 1
</script>
<scribe-music src="#tune"></scribe-music>
Shorthand type inference:
D4, F#4, or Bb3 automatically resolve as note events without the "note" keyword.Cmaj7, D-7, G7alt) is inferred directly without requiring explicit "chord" keywords.[beat] [token] [duration] or keyword declarations like 0 key D and 0 meter 4/4.
The <scribe-music> element reflects all attributes as live JavaScript DOM properties.
Querying and mutating these properties triggers an immediate, reactive re-render:
| property | attribute | type | description |
|---|---|---|---|
.src |
src |
URL | string |
URL of sequence file or selector to an inline script (e.g. "#tune") |
.type |
type |
string |
Mimetype: "application/json" (or "json"), "text/sequence" |
.staff |
staff |
string |
Clef/stave configuration (defaults to "treble") |
.key |
key |
string |
Tonic of major key signature (e.g. "C", "F#", "B♭") |
.meter |
meter |
string |
Time signature (e.g. "4/4", "3/4", "6/8") |
.beat |
beat |
number |
Starting beat from which notation rendering begins (defaults to 0) |
.duration |
duration |
number |
Total duration in beats to render (defaults to Infinity) |
.transpose |
transpose |
number |
Pitch and key transposition offset in semitones |
.swing |
swing |
boolean |
Boolean flag; displays swung 8ths as straight 8ths |
.shuffle |
shuffle |
boolean |
Boolean flag; displays swung 16ths as straight 16ths |
.data |
— | object |
Reactive sequence data object; mutating this immediately triggers a re-render |
Attributes and properties can be manipulated imperatively via standard DOM methods or direct property assignment:
// Query the music element
const scribe = document.querySelector('scribe-music');
// Inspect reflected properties
console.log(scribe.staff); // "treble"
console.log(scribe.key); // "C"
// Programmatically update properties (triggers dynamic re-render)
scribe.key = "B♭";
scribe.meter = "3/4";
scribe.transpose = 2;
scribe.staff = "bass";
// Push events into the reactive data structure
scribe.data.events.push([12, "note", "C4", 0.1, 1]);
Scribe has no provision for declaring minor key signatures directly (e.g. setting key="Dm" is not supported).
Always declare the relative major tonic (e.g. use key="F" for D minor, or key="Eb" for C minor).
Key names accept both Unicode accidentals ("♯", "♭") and ASCII symbols ("#", "b").
The key attribute sets the pre-transposition key signature.
When transpose is specified (e.g. transpose="2"),
the key signature is automatically transposed alongside all notes and chord symbols before engraving.
The staff attribute accepts standard clefs as well as octave-transposing clefs:
"treble" (default): Standard G-clef on second line."treble-up": Treble clef 8va (sounds one octave higher than written)."treble-down": Treble clef 8vb (tenor vocal clef, sounds one octave lower than written)."alto": C-clef on third line."tenor": C-clef on fourth line."bass": Standard F-clef on fourth line."grand": Braced piano stave combining treble and bass staves."drum" / "percussion": Neutral percussion clef with unpitched drum kit mappings.