Mora
A metaobject protocol for audio-visual livecoding.
Compose temporal patterns, sound, and visuals in Common Lisp. Inspect the objects behind a performance, and extend how they behave from inside the running system.
- Mora
- The language: terse temporal patterns inside a running, SBCL-first Common Lisp image.
- Mora Studio
- Its optional native editor and performance environment, with an OpenGL canvas.
- Output
- Sound through SuperDirt, native shader visuals, MIDI, and optional adapters.
A short form, a running pattern
One channel, one pipeline, five decisions.
(d1! (>> (s "bd [hh hh] sn hh")
(every 4 #'rev)
(gain 0.9)))
-
d1!Install this pattern on channel 1 now. Evaluating the form again replaces it. -
>>Thread the pattern through each step, from left to right. -
"bd [hh hh] sn hh"Mini notation: four slots per cycle. The brackets divide the second slot in two. -
(every 4 #'rev)Conditionally apply rev, which reverses the score, on every fourth cycle. -
(gain 0.9)Annotate each event with a gain value, without changing how many events there are.
The mini-notation string over one cycle
A rhythm is not a one-time instruction. It becomes a pattern that can answer future questions about time. Press Play to hear it, and edit the form: the diagram and the sound follow.
Patterns are questions about time.
A Mora pattern is an object that answers one question: what events are visible in this span?
Spans are half-open, including their start and excluding their end. Time is expressed in cycles, and
positions such as 1/3 and 7/8 can remain exact Common Lisp ratios.
A cycle is a musical coordinate, not a fixed number of seconds. The transport maps cycles to physical time using tempo. Asking a pattern for its events does not itself send audio: playback also needs a running transport, a channel, a backend, and a working receiver.
(events-in *first-pattern*
(span 1/3 7/8)
)
Drag the window or its endpoints, or tap the track to move the nearer endpoint. With an endpoint focused, use the arrow keys.
| Event | Whole | Visible part | Attack in this span |
|---|---|---|---|
hh
|
[1/4, 3/8) | [1/3, 3/8) | No, it began earlier |
hh
|
[3/8, 1/2) | [3/8, 1/2) | Yes |
sn
|
[1/2, 3/4) | [1/2, 3/4) | Yes |
hh
|
[3/4, 1) | [3/4, 7/8) | Yes |
*first-pattern*, bound to (s "bd [hh hh] sn hh") as in the
getting-started guide. The window is half-open: it includes its start
(filled endpoint) and excludes its end (open endpoint). An event cut by the window keeps its whole span, and only
an event whose onset falls inside the window starts a new attack. This figure is drawn by the website to explain
the idea; it does not run Mora.
Evaluation leaves objects you can open.
When an expression is evaluated, selected parts of it remain available as CLOS pattern objects instead of disappearing behind an opaque result. Their structure can be inspected, and the way they answer queries can be extended by adding methods in the running image.
-
1
Source expression
(defparameter *example-pattern* (>> (s "bd sn") (fast 2))) -
2
Retained pattern objects
speed-pattern instance
(s "bd sn")pattern Inspectable now:pattern-summary,pattern-children -
3
A query over a span (here, cycle 0)
Arbitrary spans use the lower-level(query-cycle *example-pattern* 0)events-inprotocol, whose methods can be extended -
4
Events with values, spans, and provenance
bd[0, 1/4)sn[1/4, 1/2)bd[1/2, 3/4)sn[3/4, 1) - 5 Interpretation by an output backend For sample and synthesis output: timestamped OSC to SuperDirt in SuperCollider.
Introspection
Inspect the retained objects: their class, structure, children, and their answers to any span.
(defparameter *pattern* (>> (s "bd ~ sn hh") (fast 2)))
(class-of *pattern*)
(pattern-summary *pattern*)
(pattern-children *pattern*)
(events-in *pattern* (span 13/4 7/2))
Intercession
Define a pattern class, install an instance, then add a method. The same object answers future queries differently.
(defpattern pulse (&key (factor 1))
"An inspectable backbeat. Example: (pulse :factor 2)."
(fast factor (s "bd ~ sn ~")))
(defparameter *live-pulse* (pulse :factor 1))
(d1! *live-pulse*)
(defmethod events-in ((self pulse-pattern) query-span)
(declare (ignore self))
(events-in (s "bd hh sn [hh cp]") query-span))
Live changes affect future queries. They do not rewrite OSC bundles already dispatched, frames already rendered, or sound that has already been heard.
Terse notation, inside a real Common Lisp.
Mora is embedded in Common Lisp, not a fixed vocabulary wrapped around an engine. Functions, variables, macros, exact ratios, classes, generic functions, and the inspector are all present in the same running image. A short phrase can grow into a new operator or instrument without leaving the language.
-
Authored pattern program
(>> (s "bd sn") (fast 2))Constructs a temporal program. - Mora's retained implementation objects A speed-pattern instance, events-in, applicable methods Interpret and expose that temporal program.
- CLOS implementation facilities Classes, generic functions, methods Supply the metalanguage in which Mora is built.
defpattern- Defines a named pattern class with a constructor and observation methods.
deftransform- Defines a reusable, inspectable transformation.
- Methods on
events-in - Change how a class of patterns answers temporal queries, without editing a central dispatcher.
define-scale,define-chord,define-voicing- Extend the musical vocabularies with your own scales, chords, and voicings.
Compose more than sound.
Mora's temporal language reaches shader programming. A visual operator is also a pattern, so temporal transformations act on it too. One channel form can carry audio, a shader chain, and an ordered chain of image processing, each with its own responsibility.
Visuals can follow the score's events, or respond to measured audio. Those are different sources of information, and Mora keeps them distinct.
(d1! :audio (s "bd ~ sn [hh hh]")
:viz (>> (osc :freq 36)
(kaleid 6))
:post (>> (mora.visual:pixelate :px 128
:py 72)
(mora.visual:ascii :characters " .:-=+*#%@")
(mora.visual:edge-glow :amount .6
:radius 1.5)))
Shader material renders in Mora Studio's native OpenGL canvas; sound needs SuperDirt. Omitting a facet in a later edit leaves it alone.
:post chain: pixelate, then ascii
- Source
- pixelate
- then ascii
:post chain: ascii, then pixelate
- Source
- ascii
- then pixelate
A native place to perform.
Mora Studio is a native Common Lisp livecoding environment. The editor and the running language share one Lisp image, so a pattern, transform, or visual operator can be inspected and redefined while the session continues. It is one way to use Mora, not a requirement.
- Structural editing
- Paredit operations, Lisp-aware indentation, matching parentheses, multiple cursors, and a REPL.
- Documentation where you type
- Live completion, ghost argument hints, signature snippets, keyword popups, and contextual documentation.
- Inline views and controls
- Piano rolls, punch cards, and scopes beside the code; knobs and sliders you can perform or write into the score.
- The running image
- An inspector, macro expansion, definition navigation, and SLYNK for remote development.
- On stage
- Desktop fullscreen and the zen performance layout are independent; a performance layout can put the code over the visual canvas while you keep editing.
- Requirements
- A driver supporting OpenGL 4.1 core / GLSL 410, plus native GLFW, libffi, and font libraries. The language itself needs no GL window.
Your samples, without filenames on stage.
Select loops by musical and descriptive metadata. Build stems by role, use mini notation for variants and slices, change one stem lane while the others keep playing, and request explicit tempo and key matching where the optional live engine is available.
(loops :bpm-range '(130 150)
:mode :minor)
(d1! (stack
(>> (part :drums 0)
(cut 1))
(>> (stem :type :melody
:n "<0 1 1>"
:p "0 ~ 2 3 4 ~ 6 7")
(cut 2))))
Needs an imported sample collection with metadata, and the matching sound banks loaded in SuperDirt.
Optional listening tools add other ways to find material: learned search, and PHALAR pairing that ranks candidates against an anchor recording or a capture of the audible mix. They do not replace your judgment or the original metadata, and none of them is needed to write or hear a pattern.
Where to go next.
- Learn A path from a first pattern to the deeper ideas, with honest prerequisites.
- The language Time, notation, composition, Common Lisp and CLOS, and extension.
- Mora Studio The native editor, its feedback, its controls, and its stage presentation.
- Documentation Guides, concepts, and reference, searchable from any page.
Common questions.
- Is Mora only for music?
- No. Its temporal model can describe sound, visuals, and other outputs. Music is a major current application, not the definition of live coding.
- Is it TidalCycles or Strudel in Lisp?
- It shares a temporal and notational lineage, and much of the mini notation is the same, which is why the website can play examples by translating them to Strudel. Mora's implementation, extension model, editor, native visuals, and backend contracts are its own.
- Do I need to learn all of Common Lisp first?
- Small pattern forms are a practical entry point. Lisp becomes especially useful when you want abstraction, inspection, or a new operator. Studio's documentation and structural tools help, but they do not make the language's concepts disappear.
- Do I need Mora Studio?
- No. The pattern language can be used from SBCL without OpenGL. Studio provides the integrated native editor and canvas.
- Is AI required?
- No. Patterns, sound output, and many visuals need no ML model. Machine learning is an optional set of tools for discovery, listening, generation, and ranking.
- Where do the sounds come from?
- From SuperDirt: its sample banks, its synths, and any collections you load into it. Optional models and native components are installed separately and carry their own licenses.
- Can I try it without installing anything?
- Partly. The Play page translates pattern forms into Strudel so you can hear them in the browser. The full language, Mora Studio, visuals, and Lisp extension run in Common Lisp; the Learn page covers setup.
- What does the name stand for?
- Mora is not an acronym. The name was chosen for its temporal meaning.