notation: Sheet Music That Plays
notation writes music out as notes on a staff, and lights the notes as they play. It is for any age and any level: you give it a tune in one line and sheet music appears; you play the tune and each note lights as it sounds; you can play along on a keyboard and it tells you how you did.
It is two libraries with the same names. notation.js is for pictures.js scenes and pynotation is for Pyctures scenes. The sound comes from synths, and the keys on the screen from dials.
A tune, written out
showNotes("E4 D4 C4 D4 E4 E4 E4:2", "quarter", { title: "Mary had a little lamb" })
function draw() {
background("midnightblue")
}That is a whole program.
The notes are written the way playNotes takes them in Notes and tunes: a note is a letter and a number ("C4" is middle C, "F#3", "Bb5"), - is a rest, :2 makes a note last two steps, and "quarter" says how long a step is. The key, the clef and the bars are worked out for you.
It draws clefs, keys, time signatures, notes and rests of every usual length, dots, ties, beams, chords, ledger lines, bar lines and bar numbers, on as many lines as your screen needs.
- In JavaScript there is nothing to import.
showNotesandmakeSheetare ready in every scene. - In Python a scene starts with
from pyctures import *andfrom pynotation import *. That brings the sound with it, so nothing else is needed to hear the page;from synths import *is still wanted forplay_song,make_synthand the rest of the sound names.
In Circuitry both run in the preview beside your code. Sheet music is loaded only by a scene that uses it: a scene that shows no music loads exactly what it always did.
Export as Web Page carries sheet music for a JavaScript scene.
In a recorded video of a scene with sheet music, each note is lit in the first frame at or after its sound begins. A Python scene's page of music in a video is the preview's page scaled to the video's width, so it is a little softer than a JavaScript one.
The first click turns the sound on, as browsers require. A tune that is asked for before that waits, and begins when the sound is on: the line never moves through notes nobody can hear.
The next three steps
Each of these is a whole program too.
Play it, and watch it go
const page = showNotes("C4 C4 G4 G4 A4 A4 G4:2 F4 F4 E4 E4 D4 D4 C4:2", "quarter", { tempo: 100 })
page.onNote = (note) => console.log("now playing", note.name)
function onMouseDown() {
page.play()
}
function draw() {
background("midnightblue")
text("Click here to play", width / 2, height - 30, "white", 18, "centre")
}page.play() plays the music and follows it: the notes that are sounding turn orange, a line moves through the bar, and the page turns when the music reaches the next line. page.onNote is called as each note begins, so your scene can do something on every note. page.stop() stops it. See Playing and following.
Two notes joined by a curved line are one note, held: that is a tie, and it is how a note is written when it lasts across a beat or a bar line. Both light together and you hear one sound.
Follow a song
const song = await loadJSON("songs/night_drive.json")
const page = showNotes(song, { channels: [3, 1] }) // the tune and the bass, a staff each
function onMouseDown() {
page.play() // the page plays the song itself, and follows it
}
function draw() {
background("#0b1020")
text(page.bar ? "bar " + page.bar : "Click to start the song", width / 2, height - 24, "white", 18, "centre")
}A tracker song is turned into written music: you choose which channels to show. page.play() plays the song itself, and the page follows it. Tap a bar and the song jumps there. That is the picture at the top of this page. See A song as sheet music.
Play along
const lead = makeSynth("keys")
const page = showNotes("E4 D4 C4 D4 E4 E4 E4:2", "quarter")
page.playAlong({ mode: "wait", synth: lead })
keyboard(page, { octave: 4, octaves: 1 }) // a dials.js keyboard plays the page
function draw() {
background("#12261e")
text(`${page.result.right} right, ${page.result.wrong} wrong`, width / 2, height / 2, "white", 20, "centre")
}The line waits at each note until you play it. The key to press is lit. A right note turns green; a wrong one shows in red for a moment where you played it. See Playing along.
Outside Circuitry
Inside Circuitry there is nothing to install. On your own:
- notation.js is one file, from the notation.js repository. It comes with pictures.js, in a
notationfolder beside it, and is loaded only by a scene that uses it: save a scene astwinkle.jsand runnpx pictures run twinkle.js. - pynotation, from the pynotation repository, comes with Pyctures and carries notation.js inside it:
pyctures run twinkle.py.
Neither is published on its own yet, to npm or to PyPI, and sheet music is in the next release of pictures.js and of Pyctures. Until then they are used from copies of the repositories.
On a page of your own with no pictures.js, notation.js fills any element:
import { notation } from "./notation.js"
const { showNotes } = notation(document.getElementById("music"))
showNotes("C4 E4 G4 C5:4", "quarter", { at: "fill" })
What's in this section
- What you can show: a tune as text, a list, a sheet you write a note at a time, a song, plain data; and every option.
- Playing and following: play the page, follow a song, and everything a page can be asked.
- Playing along: a learner plays, and the page marks right and wrong notes.
- A song as sheet music: how a tracker song becomes written music, and where it guesses.
- Using it with Python: what is different in pynotation.