SkillAgentSearch skills...

Scribe

Renders music in HTML.

Install / Use

/learn @stephband/Scribe
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<img src="https://stephen.band/scribe/logo.png" style="float: right;" />

Scribe

Responsive music notation for the web.

Scribe takes a sequence of events – notes, chords, meter changes and so on – and interprets and renders notation in HTML and CSS.

Documentation and examples at stephen.band/scribe.

<scribe-music>

To use the <scribe-music> custom element, import the CSS and JS from the CDN:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/stephband/scribe@0.4.2/build/scribe-music/element.css" />
<script type="module" src="https://cdn.jsdelivr.net/gh/stephband/scribe@0.4.2/build/scribe-music/element.js"></script>

The <scribe-music> element is now registered. It renders music notation from JSON data imported via its src attribute:

<scribe-music src="/path/to/json"></scribe-music>

Alternatively the src attribute may reference JSON already in the document:

<!-- Head -->
<script type="application/json" id="so-what">{
    "events": [...]
}</script>

<!-- Body -->
<scribe-music src="#so-what" swing></scribe-music>
<!--Here's another example: <a href="https://stephen.band/scribe/dolphin-dance">Dolphin Dance</a>.-->

Scribe data

Scribe consumes <a href="https://github.com/soundio/music-json/">Sequence JSON</a>. Here's an example of the horn part for <a href="https://www.youtube.com/watch?v=ylXk1LBvIqU&list=RDylXk1LBvIqU">So What</a> as a sequence document:

{
    "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", 32],
            [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]
        ]
    }]
}

The main sequence plays the "Section" sequence four times, 32 beats (or 8 bars) per section, with the third section transposed up 1 semitone. The "Section" sequence plays the "Horns" phrase four times, 8 beats (or 2 bars) per phrase. That renders as:

<img src="https://stephen.band/scribe/assets/images/so-what.png" alt="Scribe rendered music for So What" style="width: 100%; height: auto;" />

Scribe detects when bars are repeated and automatically inserts bar repeat symbols.

The only property actually required by a sequence is an "events" array.

Events

Events are described in the "events" array. Each event is an array that starts with [beat, type, ...] and each type carries some data. Scribe supports these event types:

| beat | type | 2 | 3 | 4 | 5 | | :----- | :----------- | :--- | :--- | :--- | | beat | "chord" | root | mode | duration | bass | | beat | "text" | text | duration | | | | beat | "note" | pitch | dynamic | duration | | | beat | "meter" | duration | divisor | | | | beat | "rate" | number | | | | | beat | "sequence" | id | - | duration | transforms... | | beat | "key" | notename | | | |

Unrecognised event types are ignored.

Sequences

Sequences are described in the "sequences" array, and "sequence" events refer to them by id. Child sequences have the same structure as parent sequences. Events may refer to sequences in the same sequence or to any sequence found in their parent chain. Sequences are arbitrarily nestable.

Scribe considers the top-level sequence to describe musical structure by convention. In the example above the top-level sequence contains the key, meter and musical sections as "sequence" events. Scribe renders double bar lines at the end of each of these.

