A music composition library for Clojure and Clojurescript.

Related tags

Audio leipzig


Build Status

A composition library for Clojure and Clojurescript by @ctford.


Include it as a dependency in your project.clj, along with Overtone:

[overtone "0.9.1"]
[leipzig "0.10.0"]

Leiningen template

There is a Leiningen template that creates a simple Leipzig project, ready to run.

Get started

Leipzig models music as a sequence of notes, each of which is a map. They are ordered by :time:

[{:time 0
  :pitch 67
  :duration 2
  :part :melody}
 {:time 2
  :pitch 71
  :duration 2
  :part :melody}]

When using Leipzig, it's helpful to keep this model of notes in mind. Leipzig's functions are convenient ways of manipulating this basic structure, but there's nothing to stop you from building your own.

You can create a melody with the phrase function. Here's a simple melody:

(require '[leipzig.melody :refer [all bpm is phrase tempo then times where with]])

(def melody
         ; Row,  row,  row   your  boat
  (phrase [3/3   3/3   2/3   1/3   3/3]
          [  0     0     0     1     2]))

The first argument to phrase is a sequence of durations. The second is a sequence of pitches. phrase builds a sequence of notes which we can transform with sequence functions, either from Leipzig or ones from Clojure's core libraries.

To play a melody, first define an arrangement. play-note is a multimethod that dispatches on the :part key of each note, so you can easily define an instrument responsible for playing notes of each part. Then, put the sequence of notes into a particular key and tempo and pass them along to play:

(require '[overtone.live :as overtone]
         '[leipzig.live :as live]
         '[leipzig.scale :as scale])

(overtone/definst beep [freq 440 dur 1.0]
  (-> freq
      (* (overtone/env-gen (overtone/perc 0.05 dur) :action overtone/FREE))))

(defmethod live/play-note :default [{midi :pitch seconds :duration}]
  (-> midi overtone/midi->hz (beep seconds)))

  (tempo (bpm 90))
  (where :pitch (comp scale/C scale/major))

There's nothing magic about where. It just applies a function to a particular key of each note, like update-in for sequences.

Let's define two other parts to go with the original melody:

(def reply "The second bar of the melody."
         ; Gent -ly  down the stream
  (phrase [2/3  1/3  2/3  1/3  6/3]
          [  2    1    2    3    4]))

(def bass "A bass part to accompany the melody."
  (->> (phrase [1  1 2]
               [0 -3 0])
       (all :part :bass)))

(defmethod live/play-note :bass [{midi :pitch}]
  ; Halving the frequency drops the note an octave.
  (-> midi overtone/midi->hz (/ 2) (beep 0.5)))

You can then put multiple series of notes together:

  (then (with bass melody))
  (then (with bass melody reply))
  (then (times 2 bass))
  (tempo (bpm 90))
  (where :pitch (comp scale/C scale/major))


Leipzig features a number of namespaces, each containing functions pertaining to a particular area of composition.


This namespace contains the core functions for creating and manipulating melodies. In particular:

  • phrase creates a melody from a sequence of durations and a sequence of pitches.
  • where applies a function to a specified key of each note in a melody.

For example:

(->> (phrase [3/3 3/3 2/3 1/3 3/3] [0 0 0 1 2])
     (where :time inc))


Here are functions to send your melodies to Overtone:

  • play-note is a multimethod that dispatches on the :part a note has.
  • play plays the notes.
  • jam loops the notes, reloading the var each time.
  • stop stops all running melodies.

For example:

(defmethod live/play-note :melody [{midi :pitch}]
  (some-> midi overtone/midi->hz beep))

(def boring-scale
  (->> (phrase (repeat 1) (range 8))
       (all :part :melody)
       (where :pitch (comp C major))))

(jam (var boring-scale))
; Edits to boring-scale will be played each time we go back round the loop.


This namespace contains functions for placing melodies within musical scales. In particular:

  • major and minor are functions that place a pitch within a relative scale.
  • A, B, C etc are functions that take a relative pitch, and place it in a specific absolute key.

For example:

(->> (phrase (repeat 1) (range 8))
     (where :pitch (comp C major)))


The phrase function accepts chords as well as simple pitches. This namespace provides simple ways to manipulate them:

  • triad is the tonic, which can be manipulated to form other chords.
  • root scales the chord up to the specified root.
  • inversion inverts the chord, leaving the root where it is.

For example, a fourth chord, then the second inversion of the fifth:

  [4 4]
  [(-> triad (root 3))
   (-> triad (inversion 2) (root 4))])


This namespace translates midi pitches into frequencies. Overtone's midi->hz will usually do just fine, but if you want to experiment with more exotic temperaments, there are plenty here.

In particular:

  • equal is equivalent to midi->hz and translates frequencies into pitches like a piano is tuned.
  • just uses pure ratios, and more closely models how singers interpret intervals into frequencies.

For example:

(->> (phrase (repeat 1) (range 8))
     (where :pitch (comp just C major)))

Advanced use

In addition to simple pitches, phrase can take maps representing chords or nils:

(require '[leipzig.chord :as chord])

(def chords "Off-beat chords."
  (->> (phrase (repeat 1/2)
               [nil chord/triad
                nil (-> chord/seventh (chord/root 4) (chord/inversion 1) (dissoc :v))
                nil chord/triad
                nil chord/triad])
       (all :part :chords)))

The maps generate a note for each value in the map - the keys are used only to enable chord-transforming functions such as root and inversion.

The nils generate notes without pitches, representing rests. This is convenient, because it allows melodies to have a duration extending beyond their last audible note. However, the play-note implementations and where invocations must be prepared to handle this, e.g. by using when and where's variation wherever:

(require '[leipzig.melody :refer [wherever]]
         '[leipzig.scale :refer [lower]])

(defmethod live/play-note :chords [{midi :pitch}]
  (when midi (-> midi overtone/midi->hz beep)))

  (times 2 chords)
  (wherever :pitch, :pitch lower)
  (with (->> melody (then reply)))
  (tempo (bpm 90))
  (where :pitch (comp scale/C scale/major))


Leipzig supports Clojurescript for all of its namespaces save leipzig.live. The live namespace depends directly on Overtone, so it cannot be used in the browser. However the rest of Leipzig can be used so long as an alternative synthesis engine is present like the Web Audio API. Klangmeister is a good example of this.


See Row, row, row your boat or whelmed for examples.

In Leipzig from scratch, I demonstrate how to create a piece from lein new onwards.

Leipzig came out of a talk I gave called Functional Composition, where I explain basic music theory using Overtone and Clojure.


API documentation, generated by Codox.


Leipzig is designed to play nicely with Clojure's standard sequence functions. Therefore, Leipzig's functions for transforming notes all take the sequence as a final argument so that they can be threaded with the ->> macro:

  (phrase (repeat 1) (cycle [0 2 4]))
  (take 24)
  (filter #(-> % :time even?)))

These sequence functions all exhibit "closure" i.e. their result is the same shape as their input. That allows them to be used and combined very flexibly. where for example, can raise the pitch, set the part or put the notes into a particular tempo:

(->> notes (where :pitch inc))
(->> notes (where :time (bpm 90)))

Leipzig aims to be a library rather than a framework or environment. It uses simple Clojure datastructures and strives to be as open as possible. A new timing scheme, tuning or tempo can be mixed with Leipzig's other functions just as easily as the ones that come with the library.


To run the unit tests without having to start Overtone's Supercollider server:

lein midje leipzig.test.*


As pointed out by @clojens, leipzig.live imports overtone.live, which implicitly boots an internal Supercollider server and can cause problems for folks using 64 bit Windows.

  • [Feature Request] Velocity support

    [Feature Request] Velocity support

    Velocity is also an important part of music. It would be great if notes were represented like this:

    {:time 0
    :pitch 67
    :velocity 127
    :duration 2
    :part :melody}
    opened by kureta 9
  • how to implement retrograde?

    how to implement retrograde?

    I'm not sure if this is an issue or just my misunderstanding. I'm trying to implement a "retrograde" function that will reverse a melody. I think I should be able to do something along the lines of (->> the-phrase crab (simple the-phrase-length)), but that doesn't quite work. I hope you can set me straight here.

    Consider a simple example of a whole note followed by two half notes:

    > ((lc/simple (- 2 0.5)) (lc/crab (lm/phrase [1 0.5 0.5] [4 5 6])))
    ({:pitch 4, :duration 1, :time 1.5} 
     {:pitch 5, :duration 0.5, :time 0.5} 
     {:pitch 6, :duration 0.5, :time 0.0})

    That almost works. Note 6 & 5 start at 0 and 0.5, but Note 4 is at time 1.5? Shouldn't it be at time 1?

    I drew this out and it finally made sense to me. I think there is a missing step

    ;;  -2  -1   0   1   2
    ;; --|-+-|-+-|-+-|-+-|
    ;;           AaaaBbCc = start with whole note + 2 half-notes
    ;;    cCbBaaaA        = crab?  no...we don't play notes backwards
    ;;     CcBb  Aaaa     = actual crab result when notes actual durations
    ;;   CcBbAaaa         = a missing step: offset-each-by-duration
    ;;           CcBbAaaa = (simple 2) puts melody in proper location

    Do you agree there's an issue? Or is this just a misunderstanding on my part?

    Thanks in advance...


    opened by rogerallen 8
  • Full stop

    Full stop

    Was playing around with leipzig again this evening and found myself wanting a "full stop" helper.

    I noticed I can capture the play future and stop it like this:

    (def player (->> drums (times 4) play))
    (future-cancel player)

    But I get the feeling something like


    that canceled all play futures could be handy if you happen to kick off a somewhat long sequence.

    opened by matschaffer 5
  • Support CLJS

    Support CLJS

    Leipzig's dependence on Overtone is very slim - at and now. If we provide equivalents for CLJS, then Leipzig should be able to support music in the browser, using web audio instead of Overtone synths.

    opened by ctford 5
  • Fix several typos in README.md

    Fix several typos in README.md

    opened by ethancrawford 2
  • [question] Stringed instruments?

    [question] Stringed instruments?

    Hi there, I am really digging this library but I am having a bit of a hard time using it with stringed instruments. I tried to write an example based on the overtone stringed synth examples here but it is not clear to me how I could express the chords in a more elegant way. Any ideas or suggestions?

    opened by baskeboler 2
  • README example needs another couple of imports

    README example needs another couple of imports

    I needed to add all and tempo to the list of imports from leipzig.melody when running the example code.

    opened by dfannius 1
  • Add `tempo` to `require` form in README.md example

    Add `tempo` to `require` form in README.md example

    tempo is used later in the example code. Add it to the require so that copy-pasting code from the README works.

    opened by bjackman 1
  • Is clojure /  clojurescript dep really needed?

    Is clojure / clojurescript dep really needed?

    When using Leipzig, I'm noticing that deps are rather old and include both Clojure and Clojurescript. Maybe they both could be a :provided dependency, so you don't get them when consuming the library?

    opened by l3nz 0
  • [question] syncing leipzig compositions with quil visuals

    [question] syncing leipzig compositions with quil visuals

    hello, I have been making some music with leipzig and I'm loving the library, but I would like to sync some live visuals to my music with quil. Is there a way to do this with leipzig?

    opened by nihilazo 4
  • Allow to pass note play scheduling function

    Allow to pass note play scheduling function

    The current implementation of leipzig/live uses overtone/at to schedule playing a note, but this works only with OSC events. In my use case I want to schedule MIDI out events which uses Java MIDI API. I'm doing some research about the best options, for now I'm using overtone.at-at/at-at with fixed thread pool.

    So in this PR I only make possible to pass own play-fn function to both leipzig.live/play and jam

    opened by pjagielski 1
  • Windows 64bit and overtone.live

    Windows 64bit and overtone.live


    Just to inform you, something I faced a bit earlier when trying to setup Overtone in LightTable on Windows (8) 64bit. Normally I run a ArchLinux setup in virtualbox guest on this machine but due to a cracking sound, I had to switch back to the host Win. The problem I faced was java.lang.NoClassDefFoundError: Could not initialize class com.sun.jna.NativeLong exceptions thrown and the solution was found in https://groups.google.com/forum/#!topic/overtone/orbm9ZEtZaE group.

    Basically what it boils down to, from what I understand, is that overtone.live can't be used/called in Windows 64bit. It is not just limited to LightTable, lein repl throws the same. Falling back to overtone.core and then using (boot-external-server) everything run smoothly though and, from what I read, seems to be the prefered method of dealing with this issue.

    Perhaps something to take into account for leipzig as I noticed you made use of it in leipzig.live and mention it in the how-to of the README file.

    opened by clojens 6
Chris Ford
Chris Ford
🎵 Music notation engraving library for MEI with MusicXML and Humdrum support and various toolkits (JavaScript, Python)

Verovio is a fast, portable and lightweight library for engraving Music Encoding Initiative (MEI) digital scores into SVG images. Verovio also contain

RISM Digital Center 442 Jan 10, 2022
music library manager and MusicBrainz tagger

beets Beets is the media library management system for obsessive music geeks. The purpose of beets is to get your music collection right once and for

beetbox 10.6k Jan 23, 2022
Python library for audio and music analysis

librosa A python package for music and audio analysis. Documentation See https://librosa.org/doc/ for a complete reference manual and introductory tut

librosa 4.9k Jan 15, 2022
C++ library and Python bindings for the Music Encoding Initiative format

LibMEI LibMEI is a C++ library for reading and writing MEI files It is developed by the Distributed Digital Music Archives and Libraries Lab at the Sc

Distributed Digital Music Archives and Libraries Lab 52 Jan 14, 2022
A JavaScript library for rendering music notation and guitar tablature.

VexFlow 3 A JavaScript library for rendering music notation. Copyright (c) 2010 Mohit Muthanna Cheppudira Sponsor this Project If you use VexFlow in y

Mohit Cheppudira 3.2k Jan 15, 2022
Streaming music player that finds free music for you

Desktop music player focused on streaming from free sources Links Official website Mastodon Twitter Support channel (Matrix): #nuclear:matrix.org Disc

null 7.2k Jan 16, 2022
MusicXML parsing and layout library

mxml mxml is a C++ parser and layout generator for MusicXML files. Usage You will need: CMake >= 2.6 LibXML2 boost (for unit tests) To build a stand-a

Reinvent Inc. 19 Mar 29, 2021
Javascript audio library for the modern web.

Description howler.js is an audio library for the modern web. It defaults to Web Audio API and falls back to HTML5 Audio. This makes working with audi

James Simpson 19.6k Jan 17, 2022
MuseScore is an open source and free music notation software. For support, contribution, bug reports, visit MuseScore.org. Fork and make pull requests!

Music notation and composition software MuseScore is an open source and free music notation software. For support, contribution, and bug reports visit

MuseScore 6.7k Jan 14, 2022
Small, fast and powerful console music player for Unix-like operating systems.

Warning: cmus is not actively maintained. For details, please see #856 cmus — C* Music Player https://cmus.github.io/ Copyright © 2004-2008 Timo Hirvo

C* Music Player 4.5k Jan 16, 2022
🎵 A simple, clean and cross-platform music player

Museeks A simple, clean and cross-platform music player. (museeks.io) Features Museeks aims to be a simple and easy to use music player with a clean U

Pierre de la Martinière 1.1k Jan 10, 2022
Ear-bending noises and music

Cecilia5 - the audio processing toolbox Cecilia is an audio signal processing environment. Cecilia lets you create your own GUI (grapher, sliders, tog

Olivier Bélanger 130 Dec 29, 2021
Compose music and write score easily in your browser!

inknote Compose music easily in your browser! http://www.michalpaszkiewicz.co.uk/inknote/ Store multiple files, tag them with colours. Made using Type

Michal Paszkiewicz 147 Dec 10, 2021
:tangerine: Clementine Music Player

Clementine Clementine is a modern music player and library organizer for Windows, Linux and macOS. Latest Release Latest Pre-Releases Website: http://

Clementine 3.1k Jan 2, 2022
Music player for deepin desktop environment.

deepin-music Deepin music is a local music player with beautiful design and simple functions developed by Deepin Technology. Dependencies Build depend

Wuhan Deepin Technology Co.,Ltd. 158 Jan 6, 2022
A beautiful cross platform Desktop Player for Google Play Music

Google Play Music™ Desktop Player Windows: MacOS / Linux: Run "Google Play Music" as a standalone desktop app. Never again will you have to hunt throu

Samuel Attard 8.5k Jan 21, 2022
Cross-platform music production software

LMMS A soft PR-Freeze is currently underway to prepare for refactoring (#5592). Please do not open non-essential PRs at this time. What is LMMS? LMMS

LMMS 5.5k Jan 22, 2022
Mopidy is an extensible music server written in Python

Mopidy Mopidy is an extensible music server written in Python. Mopidy plays music from local disk, Spotify, SoundCloud, Google Play Music, and more. Y

Mopidy 7.3k Jan 21, 2022