Skip to content

Commit 6fa92bb

Browse files
committed
Add noon introduction article with piano roll visuals
- Introduces noon's core model, building blocks, and harmonic layer system - 6 piano roll diagrams illustrating chromatic/diatonic/structural/tonic steps, scale comparisons, structure comparisons, chord progressions, and layer mixing - Images generated from evaluated noon expressions with color-coded harmonic layers
1 parent 75b1f87 commit 6fa92bb

8 files changed

Lines changed: 293 additions & 3 deletions

File tree

site/db.edn

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,12 @@
197197
:url "https://hyperphor.com"
198198
:affiliation [:scicloj]
199199
:links [{:icon "github" :href "https://github.com/mtravers"}
200-
{:icon "mastodon" :text "Mastodon" :href "https://mastodon.social/@mtraven23"}
201-
]}
202-
]
200+
{:icon "mastodon" :text "Mastodon" :href "https://mastodon.social/@mtraven23"}]}
201+
{:id :pbaille
202+
:name "Pierre Baille"
203+
:image "https://avatars.githubusercontent.com/u/4739483"
204+
:url "https://github.com/pbaille"
205+
:links []}]
203206

204207
:affiliation
205208
[{:id :clojure.core

src/music/noon_introduction.clj

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
^{:kindly/hide-code true
2+
:clay {:title "Noon — Composing Music with Clojure"
3+
:quarto {:author :pbaille
4+
:description "An introduction to noon, a Clojure library for composing MIDI music using layered harmonic abstractions."
5+
:type :post
6+
:date "2026-02-10"
7+
:category :music
8+
:tags [:music :harmony :functional-programming :midi]
9+
:image "noon_introduction/steps.png"}}}
10+
11+
(ns music.noon-introduction)
12+
13+
;; ## What is Noon?
14+
;;
15+
;; [Noon](https://github.com/pbaille/noon) is a Clojure library for composing and playing MIDI music.
16+
;; The name comes from French — "noon" translates to "MIDI."
17+
;;
18+
;; The motivation is simple: digital audio workstations (DAWs) are powerful but slow.
19+
;; The feedback loop — click, drag, listen, repeat — doesn't match how musicians actually think about music.
20+
;; Musicians think in terms of scales, intervals, chord progressions, melodic contour.
21+
;; Noon tries to bring that level of abstraction into code.
22+
23+
;; ## The Core Model
24+
;;
25+
;; A noon score is a set of event maps. That's it.
26+
;; Each event represents a MIDI note with properties like position, duration, velocity, and pitch:
27+
;;
28+
;;```clj
29+
;; noon.events/DEFAULT_EVENT
30+
;; ;; => {:position 0, :duration 1, :channel 0, :track 0,
31+
;; ;; :velocity 80, :pitch {...}, :voice 0, :patch [0 4]}
32+
;;```
33+
;;
34+
;; A fresh score contains a single event — middle C:
35+
;;
36+
;;```clj
37+
;; (score)
38+
;; ;; => #{{:position 0, :duration 1, ...}}
39+
;;```
40+
;;
41+
;; Everything in noon is a **transformation** — a function from score to score.
42+
;; You build music by composing transformations:
43+
;;
44+
;;```clj
45+
;; (play dur2 (tup s0 s1 s2 s3))
46+
;;```
47+
;;
48+
;; This plays an ascending arpeggio at double duration.
49+
;; `dur2` doubles the duration, `tup` splits it into equal parts,
50+
;; and `s0` through `s3` are structural intervals (more on those soon).
51+
52+
;; ## Building Blocks
53+
;;
54+
;; Three core functions handle most of the composition:
55+
;;
56+
;; **`lin`** — concatenate transformations in sequence (a melody):
57+
;;
58+
;;```clj
59+
;; (play (lin C0 E0 G0 B0))
60+
;;```
61+
;;
62+
;; **`par`** — stack transformations simultaneously (a chord):
63+
;;
64+
;;```clj
65+
;; (play (par C0 Eb0 G0)) ; C minor chord
66+
;;```
67+
;;
68+
;; **`tup`** — like `lin`, but fitted to the current duration (a tuplet):
69+
;;
70+
;;```clj
71+
;; (play (tup c0 c3 c7 c10)) ; four notes in the space of one
72+
;;```
73+
;;
74+
;; These compose freely. A chord progression with arpeggios:
75+
;;
76+
;;```clj
77+
;; (play (lin I IV V I)
78+
;; (each (tup s0 s1 s2)))
79+
;;```
80+
;;
81+
;; And transformations can be chained using vectors:
82+
;;
83+
;;```clj
84+
;; (play [dur:2 (lin C0 E0 G0)])
85+
;;```
86+
;;
87+
;; A few more useful combinators:
88+
;;
89+
;; - `rep` — repeat a transformation accumulating results: `(rep 8 d1)` plays 8 ascending scale steps
90+
;; - `dup` — duplicate a score n times: `(dup 4)` repeats 4 times
91+
;; - `each` — apply a transformation to every event individually
92+
;; - `chans` — like `par` but on separate MIDI channels (for different instruments)
93+
;;
94+
;;```clj
95+
;; (play
96+
;; (chans
97+
;; [(patch :ocarina) (tup s0 s1 s2 s3) (rep 4 s1)]
98+
;; [(patch :vibraphone) vel3 (par s0 s1 s2)]
99+
;; [(patch :acoustic-bass) o1-])
100+
;; (dup 4))
101+
;;```
102+
103+
;; ## How Musicians Think About Pitch
104+
;;
105+
;; Before diving into noon's harmonic system, let's consider how musicians actually think about notes.
106+
;;
107+
;; When a jazz musician sees a G7 chord symbol, they don't think "play MIDI notes 55, 59, 62, 65."
108+
;; They think: "root, third, fifth, seventh — in G mixolydian."
109+
;; If the chord changes to Gm7, they adjust their mental framework — same structure, different scale.
110+
;; The *relationships* between notes matter more than the notes themselves.
111+
;;
112+
;; This is a crucial distinction. Most music software works at the lowest level — absolute pitches, raw MIDI numbers.
113+
;; But musicians navigate a hierarchy of abstractions:
114+
;;
115+
;; - "Go up a semitone" — **chromatic** thinking
116+
;; - "Go to the next note in the scale" — **diatonic** thinking
117+
;; - "Go to the next chord tone" — **structural** thinking
118+
;; - "Go to the next octave" or "change the key" — **tonic** thinking
119+
;;
120+
;; Noon encodes this hierarchy directly.
121+
122+
;; ## The Four Layers
123+
;;
124+
;; Noon's harmonic system is built on four layers, each one selecting from the layer below:
125+
;;
126+
;; **1. Chromatic** — the 12 semitones of an octave: `[0 1 2 3 4 5 6 7 8 9 10 11]`
127+
;;
128+
;; This is the raw material — every possible note.
129+
;;
130+
;; **2. Diatonic** — a selection of the chromatic layer forming a scale: `[0 2 4 5 7 9 11]`
131+
;;
132+
;; This is the major scale — 7 of the 12 chromatic notes. Change this and you're in a different scale (dorian, melodic minor, etc.).
133+
;;
134+
;; **3. Structural** — a selection of the diatonic layer forming a chord: `[0 2 4]`
135+
;;
136+
;; This picks the 1st, 3rd, and 5th degrees of the scale — a triad. Change this and you get a different chord shape (tetrad, sus4, etc.).
137+
;;
138+
;; **4. Tonic** — the root: `[0]`
139+
;;
140+
;; The single reference point everything is built from.
141+
;;
142+
;; Each event in a noon score carries this full context under its `:pitch` key:
143+
;;
144+
;;```clj
145+
;; (:pitch noon.events/DEFAULT_EVENT)
146+
;; ;; => {:scale [0 2 4 5 7 9 11] ; C major
147+
;; ;; :structure [0 2 4] ; triad
148+
;; ;; :origin {:d 35, :c 60} ; middle C
149+
;; ;; :position {:t 0, :s 0, :d 0, :c 0}}
150+
;;```
151+
;;
152+
;; The `:position` map holds an offset at each layer.
153+
;; This is where the note actually *is* within the harmonic context.
154+
155+
;; ## Steps
156+
;;
157+
;; Each layer has a corresponding **step** operation.
158+
;; A step moves you to the next position *on that layer*:
159+
;;
160+
;;```clj
161+
;; (play (tup c0 c1 c2 c3 c4 c5 c6)) ; chromatic: semitone by semitone
162+
;; (play (tup d0 d1 d2 d3 d4 d5 d6)) ; diatonic: scale degree by degree
163+
;; (play (tup s0 s1 s2 s3)) ; structural: chord tone by chord tone
164+
;; (play (tup t0 t1 t2)) ; tonic: octave by octave
165+
;;```
166+
167+
;; ![The four step types — chromatic, diatonic, structural, and tonic — each walking up from C4. Note how the same ascending pattern selects different notes depending on the layer. Colors indicate each note's role: tonic (dark blue), structural (medium blue), diatonic (light blue), chromatic (grey).](noon_introduction/steps.png)
168+
169+
;; The chromatic run gives you all 7 semitones.
170+
;; The diatonic run gives you the major scale.
171+
;; The structural run gives you the arpeggio.
172+
;; The tonic run gives you octaves.
173+
;; Same syntax, different musical meaning — determined by the layer.
174+
;;
175+
;; Here's the key insight: **steps are always relative to the current harmonic context**.
176+
;; When you change the scale or structure, the same step operations produce different notes:
177+
;;
178+
;;```clj
179+
;; ;; Major scale
180+
;; (play dur:4 (lin d0 d1 d2 d3 d4 d5 d6 d7))
181+
;;
182+
;; ;; Dorian scale — same code, different result
183+
;; (play dur:4 (scale :dorian) (lin d0 d1 d2 d3 d4 d5 d6 d7))
184+
;;
185+
;; ;; Hungarian scale
186+
;; (play dur:4 (scale :hungarian) (lin d0 d1 d2 d3 d4 d5 d6 d7))
187+
;;```
188+
189+
;; ![Same diatonic steps, three different scales. The code is identical — only the scale context changes. Notice how the intervals between notes shift while the structure remains.](noon_introduction/scales.png)
190+
191+
;; The same goes for structural steps:
192+
;;
193+
;;```clj
194+
;; ;; Triad arpeggio
195+
;; (play (tup s0 s1 s2 s3))
196+
;;
197+
;; ;; Tetrad arpeggio — same code, richer chord
198+
;; (play (structure :tetrad) (tup s0 s1 s2 s3))
199+
;;```
200+
201+
;; ![Triad vs tetrad: the same structural steps produce C-E-G-C with a triad structure, but C-E-G-B when the structure is set to tetrad — the seventh appears automatically.](noon_introduction/structures.png)
202+
203+
;; ## Changing the Context
204+
;;
205+
;; Several functions let you reshape the harmonic context:
206+
;;
207+
;; **`scale`** — change the scale:
208+
;;
209+
;;```clj
210+
;; (play (scale :melodic-minor) (rup 8 d1))
211+
;;```
212+
;;
213+
;; **`structure`** — change the chord structure:
214+
;;
215+
;;```clj
216+
;; (play (structure :tetrad) (tup s0 s1 s2 s3))
217+
;;```
218+
;;
219+
;; **`root`** — change the tonal center:
220+
;;
221+
;;```clj
222+
;; (play (root :Eb) (tup s0 s1 s2))
223+
;;```
224+
;;
225+
;; **`degree`** — move to a scale degree (mode change):
226+
;;
227+
;;```clj
228+
;; (play (lin I IV V I) (each (tup s0 s1 s2)))
229+
;;```
230+
231+
;; ![A I-IV-V-I chord progression, each chord arpeggiated. Dashed lines mark where the harmonic context changes — the same arpeggio pattern adapts to each degree.](noon_introduction/progression.png)
232+
233+
;; Here `I`, `IV`, `V` are degree changes — they shift the harmonic context
234+
;; so that structural steps target the chord tones of each degree.
235+
;; The `each` function applies the arpeggio pattern to every chord individually.
236+
237+
;; ## Mixing Layers
238+
;;
239+
;; The real power emerges when you mix layers.
240+
;; Since each event carries the full context, you can navigate between layers freely:
241+
;;
242+
;;```clj
243+
;; ;; Structural arpeggio with diatonic passing tones
244+
;; (play dur:2
245+
;; (tup s0 s1 s2 s3)
246+
;; (each (tup d1 d1- d0)))
247+
;;```
248+
249+
;; ![Mixing layers: structural chord tones (medium blue) decorated with diatonic neighbor notes (light blue). The structural steps define the skeleton; the diatonic steps fill in the passing tones.](noon_introduction/mixing.png)
250+
251+
;; This creates a chord arpeggio (`s0 s1 s2 s3`) then decorates each chord tone
252+
;; with upper and lower neighbor scale notes (`d1 d1- d0`).
253+
;; The diatonic steps know which scale they're in;
254+
;; the structural steps know which chord they're targeting.
255+
;; They coexist naturally.
256+
;;
257+
;; An arpeggiated chord progression in harmonic minor:
258+
;;
259+
;;```clj
260+
;; (play (scale :harmonic-minor)
261+
;; (lin I IV VII I)
262+
;; (each (tup s0 s1 s2)))
263+
;;```
264+
265+
;; ![Harmonic minor progression: I-IV-VII-I with arpeggiated chords. Notice the characteristic G♯ (raised 7th degree) appearing in the VII chord.](noon_introduction/harmonic-minor.png)
266+
267+
;; ## Why This Matters
268+
;;
269+
;; In most music programming systems, if you want a chord arpeggio, you specify exact intervals:
270+
;; "0, 4, 7 semitones" for major, "0, 3, 7" for minor.
271+
;; If you change the key or the chord quality, you rewrite the intervals.
272+
;;
273+
;; In noon, `(tup s0 s1 s2)` means "play the first three chord tones" regardless of context.
274+
;; Put it in C major and you get C-E-G.
275+
;; Put it in D dorian and you get D-F-A.
276+
;; Change to a tetrad structure and `s3` appears as the seventh.
277+
;; The code expresses the musical *intent* — "arpeggiate the chord" — and the harmonic context handles the rest.
278+
;;
279+
;; This mirrors how musicians think.
280+
;; A jazz musician doesn't memorize separate fingerings for every chord in every key.
281+
;; They learn *patterns* — "1-3-5", "approach from below", "enclosure" — and apply them across contexts.
282+
;; Noon works the same way.
283+
;;
284+
;; ---
285+
;;
286+
;; Noon is open source — [github.com/pbaille/noon](https://github.com/pbaille/noon).
287+
;; There's also an [online playground](https://pbaille.github.io/noon/) where you can try it in the browser.
15.1 KB
Loading
11.3 KB
Loading
13.1 KB
Loading
20.9 KB
Loading
31.5 KB
Loading
16.9 KB
Loading

0 commit comments

Comments
 (0)