<!-- Scribe also parses a shorthand version of this format intended for quick hand authoring, basically Sequence JSON structure with all the JSON syntax removed. ```html <scribe-music type="sequence" clef="treble" meter="4/4"> 0 chord Dmaj 4 0 F#5 0.2 1 0 A4 0.2 1 0 D4 0.2 1 </scribe-music> ``` <!--Scribe 0.3 also parses ABC (thanks to the parser from [ABCjs](https://github.com/paulrosen/abcjs)).-->

type="json"

Both an attribute and a property. Mimetype or type of data to fetch from src or to parse from text content. Scribe supports 3 types of data:

  • "application/json", or just "json"
<!-- - "text/x-abc", or just "abc" -->
  • "sequence"

src="url"

Both an attribute and a property. The URL of a JSON file, or a hash reference to a script element in the document.

clef="treble"

Both an attribute and a property. The name of the clef, one of "treble", "treble-up", "treble-down", "alto", "bass", "piano", "drum" or "percussion". Defaults to "treble".

<scribe-music clef="bass">...</scribe-music>
let scribe = document.body.querySelector('scribe-music');
console.log(scribe.clef) // "bass";

layout="compact"

The layout attribute can have one of the values "compact" (the default), "regular" or "spaced". Layout does nothing JavaScript-y, it simply sets the flex-basis and min/max-width of the bars for more regular spacing inside their container.

key="C"

Both an attribute and a property. Gets and sets the key signature of the stave. Accepts any chromatic note name, spelled with unicode sharps and flats or with hash # and small case b. This is the name of the tonic of a major scale. Defaults to "C".

<scribe-music key="F#">...</scribe-music>
let scribe = document.body.querySelector('scribe-music');
scribe.key = "B♭";

There is no provision for choosing a 'minor' key. Declare its relative major.

The key is the key signature pre-transposition. If transpose is something other than 0, the key signature is also transposed.

meter="4/4"

Both an attribute and a property. The meter, expressed as a standard time signature. This setting is overridden by any meter event found in the data at beat 0. If this attribute is omitted (or the property not set in JS), no time signature is displayed (unless the data contains a "meter" event at beat 0). Defaults to "4/4".

<scribe-music meter="3/4">...</scribe-music>
let scribe = document.body.querySelector('scribe-music');
scribe.meter = "3/4";

transpose="0"

Both an attribute and a property. Sets scribe to render notation transposed by transpose semitones. Transposition is applied to key signature, notes and chords before render, and not to the underlying data.

<scribe-music transpose="2">...</scribe-music>
let scribe = document.body.querySelector('scribe-music');
scribe.transpose = 2;

swing

A boolean attribute. Displays swung and triplet 8ths as straight 8ths.

.data

Set a .data object, structured as a <a href="https://github.com/soundio/music-json/#sequence">Sequence</a>.

let scribe = document.body.querySelector('scribe-music');
scribe.data = {
    name:      'My Song',
    events:    [...]
};

Get Scribe's internal data object, whose structure is a <a href="https://github.com/soundio/music-json/#sequence">Sequence</a>. To export Sequence JSON, simply stringify scribe.data:

let scribe = document.body.querySelector('scribe-music');
let mySong = JSON.stringify(scribe.data);

<!-- ## Sequence format The `"sequence"` format is intended for quick hand-authoring, and not as an export format. The format is basically Sequence JSON events but with all the JSON syntax – commas, quotes, brackets – removed. Values are delineated by whitespace. Any value that does not parse as a number becomes a string. ``` 0 meter 4 1 0 chord D maj 4 0 note F#5 0.2 2 1 note A4 0.2 1 4 note D4 0.2 1 ``` If notes have named pitches (as opposed to numbered pitches), declaring the `note` type is optional. ``` 0 meter 4 1 0 chord D maj 4 0 F#5 0.2 2 1 A4 0.2 1 4 D4 0.2 1 ``` The same is true for chords. ``` 0 meter 4 1 0 D maj 4 0 F#5 0.2 2 1 A4 0.2 1 4 D4 0.2 1 ``` -->

Develop

Clone

To install Scribe locally clone the repo and update the submodules:

git clone https://github.com/stephband/scribe.git scribe
cd scribe
git submodule update --init

To check things are working serve this directory and navigate to http://localhost/scribe-music/index.html (obviously localhost needs to be replaced depending on what you are using as a server).

Build

make modules

Research

There is a discussion about using CSS grid layout for rendering music notation in the blog post <a href="https://cruncher.ch/blog/printing-music-with-css-grid/">Printing Music with CSS Grid</a>. Scribe's internals have changed since that post was written but the principal layout technique remains the same.

Changes

0.4.x – The Lead Sheet

Version 0.4 is capable of rendering a reasonable lead sheet. Features:

  • Supports multiple concurrent notes
  • Supports multiple parts per stave
  • Supports sequence events, top-level sequence events denote musical structure
  • Supports arbitrary nesting of sequences and transforms
  • Tuplet detection up to nonuplets
  • Automatic bar repeat symbols for repeated identical bars
  • New probablistic key-to-spelling detector
  • Adds config object
  • Setting settings.swingAsStraight8ths makes 8th tuplets display as stra
View on GitHub
GitHub Stars657
CategoryDevelopment
Updated3d ago
Forks29

Languages

HTML

Security Score

80/100

Audited on Mar 24, 2026

No findings