Universe

Author: Matthew Davis

This Typst package is used to draw musical scales. This package can be used to write arbitrary notes, but is not intended to be used for entire songs.

Quickstart Examples

Example: D Major Scale

#import "@preview/staves:0.1.0": major-scale
#major-scale("treble", "D", 4)

D Major
Scale

Example: G Minor Arpeggio

#import "@preview/staves:0.1.0": arpeggio
#arpeggio("bass", "g", 2, note-duration: "crotchet")

G Minor
Arpeggio

Example: Custom Notes

#import "@preview/staves:0.1.0": stave
#stave("alto", "c", notes: ("C3", "D#4", "F3"), width: 7cm)

Custom
Notes

Documentation and Examples

A PDF version of this documentation (with slightly better formatting) is available here.

To see a showcase of what this package can do, see this example of a normal scale book: PDF and Typst source code.

Another example is a scale book which is a random mix of all scales: PDF and Typst source code. The purpose of this second one is to provide variety during your scales practices.

Stave

The foundational function is called stave. This is for writing just clefs, clefs and key signatures, or clefs, key signatures and custom notes. Typically as a user you should use the higher-level abstractions such as arpeggio and major-scale (documented further down), if they suit your needs. staves is exposed for creating custom scales which are not yet supported (e.g. broken chords, scales in thirds etc). This documentation section also explains parameters which are common to all functions.

Usage

The arguments are:

clef: (Required) Allowed values are “treble", “bass”, “alto”, "tenor”. Drawing a treble clef above a bass clef, linked as a double-stave (like for a piano) is not yet supported.

key: (Required) Two possible forms.

  • Letter based: Uppercase for major, lowercase for minor, with # or b appended. e.g. "C", "Db", "f#"

  • Number based, with a symbol: “5#” (or “5s”) for 5 sharps, “2b” for 2 flats

