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.
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/[email protected]/build/scribe-music/element.css" />
<script type="module" src="https://cdn.jsdelivr.net/gh/stephband/[email protected]/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>
Scribe consumes Sequence JSON. Here's an example of the horn part for So What 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:
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 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 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.
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"
- "sequence"
Both an attribute and a property. The URL of a JSON file, or a hash reference to a script element in the document.
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";
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.
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.
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";
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;
A boolean attribute. Displays swung and triplet 8ths as straight 8ths.
Set a .data
object, structured as a Sequence.
let scribe = document.body.querySelector('scribe-music');
scribe.data = {
name: 'My Song',
events: [...]
};
Get Scribe's internal data object, whose structure is a Sequence.
To export Sequence JSON, simply stringify scribe.data
:
let scribe = document.body.querySelector('scribe-music');
let mySong = JSON.stringify(scribe.data);
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).
make modules
There is a discussion about using CSS grid layout for rendering music notation in the blog post Printing Music with CSS Grid. Scribe's internals have changed since that post was written but the principal layout technique remains the same.
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 straight 8ths - Setting
settings.swingAsStraight16ths
makes 16th tuplets display as straight 16ths <scribe-music>
swing
attribute corresponds to.swingAsStraight8ths
- Triplet detection and rendering
- Supports nested sequences to level 2 nesting
- Bar divisions, note, beam and rest splitting on divisions
- In-bar and cross-bar ties
- Adds support for SMuFL fonts
- Proof of concept
Developed at Cruncher by Stephen Band.
Rich Sigler of Sigler Music Fonts jazzfont.com very kindly granted permission to use JazzFont shapes as SVG paths in this project. Scribe now renders SMuFL fonts so SVG shapes are no longer used.
Scribe logo/mascot by Mariana Alt.
Gavin Band dreamt up probabalistic key centre analysis.
ABC parser borrowed from ABCjs.
Code contributions: Halit Celik.