Getting started

Meeting the Scheme sandbox

The Scheme “sandbox” is a convenient way to explore Scheme and do quick tests. It is an “interactive prompt”, or Read-Eval-Print-Loop. Unlike a LilyPond file, where the contained Scheme code is read and executed in batch, a Read-Eval-Print-Loop works interactively, by asking for an expression, evaluating it, and printing the result back. This is much like a pocket calculator.

To fire up the Scheme sandbox, you first need to open a terminal. On Linux, I trust you to do that. Under macOS, you need to launch the “Terminal” application. Under Windows, open whichever folder in the file explorer, and select “Windows Powershell” in the File menu.

In your terminal, run this command:

lilypond scheme-sandbox

This will work if the lilypond executable is found in the search path. If this is not the case, you need to provide the full path to the executable. In this matter, how to proceed will be different depending on your LilyPond version. In the 2.23 series, binaries don’t come with an installer, they are just archives that you can unpack to get the executables directly. If you use version 2.22, try this:

/Applications/ scheme-sandbox

Under Windows, either:

"C:\Program Files\LilyPond\usr\bin\lilypond" scheme-sandbox


"C:\Program Files\LilyPond\usr\bin\lilypond" scheme-sandbox

If you use version 2.23, when installing LilyPond, you placed the binaries somewhere you wouldn’t move it afterwards. You need to reuse the path to this location:

/full/path/to/lilypond-x.y.z/bin/lilypond scheme-sandbox

or under Windows:

"C:\full\path\to\lilypond-x.y.z\bin\lilypond" scheme-sandbox

In any case, this command should give you either a screen like this:

GNU LilyPond 2.22.2
Processing `/.../ly/'

or like this:

GNU LilyPond 2.23.10 (running Guile 2.2)
Processing `/.../ly/'
GNU Guile 2.2.7
Copyright (C) 1995-2019 Free Software Foundation, Inc.

Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.