notes: An (optional) array of strings representing notes to play sequentially. Chords are not supported. e.g.

  • “C4” is middle C

  • “C5” is the C an octave above middle C.

  • “Db4” or “C#4” is a semitone above middle C

  • “B3” is a semitone below middle C

  • “Bn3” has an explicit natural accidental ♮ infront of it

  • “Fx3” is an F3 with a double sharp, drawn as an 𝄪

    (Formats such as “F##3” to show ♯♯ are not supported yet.)

  • double flats are not yet supported.

notes-per-stave: (Optional) Used to break a long scale over multiple lines. Line breaks will be inserted after every group of this many notes. If omitted, all notes will be placed on the first stave. Page breaks are blocked between staves of the same scale.

note-duration: (optional) Allowed values are “whole", “quarter”, “semibreve”, "crotchet”. Default is “whole” note. All notes are the same duration.

width: (Optional) If provided, sets the length of the stave lines. It omitted (or auto), the stave lines will be stretched to the available space. If the page width itself is auto, a sensible default will be used.

line-sep: (Optional) A length used to set the vertical spacing of the 5 stave lines (within a given stave). Note that this is a length with units, e.g. 3cm, not just 3.

equal-note-head-space: true or false. Defaults to true. If true, note heads will be equally spaced. Some of this space will be taken up with accidentals. If false, adding an accidental to a note will shift the note head further right. true looks better (in my opinion), but false is useful when trying to squish many notes into one stave, to avoid accidentals overlapping with previous note heads.

Examples

To draw just a key signature, omit the notes argument

#import "@preview/staves:0.1.0": stave
#stave("treble", "D")

Here is an example of including notes. Legerlines are supported.

#stave("treble", "F", notes: ("F4",  "C5", "F5", "C6", "F6", "C6", "F5", "C5", "F4"))

Note that accidentals are independent of the key signature. For the example of F major, the key contains B flat. A “B” note will be drawn with no accidental, so it is flattenned by the key signature. A “Bb” will have a redundant flat accidental drawn. “Bn” will have an explicit natural accidental. This behavior may change in future versions.

#stave("bass", "F", notes: ("C2", "B2", "Bb2", "B2", "Bn2"))

The note-duration argument can be used to change the note symbol.

note-duration: whole

whole

note-duration: quarter

quarter

note-duration: semibreve

semibreve

note-duration: crotchet

crotchet

Spacing and Sizing

The notes-per-stave argument can be used to split up long scales into multiple lines.

#major-scale("treble", "D", 4, num-octaves: 2, notes-per-stave: 16)

The width argument can be used to adjust the overall width.

#stave("treble", "f", width: 7cm)

The line-sep argument can be used to adjust the vertical spacing between stave lines. Note that this must be a length (i.e. includes a unit like “cm”, “inches” etc) not just a float.

line-sep: 0.2cm

0.2cm

line-sep: 0.5cm

0.5cm

equal-note-head-space is used to adjust the spacing based on whether there are accidentals. True means the space between notes is equal regardless of whether there is an accidental. False means there is an equal space between each note and the accidental of the next note. (i.e. unequal space between each note head.)

equal-note-head-space: true

true

equal-note-head-space: false

false

Major Scales

The major-scale function is for writing major scales.

Usage

clef: (Required) Allowed values are “treble", “bass”, “alto”, "tenor”. (Same as for stave.)

key: (Required) e.g. “A”, “Bb”, “C#”. Uppercase only.

start-octave: (Required) integer. e.g. 4 is the octave starting from middle C. 5 is the octave above that.

num-octaves: Optional, defaults to 1.

notes-per-stave: (Optional) Used to break a long scale over multiple lines. Line breaks will be inserted after every group of this many notes. If omitted, all notes will be placed on the first stave. Page breaks are blocked between staves of the same scale.

note-duration: (optional) Allowed values are “whole", “quarter”, “semibreve”, "crotchet”. Default is “whole” note. All notes are the same duration.

width: (Optional) If provided, sets the length of the stave lines. It omitted (or auto), the stave lines will be stretched to the available space. If the page width itself is auto, a sensible default will be used.

line-sep: (Optional) A length used to set the vertical spacing of the 5 stave lines (within a given stave). Note that this is a length with units, e.g. 3cm, not just 3.

equal-note-head-space: true or false. Defaults to true. If true, note heads will be equally spaced. Some of this space will be taken up with accidentals. If false, adding an accidental to a note will shift the note head further right. true looks better (in my opinion), but false is useful when trying to squish many notes into one stave, to avoid accidentals overlapping with previous note heads.

Examples

Example: D Major Scale

#import "@preview/staves:0.1.0": major-scale
#major-scale("treble", "D", 4)

D Major
Scale

You can write a 2 octave scale with num-octaves: 2.

Minor Scale

The minor-scale function is for writing natural and harmonic minor scales. The usage is the same as for major-scale, plus an additional minor-type argument.

Usage

clef: (Required) Allowed values are “treble", “bass”, “alto”, "tenor”. (Same as for stave.)

key: (Required) e.g. “A”, “Bb”, “c#”. Uppercase or lowercase.

start-octave: (Required) integer. e.g. 4 is the octave starting from middle C. 5 is the octave above that.

num-octaves: Optional, defaults to 1.

minor-type: Defaults to “harmonic”. Allowed values are “natural", "harmonic”. Melodic minor scales are not yet supported.

seventh: Where the raised seventh would be a double sharp, configure how it is shown. Allowed values are “n", "x”. See examples below.

notes-per-stave: (Optional) Used to break a long scale over multiple lines. Line breaks will be inserted after every group of this many notes. If omitted, all notes will be placed on the first stave. Page breaks are blocked between staves of the same scale.

note-duration: (optional) Allowed values are “whole", “quarter”, “semibreve”, "crotchet”. Default is “whole” note. All notes are the same duration.

width: (Optional) If provided, sets the length of the stave lines. It omitted (or auto), the stave lines will be stretched to the available space. If the page width itself is auto, a sensible default will be used.

line-sep: (Optional) A length used to set the vertical spacing of the 5 stave lines (within a given stave). Note that this is a length with units, e.g. 3cm, not just 3.

equal-note-head-space: true or false. Defaults to true. If true, note heads will be equally spaced. Some of this space will be taken up with accidentals. If false, adding an accidental to a note will shift the note head further right. true looks better (in my opinion), but false is useful when trying to squish many notes into one stave, to avoid accidentals overlapping with previous note heads.

Examples

Example: D Harmonic Minor Scale

#import "@preview/staves:0.1.0": minor-scale
#minor-scale("treble", "D", 4)

D Harmonic Minor
Scale

Example: Bb Natural Minor Scale

#minor-scale("bass", "Bb", 2, minor-type: "natural")

Bb Natural Minor
Scale

Note that for keys with a sharp, the raised 7th can be written as a double sharp, or a natural of the next note.

#minor-scale("treble", "F#", 4, seventh: "n")

#minor-scale("treble", "F#", 4, seventh: "x")

Arpeggio

The arpeggio function is for writing arpeggios.

Usage

The arguments are the same as for major-scale.

clef: (Required) Allowed values are “treble", “bass”, “alto”, "tenor”. (Same as for stave.)

key: (Required) e.g. “A”, “Bb”, “C#”. Uppercase for major, lowercase for minor. Do not include a number for the octave.

start-octave: (Required) integer. e.g. 4 is the octave starting from middle C. 5 is the octave above that.

num-octaves: Optional, defaults to 1.

notes-per-stave: (Optional) Used to break a long scale over multiple lines. Line breaks will be inserted after every group of this many notes. If omitted, all notes will be placed on the first stave. Page breaks are blocked between staves of the same scale.

note-duration: (optional) Allowed values are “whole", “quarter”, “semibreve”, "crotchet”. Default is “whole” note. All notes are the same duration.

width: (Optional) If provided, sets the length of the stave lines. It omitted (or auto), the stave lines will be stretched to the available space. If the page width itself is auto, a sensible default will be used.

line-sep: (Optional) A length used to set the vertical spacing of the 5 stave lines (within a given stave). Note that this is a length with units, e.g. 3cm, not just 3.

equal-note-head-space: true or false. Defaults to true. If true, note heads will be equally spaced. Some of this space will be taken up with accidentals. If false, adding an accidental to a note will shift the note head further right. true looks better (in my opinion), but false is useful when trying to squish many notes into one stave, to avoid accidentals overlapping with previous note heads.

Example

Example: F Major Arpeggio

#arpeggio("bass", "F", 2, num-octaves: 2)

F Major
Arpeggio

Chromatic Scales

chromatic-scale is used to write chromatic scales (every semitone between two notes). The arguments are:

Usage

clef: (Required) Allowed values are “treble", “bass”, “alto”, "tenor”. (Same as for stave.)

key: (Required) e.g. “A”, “Bb”, “C#”. Uppercase for major, lowercase for minor. Do not include a number for the octave.

start-octave: (Required) integer. e.g. 4 is the octave starting from middle C. 5 is the octave above that.

num-octaves: Optional, defaults to 1.

side: ”sharp", "flat”

notes-per-stave: (Optional) Used to break a long scale over multiple lines. Line breaks will be inserted after every group of this many notes. If omitted, all notes will be placed on the first stave. Page breaks are blocked between staves of the same scale.

note-duration: (optional) Allowed values are “whole", “quarter”, “semibreve”, "crotchet”. Default is “whole” note. All notes are the same duration.

width: (Optional) If provided, sets the length of the stave lines. It omitted (or auto), the stave lines will be stretched to the available space. If the page width itself is auto, a sensible default will be used.

line-sep: (Optional) A length used to set the vertical spacing of the 5 stave lines (within a given stave). Note that this is a length with units, e.g. 3cm, not just 3.

equal-note-head-space: true or false. Defaults to true. If true, note heads will be equally spaced. Some of this space will be taken up with accidentals. If false, adding an accidental to a note will shift the note head further right. true looks better (in my opinion), but false is useful when trying to squish many notes into one stave, to avoid accidentals overlapping with previous note heads.

Examples

Example: D Chromatic Scale

#chromatic-scale("treble", "D", 4, notes-per-stave: semitones-per-octave + 1)

D Chromatic
Scale

Example: G Chromatic Scale

#chromatic-scale("bass", "G", 2, side: "flat", note-duration: "crotchet", notes-per-stave: semitones-per-octave + 1)

G Chromatic
Scale

Modes

mode-by-index is used to write modal scales. This function takes the key of the corresponding major scale (ionian), and an integer (one-indexed) to specify which mode relative to that ionian.

This function is designed so that users can easily write all modes of a given key signature with a simple for loop. If you know the word (e.g. “phrygian”) and want to programatically convert that to the relevant integer, use the constant mode-names (documented below). Watch out though. Typst indexes lists from 0, whereas this function treats 1 as the first mode (ionian), because musicians tend to count from 1.

Usage

clef: (Required) Allowed values are “treble", “bass”, “alto”, "tenor”. (Same as for stave.)

key: (Required) e.g. “A”, “Bb”, “C#”. Uppercase for major, lowercase for minor. Do not include a number for the octave.

start-octave: (Required) integer. e.g. 4 is the octave starting from middle C. 5 is the octave above that. This refers to the octave of the ionian, not the first note of the mode.

mode-index: (Required) integer. one-indexed. 1 is ionian, 0 is dorian etc.

num-octaves: Optional, defaults to 1.

side: ”sharp", "flat”

notes-per-stave: (Optional) Used to break a long scale over multiple lines. Line breaks will be inserted after every group of this many notes. If omitted, all notes will be placed on the first stave. Page breaks are blocked between staves of the same scale.

note-duration: (optional) Allowed values are “whole", “quarter”, “semibreve”, "crotchet”. Default is “whole” note. All notes are the same duration.

width: (Optional) If provided, sets the length of the stave lines. It omitted (or auto), the stave lines will be stretched to the available space. If the page width itself is auto, a sensible default will be used.

line-sep: (Optional) A length used to set the vertical spacing of the 5 stave lines (within a given stave). Note that this is a length with units, e.g. 3cm, not just 3.

equal-note-head-space: true or false. Defaults to true. If true, note heads will be equally spaced. Some of this space will be taken up with accidentals. If false, adding an accidental to a note will shift the note head further right. true looks better (in my opinion), but false is useful when trying to squish many notes into one stave, to avoid accidentals overlapping with previous note heads.

Examples

Example: G Dorian

#mode-by-index("treble", "G", 4, 2)

G
Dorian

To write all modes with 2 sharps use a for loop:

#for mode-index in range(1, num-letters-per-octave + 1) {
mode-by-index("treble", "D", 4, mode-index)
}

