Say hello to Scribe, an SVG music renderer for the web

Stephen Band

The web is great at displaying text, but for displaying music our options are limited. Scribe is a JS library that aims to make it easy.

In the last couple of months I've been working on a library that renders music notation in web pages. The project started as a need for a 'notation view' in my web-based MIDI monitor at sound.io/midi-monitor. It's still very much in alpha, but as it is already capable of rendering a decent lead sheet, I want to show it off.

There is already one noteable library that renders music, VexFlow. It uses its own neat little data format, but the data is essentially a series of graphic instructions about how to display notes. It is a music engraver.

Scribe is a music interpreter. It renders music notation by interpreting musical data. As input it takes a sequence of notes and chords, as output it renders written notation. It's a view layer for an audio app, or it's a means of writing transcriptions into a blog post.

Here’s a rundown of some of Scribe's features and what it aims to do. If you have any suggestions, let me know in the comments, and if you are interested in contributing you can fork the project at github.com/stephband/scribe. I'm particularly interested in adding transcriptions of tunes to test against. I should warn you, at this point documentation is scarce!

Scribe consumes JSON

Scribe consumes JSON data in the form of an array of events. The format of an event is loosely based on the OSC specification's notion of a 'bundle'. It should be fast to convert Scribe data to and from OSC-like binary data for sending over the wire via WebSockets, or to MIDI for sending to a MIDI instrument.

An event is an array that represents either a note or a chord. All events have a timestamp, a duration and a type, followed by data specific to that event.

[1.5, 0.5, "note", 60]

In Scribe Alpha, timestamps are described in beats, where a time of 0 is the first beat of the sequence, and duration of 1 represents one crotchet or quarter-note. The above event describes a middle ‘C’ eighth note on the ‘and of two’. Talking about music can be even harder than writing it as there are many ways to express the same thing. The ‘and’ of two can be thought about by counting “one and two and three and four and”. You could also describe it as “a quaver upbeat to the third beat”.

In a future release Scribe will interpret millisecond timestamps into beats using some form of beat interpreter.

Scribe interprets keys

Here's some data for the first few bars of the head of Goodbye Pork Pie Hat:

[0,  2, "chord", "C-7"],
[2,  2, "chord", "G♯∆(♯11)"],
[4,  2, "chord", "D♭∆(♯11)"],
[6,  2, "chord", "G♭∆(♯11)"],
[8,  2, "chord", "B♭7sus"],
[10, 2, "chord", "A♭7(♯11)"],
[12, 2, "chord", "B♭7sus"],
[14, 2, "chord", "C7"],

[0,     0.5,  "note", 72],
[0.5,   0.5,  "note", 75],
[1,     0.5,  "note", 72],
[1.5,   0.5,  "note", 75],
[2,     0.5,  "note", 77],
[2.5,   1,    "note", 75],
[3.5,   0.25, "note", 72],
[3.75,  0.25, "note", 70],
[4,     0.5,  "note", 72],
[4.5,   1,    "note", 75],
[5.5,   0.25, "note", 72],
[5.75,  0.25, "note", 70],
[6,     1,    "note", 72],
[7.75,  0.25, "note", 67],
[8,     0.5,  "note", 72],
[8.5,   0.5,  "note", 75],
[9,     0.5,  "note", 72],
[9.5,   0.5,  "note", 75],
[10,    0.5,  "note", 78],
[10.5,  1,    "note", 77],
[11.5,  0.25, "note", 72],
[11.75, 0.25, "note", 70],
[12,    0.5,  "note", 72],
[12.5,  1,    "note", 75],
[13.5,  0.25, "note", 72],
[13.75, 0.25, "note", 70],
[14,    1,    "note", 72]

Which renders as:

Scribe takes a best guess at how to spell the notes using a Hidden Markov Model to determine the current key center. You can see this in action above, where Scribe is choosing to spell the notes using flats even though nothing in the data is telling it to do so.

Now, look again at that second chord. In the data I have deliberately spelled it as G♯∆(♯11). Because Scribe knows what key we are in – and because it understands a little about how to read chords – it spells that chord correctly as A♭∆(♯11).

Beams, ties and line wraps

Scribe automatically beams together quavers, semi-quavers and so-on provided they don't cross 'invisible' bar lines. Where they do, or where they cross bars, it will tie notes together. It also handles justification and line wrapping. Scribe provides options to allow you to control these features and others:

{
    beamBreaksAtRest: true,
    beamGradientMax: 0.25,
    beamGradientFactor: 0.25,

    clef: 'treble',
    clefOnEveryStave: false,
    barsPerStave: 4
}

So what doesn't Scribe do?

Here are a few things that Scribe does not yet master, but will soon:

Rendering a lead sheet

I said at the top of this post that Scribe is capable of rendering a typical lead sheet. I transcribed Herbie Hancock’s Dolphin Dance to test Scribe. It’s long-ish, moves through several key centres, requires notes to be tied, beamed, dotted, and has complex chords. Plus I love the tune. You can see that here: labs.cruncher.ch/scribe/.

Any suggestions, please leave them in the comments.

Comments

Post a comment