Enter `,help' for help.
scheme@(#{ g101}#)>

The first case is with Guile 1.8, which is used in LilyPond 2.22. The second case corresponds to Guile 2.2. To learn more about LilyPond’s transition to Guile 2, go back to Why Scheme?. Either way, the way of interacting with the sandbox is the same. Only mundane details change, such as the prompt scheme@... in Guile 2.2 vs. guile> in Guile 1.8.

The line scheme@... or guile> indicates that Guile is here, ready to listen to your requests. You can enter a code snippet, and it will evaluate it. Let us start with the simplest of snippets: a number.

guile> 42

Remember how I said the sandbox was like a calculator? You typed 42, and since indeed the value of this number is 42, the interpreter answers back 42. Now let us write a more interesting computation (the syntax used will be explained soon):

guile> (+ 42 5)

Here, we computed 42+5, of which the result is 47.

While you follow this tutorial, you can paste code examples in the sandbox and play around with variants of these examples. In the rest of this text, by convention, the symbol “⇒” is used to denote the value of an expression. Suppose I write:

(+ 42 5)

By this, I mean that with the code (+ 42 5), you will get the result 47. In particular, if you want to test yourself, you can type (+ 42 5) in the sandbox, and it will answer 47. Note that the same convention is used in Guile’s own manual.

To exit the sandbox, enter (quit), or more simply, press Control-D.

To finish this introduction to the Scheme sandbox, I need to mention a detail that you may have found annoying if you have already started typing some examples in the sandbox. If you try to delete characters, it may not work and print strange characters. This can be worked around by installing the rlwrap program, and by using the command rlwrap ...lilypond scheme-sandbox instead of ...lilypond scheme-sandbox. On GNU/Linux, rlwrap is likely present in your distribution’s packages. Under macOS, you can install it via Homebrew or MacPorts. Under Windows, it is likely possible to install it through Cygwin, if you feel bold (not for UNIX newbies).

Inserting Scheme code in a LilyPond file

About anywhere in a LilyPond file, you can switch to Scheme mode by writing a hash, the “#” character. This reads one complete Scheme expression, and hands you back to LilyPond when this expression is over. Here are some common examples:

#(set-global-staff-size 18)
\markup \vspace #5
\shape #'((0 . 0) (0.1 . 0.3) (0.1 . 0.5) (0 . 1.0)) Slur

Scheme is often used to input values, such as the argument 5 to \markup \vspace, or the list of offsets passed to \shape. If you type these values in a Scheme expression that is not passed to LilyPond but just written on the top-level, it is usually just lost, unlike the sandbox, which prints them. For example, this minimal LilyPond file produces neither graphical output, nor a message in the log file:

#'((0 . 0) (0.1 . 0.3) (0.1 . 0.5) (0 . 1.0))

It is possible to force a value to be printed by calling the display function on it. This is used like this; the syntax will become clear later.

#(display '((0 . 0) (0.1 . 0.3) (0.1 . 0.5) (0 . 1.0)))

Within Scheme code, it is even possible to switch back to LilyPond mode in order to input some value in LilyPond syntax. This is done by enclosing the LilyPond code within #{ and #}. For example:

#(skip-of-length #{ { c'8 d' e' f' e' d' } #})


In LilyPond, comments start with a % character, and continue until the end of the line. Scheme syntax is slightly different; the character used for comments is not %, but ;. The principle of comments stays the same: they allow to explain the code for the human reader, or to stash some code away temporarily. They are annotations in the source file that are completely ignored by the interpreter.

; This is a comment. It has no effect.
42.0 ; Another comment, after a line of code.

LilyPond also has multiline comments, which unlike simple % comments can span several lines. They start with %{ and are terminated by %}. Scheme has an equivalent syntax too; the delimiters are #! and !#.

Here, we are inside a multiline comment. Users of UNIX
systems (such as GNU/Linux and macOS) may have noticed that
this comment syntax is suitable for shebang lines. If you
don't know what a shebang is, forget about it.


This part deals with how to input values of frequently used types, those that typical LilyPond users will meet often.

Let’s start with the beginning: numbers. They are written in Scheme just like in English:


Non-integer numbers can be entered using a dot. This sort of number is generally called a floating-point number in computer science.


Another common type is strings, which represent text as a sequence of characters. They are input between straight double quotes.

"Sonata IV"
 "Sonata IV"

They can contain newlines.

"Twinkle, twinkle, little star
How I wonder what you are"
 "Twinkle, twinkle, little star
How I wonder what you are"

You can also indicate a newline character using \n. Both forms are strictly equivalent.

"Twinkle, twinkle, little star\nHow I wonder what you are"
 "Twinkle, twinkle, little star
How I wonder what you are"

If the string contains an actual double quote character, you need to “escape” it using a backslash, so that it is not interpreted as the end of the string.

"Rondo \"alla turca\""
 "Rondo \"alla turca\""

In order to print a string, you can call the display function on it.

(display "Rondo \"alla turca\"\n")
 Rondo "alla turca"

Now is a good time to introduce another convention for this tutorial. When I write “⊨”, this means that the code prints what follows the “⊨” sign. For example, the code below prints Rondo "alla turca" on the console. Beware of the difference between printing a string, with display (which I denote with “⊨”), and merely using it without display (which I denote using “⇒”). Despite the superficial similarity, these are vastly different things. When you just do "Rondo", this gives you the string “Rondo”. You can pass this string somewhere, for example give it to LilyPond:

\header {
  title = "Rondo"

On the other hand, display is just a one-time order to Guile to print the value on the console. Once Guile has executed this request, it doesn’t give you anything back. display does not return a meaningful value. It just prints the string, which is useful for debugging.

It is important to understand the difference between how a string is input and how it is displayed. By definition, a string is a sequence of characters. Despite the superficial look, the string "\"\"\n" contains just three characters, namely two double quotes and one newline. We only need these backslashes to avoid a confusion when Guile reads the string. They are not part of the resulting string, as confirmed by display:

(display "\"\"\n")

It is useful to know that LilyPond recognizes a syntax similar to Scheme’s for numbers and strings. The lines below are equivalent:

\override TextScript.Y-offset = 5.2
\override TextScript.Y-offset = #5.2

Similarly, these are equivalent:

title = #"Rondo"
title = "Rondo"
title = Rondo

(Of course, the third syntax can only work with a single word. You need the double quotes as soon as there are spaces in the string.)

Defining variables

Practically all LilyPond users know that their code will be much easier to follow and modify if they organize it using variables.

clarinet = \relative {

{ \clarinet }
{ \transpose c d \clarinet }

There are several advantages to this. For one thing, once you have defined a variable, you can reuse it in several places without typing it over again. For another, even if you do not need to use it in multiple places, using a variable makes the code more structured and thus easier to understand and later modify.

The syntax to define variables in Scheme is:

(define name value)

For example, to assign 5 to the variable x, you can use:

(define x 5)

After that, you can refer to the variable simply by its name. This is in contrast with LilyPond, where referring to a variable is done by writing its name preceded with a backslash.

(define x 5)
(display x)

For multi-word variable names, the convention is to use dashes.

(define my-variable 44)

Perhaps surprisingly, variable names can contain some special characters. Notably, these are acceptd: +, -, *, /, ?, !, =, &, |, @. A few characters are not valid, such as parentheses and quotes. (The precise rules on valid variable names are somewhat complex, and it is not useful to know about them in detail.)

(define x+|^ 34)
(display x+|^)