Constants

There are some constants which are exposed by the library. They may make it easier to write scale books. However the structure, value and presence of these should be considered unstable, and is likely to change in future versions.

all-clefs: (“treble”, “bass”, “alto”, “tenor”)

key-data: (major: (“Cb”, “Gb”, “Db”, “Ab”, “Eb”, “Bb”, “F”, “C”, “G”, “D”, “A”, “E”, “B”, “F#”, “C#”), minor: (“ab”, “eb”, “bb”, “f”, “c”, “g”, “d”, “a”, “e”, “b”, “f#”, “c#”, “g#”, “d#”, “a#”))

all-notes-from-c: (sharp: (“C”, “C#”, “D”, “D#”, “E”, “F”, “F#”, “G”, “G#”, “A”, “A#”, “B”), flat: (“C”, “Db”, “D”, “Eb”, “E”, “F”, “Gb”, “G”, “Ab”, “A”, “Bb”, “B”))

semitones-per-octave: 12

middle-c-octave: 4

all-letters-from-c: (“C”, “D”, “E”, “F”, “G”, “A”, “B”)

num-letters-per-octave: 7

sharp-order: (“F”, “C”, “G”, “D”, “A”, “E”, “B”)

mode-names: (“ionian”, “dorian”, “phrygian”, “lydian”, “mixolydian”, “aeolian”, “locrian”)

Setting Defaults

To set a default, such as the same note-duration for your whole document, use the with approach (for each different scale type):

#let major-scale = major-scale.with(note-duration: "crotchet")
#let minor-scale = minor-scale.with(note-duration: "crotchet")

Implementation Details

This package uses a canvas from the CeTZ package.

License Details

This library uses SVG images for clefs, accidentals etc. These files came from Wikipedia, and are in the public domain. They are not covered by the same license as the rest of the package. Source URLs for these SVGs are listed in /assets/README.md