git clone 'https://github.com/daveyarwood/mantra.git'

(ql:quickload :daveyarwood.mantra)


faxanadu screenshot

Clojars Project

Don't have negative thoughts. Remember your mantra.


- Faxanadu

A ClojureScript library designed to make it easy to create music using the HTML5 Web Audio API. Mantra is similar in motivation to Hum, but aims to abstract away the lower-level implementation details and provide a higher-level API.


(require '[mantra.core :as m])

The oscillator model

Normally if you want to make sound using the Web Audio API, you have to initialize your browser's AudioContext, create an instance of an oscillator, and hook it up to a gain node. Mantra abstracts away this boilerplate for you.

You can create an oscillator using the osc function:

(def sq (m/osc :type :square))

This produces a simple map that serves as a blueprint for making sound with this type of oscillator. Mantra's API functions all deal with this type of map, which we call the oscillator model.

For more options to the osc function, see the docstring for that function.

One important difference between Mantra's oscillator models and the Web Audio API's more low-level OscillatorNodes generated by the AudioContext.createOscillator function, is that Web Audio API oscillators are intended to be used in a one-off fashion; for each note you want to play, you must create a new oscillator node, connect it to a gain node, set the frequency and gain, start it, and then stop it. Mantra handles all of this for you.

The note model

Mantra also provides an abstraction for musical notes. A note is represented as a map containing the following keys:

Mantra uses chronoid for accurate timing of notes, leveraging the accuracy of the Web Audio API's clock, which runs on a separate thread.


The pitch of a note can be expressed in a couple of different ways:

  1. As a number representing the frequency in Hz:

clojure {:pitch 440 :duration 1000}

  1. As a string or keyword containing the note name (e.g. C, C#, Ab) and the octave (according to scientific pitch notation):

clojure {:pitch "C4" :duration 1000} {:pitch "F#3" :duration 1000} {:pitch :C2 :duration 1000} {:pitch :Eb5 :duration 1000}


The duration of a note can be specified in a number of different ways.

The simplest way is as a number of milliseconds:

{:pitch 440 :duration 1000}

Mantra also supports note length abstractions from Western musical notation. These represent a number of beats, expressed as a percentage of the whole note, which lasts for 4 beats (one measure in 4/4 time). Some of the American note names are:

You can use these terms, in keyword form, to specify the duration of a note:

{:pitch 100 :duration :quarter}
{:pitch 200 :duration :half}
{:pitch 300 :duration :whole}
{:pitch 400 :duration :hundred-twenty-eighth}

British terminology is also supported:

{:pitch 100 :duration :crotchet}
{:pitch 200 :duration :minim}
{:pitch 300 :duration :semibreve}
{:pitch 400 :duration :semihemidemisemiquaver}

Mantra also supports dotted notes:

{:pitch 100 :duration :dotted-half}
{:pitch 200 :duration :double-dotted-minim}
{:pitch 300 :duration :triple-dotted-semiquaver}

There is also basic support for tuplets – you can add -triplet, -quintuplet, or -septuplet after a basic note length to get a duration equal to the next longest note-length divided by 3, 5 or 7. For example a quarter note triplet is equal to 1/3 the length of a half note.

{:pitch 100 :duration :quarter-triplet}
{:pitch 400 :duration :hundred-twenty-eighth-septuplet}
{:pitch 100 :duration :crotchet-quintuplet}

Note: Combining the dotted- prefix and -triplet/-quintuplet/-septuplet suffixes is not supported, though it would be easy to add in. If you have a need for this, let me know!


When using standard notation, the duration of each note in milliseconds is derived based on the current tempo, which is described in terms of beats per minute (bpm).

The default tempo is 60 bpm.

Mantra provides get, set and update methods to allow you to interact with the current tempo.

(m/get-tempo) ;=> 60

(m/set-tempo 120) ;=> 120

(m/update-tempo inc) ;=> 121

(m/update-tempo * 2) ;=> 242

Playing notes

There are a handful of functions available for playing notes, depending on the particular behavior that you want. These functions all take an oscillator model as the first argument, and some form of a note model (for play-notes and play-chord, it is a collection of note models) as the second argument.


(m/play-note sq {:pitch 440 :duration 1000})

This sounds our oscillator for 1 second at 440 Hz.

One thing to note about this function is that any notes that the oscillator might be playing already will be stopped; in other words, this function tells the oscillator to drop whatever it's doing and play this note.

(m/play-note sq {:pitch 440})

(js/window.setTimeout #(m/stop-osc sq) 1000)

This will also sound the oscillator for 1 second, but it does so in a way that demonstrates another possible way to use play-note: if you give it a note model that does not include a duration, the note will sound indefinitely until you stop it. As you can see, you can stop the note with stop-osc, a function that stops any notes that an oscillator model might be playing at a given moment.

There is also stop-all-oscs, which will stop any currently-playing oscillators that were created via Mantra.


(m/play-note sq {:pitch 440})

(js/window.setTimeout #(m/also-play-note sq {:pitch 660}) 1000)

(js/window.setTimeout #(m/stop-osc sq) 2000)

also-play-note does the same thing as play-note, but with one important difference: it does not stop any notes that the oscillator model might already be playing. In the example above, we sound a note at 440 Hz and let it ring out, then, 1 second later, we also sound a note at 660 Hz, without stopping the first note. 1 second later than that, we stop both notes by calling stop-osc on the oscillator model.


(m/play-notes sq [{:pitch 100 :duration 333}
                  {:pitch 200 :duration 333}
                  {:pitch 300 :duration 333}
                  {:pitch 400 :duration 1000}])

play-notes plays a sequence of notes, one after the other. Each note starts when the note before it ends.

Like play-note, any notes that the oscillator model may have already been playing when this function is called, will be stopped.

(m/play-notes sq [{:pitch 220 :duration 250}
                  {:duration 250}
                  {:pitch 220 :duration 250}
                  {:duration 250}
                  {:pitch 440}])

If you omit the :pitch key from any of the notes, the note becomes a rest – when the oscillator reaches that point in the note sequence, it will pause for :duration milliseconds.

If you omit the :duration key from any of the notes, the note will sustain indefinitely (until it is stopped explicitly) and if there is a note following it in the sequence, it will start playing immediately. (This is probably only useful if you want the last note in a sequence to sustain indefinitely.)


also-play-notes : play-notes :: also-play-note : play-note


(m/play-chord sq [{:pitch 220 :duration 2000}
                  {:pitch 330 :duration 2000}
                  {:pitch 440 :duration 2000}])

play-chord plays a collection of notes simultaneously.

Just like play-note and play-notes, play-chord will stop any already-playing notes belonging to the oscillator model.


You can probably guess what this function does.


Copyright © 2015-2016 Dave Yarwood

Distributed under the Eclipse Public License version 1.0.