OOLOI.ORG
Menu

OOLOI

An Organism Evolved.

OVERVIEW

DOCUMENTATION

NEWSLETTER

The Void Was Listening

8/5/2026

11 Comments

 
Picture
With thanks to Kim Bastin in Melbourne, who put it in the post.
Spring has finally arrived in Stockholm. I mention it only because there's a quality of north European light returning after winter that genuinely affects how work feels.

The past two weeks have been quiet on the Ooloi front, code-wise. I'm being onboarded into a large AWS project at work, where my decisions will eventually affect roughly five thousand developers, and onboarding at that scale absorbs a great deal of attention. What Ooloi work I've done has been architectural cleanup: the sort of small consolidations you make when the ground is about to be built on. Data field validation refactored to be entirely general, for instance. Not glamorous, but the kind of work that matters once it has disappeared from notice.

Ross also arrived this week. The 1987 third edition, posted from Melbourne in a padded envelope, in pristine condition, and now on the desk where it belongs.

Two weeks ago I wrote that I needed the book and couldn't buy it. The void was listening, as the coda noted. It is one thing to write that, and another to hold the consequences. The book is dense and beautifully exact where it counts. It's also, in places, at odds with itself, which is the honest record of any craft this old. The beaming section alone runs to fifty pages and doesn't entirely agree across them; the accidental positioning sections give horizontal spacings down to fractions of a stave space. Exactly what I'd hoped for. Translating it into data and code will be a pleasure.
Picture
What strikes me paging through is how much of the material I already know in some sense. Musicians absorb this kind of thing through a lifetime of reading and writing scores. The interest is not the content as content; it's the formalisation. Ross surfaces principles that intuition alone never quite articulates. He's rigorous about things most musicians never have to put into words, and that's where the real value sits. It's also what makes the book usable as a specification rather than as commentary.

The road ahead, briefly. The Piece Window comes first, multi-user collaboration aware from the start, built on the patterns the Instrument Library established. After that: frontend session persistence so windows reappear where you left them; the Piece Preferences window (also collaboration aware); clock skew compensation for reliable distributed undo and redo; and the full multi-mode client work, with Google-Docs-style permission management for connecting peers. That last item closes a chapter. Once it's done, I won't have to think about multi-user logic again. It'll simply be there.

Then plugins for all JVM languages, a first version of MusicXML import and export, and Skia/Skija with GPU acceleration. The last of those is when SMuFL fonts can finally be drawn on the page.

After which the rendering pipeline proper begins. The first stage draws everything except the music: page numbers, titles, headers, footers, staves, system barlines, braces, brackets, instrument names. Crucially, it's done through the plugin system itself, in Clojure plugins. If you don't like the choices, you can replace them, in whatever JVM language suits. I'm not sure it'll ever be necessary, but it can be done if so desired.

Then the interesting material: noteheads, horizontal spacing, flags, augmentation dots, multi-voice collision resolution, accidental clustering, unisons. Everything that isn't a spanner. The solver gets the supposedly easy work first.

Spanners last: beams, ties, slurs, hairpins, glissandi, lyrics, pedalling, 8vas. Where Magnus's question about hovering secondary beams will finally have somewhere proper to live.

But first: piece windows. Expect GIFs/videos soon!​
11 Comments

Wagner Tubas on the Workbench

10/4/2026

5 Comments

 
Back in Sweden at the workbench with the instrument library. I'm finding it a little difficult to concentrate on architectural work this week. So I'm doing practical things instead.

The Instrument Library mechanism is done: the editors, the persistence, the validation, the conflict-free multi-user layer. Multiple users can edit the same library concurrently without colliding.

The single-user case (engravers working on their own computer, the normal case) is now a special case of a collaboration group whose size happens to be exactly 1. No branch in the code. No fallback. No 'offline mode'. No special case. Same logic, same invariants, same code paths, same tests, for a group size of 1 as for a group size of 30. This is the first subsystem in which that collapse is visible end-to-end, and therefore the first concrete evidence that the collaboration machinery underneath Ooloi works the way it was designed to.

So the road map looks like this. The next major thing is the Piece Window: the view in which scores are managed, and the central surface of the whole application. It's no longer a large task. Almost everything it needs already exists: drag and drop, field editors, validation machinery, persistence, undo. All of it was built for the Instrument Library, and all of it generalises directly. The Piece Window is, in effect, a new arrangement of parts that are already on the shelf.
PictureColour choices
Which is precisely why I'm tidying the workshop before I start on it. Meaning: field validation for every editor, properly factored, so I never have to think about 'did I validate this one yet?' again. Splash window timing. Colour schemes. UI principles. None of it is glamorous, but all of it is important because the UI is how Ooloi will be perceived. My aim throughout has been that the program should disappear as much as possible, so the user is in charge and can work undisturbed by technical things. There has to be a certain kind of quiet to what the end user sees.

And, in parallel whilst adding those bits of wiring, I'm filling the Instrument Library with real instruments.

The quiet chaos that is Wagner tubas, for example. Every composer who has written for them labelled them by key, from Wagner himself onwards. The tenor is always in B♭; the bass is always in F. So the library needs 'Tenor Wagner Tuba in B♭' and 'Bass Wagner Tuba in F', in four languages.

Historical notation, again, turns out to deserve a mention. Modern scores write Wagner tubas in treble clef, transposing a perfect fifth below written, the same as Horn in F, which lets horn players switch between horn and Wagner tuba parts without any mental recalibration. But Bruckner's symphonies put the tenor in B♭ basso sounding a major ninth below written and the bass in F sounding a perfect twelfth below, in bass clef; Strauss keeps the same major-ninth/perfect-twelfth transpositions but writes them in treble clef. Neither convention is wrong. Both are alive in the literature, so Ooloi provides both. This isn't a new principle. Ooloi already does the same for old horn notation, which still turns up in any nineteenth-century orchestral part you open; and for the bass and contrabass clarinet variants, which notate differently in the French and German traditions.

Mostly, though, this is a procession of small, specific questions. How do the Italians abbreviate Tuba wagneriana bassa? How do the French abbreviate le Grand Highland Bagpipe, given that no French orchestra has ever scored for one?

It's a quieter kind of work than architecture. Suits me well this week.

Picture
"Hojotohorns" (though they aren't horns by any means)
5 Comments

Three Platforms and an Old Laptop

6/4/2026

0 Comments

 
Picture
​Ooloi runs on macOS, Windows, and Linux. Each platform gets its own self-contained bundle, built separately on a machine running the target architecture natively. You download the one for your platform. It works.

On Windows and Linux there's nothing remarkable to report. On the Mac, however, Ooloi ships an Apple Silicon bundle. No Intel Mac support. This isn't controversial by any means; every Mac user knows this transition happened – no new Intel Mac has been manufactured since 2020, Apple classified the 2017 models as 'vintage' in 2024, and Rosetta 2 is being removed in macOS 28. There's a deeper reason specific to JVMs that makes Intel bundles on Apple Silicon particularly problematic, but the practical reality is simpler: Apple Silicon is where the Mac is, and it's where Ooloi's audience overwhelmingly already is.

As it happens, I develop Ooloi on a 2017 Intel MacBook Pro. There's a point to using old hardware: if there are bottlenecks in the architecture, they show up sooner. A machine that's nine years old and was never fast by today's standards will punish you for inefficiency in ways a current machine won't.

So far, I haven't seen any. This says more about Clojure's architecture than it does about the laptop. The 2017 MacBook Pro has been a surprisingly honest development partner. But you never know, moving forward.

When the deeper Skia rendering work starts – GPU-accelerated layout, real-time scrolling through large scores – I'll likely have to move to my 2024 M3 MacBook Pro, but not just for speed: Skia's GPU path on macOS is moving from deprecated OpenGL to Metal, and Metal is where Apple Silicon lives natively. As an added benefit, early benchmarks (which I've posted here before) already show a two-to-three-times speedup on CPU-bound work, and later processors like the M4 and M5 push that even further. If Metal delivers what it should, large orchestral scores won't need an overdimensioned workstation to scroll and edit smoothly.

The full technical analysis, including the JVM-under-Rosetta performance data and a four-case architecture matrix, is in ADR-0050: Platform Support Policy.
0 Comments

Non Expecto Resurrectionem Mortuorum

4/4/2026

5 Comments

 
Picture
​I'm writing this from a mountainside near Palermo on Easter Eve, the day the liturgical calendar leaves empty. Yesterday was the Crucifixion. Tomorrow is the Resurrection. Today is the silence between them; the day when, if you take the story seriously, God is dead and nobody yet knows what comes next.

Yesterday, one of my closest friends hanged himself. He is now unconscious on a ventilator. Nobody knows why he did it. Nobody knows what comes next for him either.

I'm not a believer. I've never been a believer. But I am an organist, and I've participated more times than I can count in that piece of symbolic theatre we call the liturgy. I have watched the Easter arc – from Maundy Thursday through the desolation of Good Friday to the silence of Saturday and the blazing major key of Sunday morning (Richard Strauss would of course have set it as a C major 6/4 chord) – move people in ways that have nothing to do with whether the story is true. The psychological architecture of it is real, even if its subject is not. I understand Easter Saturday better today than I did yesterday, and not because I've found faith, but because I'm living in the structure.

So I sit on a terrace in Sicily with a stupendous view and seven people I'm fond of, and the light is extraordinary, and a friend is suspended between life and death 2500 kilometres away, and there's nothing I can do. Which makes it a reasonable day to take stock.

​We went to the Teatro Massimo two days ago. It's the third largest opera house in Europe, and the building itself is magnificent: the kind of architecture that earns the word. What happened on stage, however, was something else.

Don Quichotte. Classical ballet. Minkus.

Classical ballet of this type is a closed system of circular reasoning so thorough that its practitioners no longer notice it. It no longer has anything to say, yet they defend Minkus's oom-pah-oom-pah as though it were art. They define 'musicality' as the ability to dance in time with the beat. A musician would find this primitive in the extreme, and in fact not a description of musicality at all, but a description of mere competence. It's the conflation of floor with ceiling.

And yet the system sustains itself, because it has constructed an aesthetic vocabulary that exists only to justify its own continuation. Any external reference point is treated as irrelevant. What remains is religion, not art.

I recognise the pattern. I've encountered it in functional programming circles too: articles of faith masking real emptiness, defended not by argument but by social enforcement. The dynamic is identical. The domain is irrelevant.

In the Christian calendar, today is the day of unresolved tension, the day when the official story hasn't yet provided its answer. What I find clarifying about this particular Saturday is the absence of dogma. The story hasn't yet told you what to believe. For twenty-four hours, you're left with the facts.

I prefer the facts.

When people ask why this blog looks the way it does – why there's so much documentation, so many architecture decision records, so much apparent excess – the honest answer again has to do with death.

I created Igor Engraver in the mid-1990s because I hated Finale. I had written an entire opera in it and the experience was sufficiently wretched to convince me that the problem was one of fundamental architecture. Igor was a music notation system that was, by most accounts, genuinely ahead of its time. It was acquired by idiots and left to die.

What I've come to understand, twenty-five years later, is that almost every fundamental architectural decision in Ooloi is a response to what happened to Igor, whether I originally intended it that way or not.

Open source, so that no individual's mismanagement can bury the work. Open-source components throughout, so that no proprietary dependency can become a chokepoint. ADRs. Guides. A RAG system that can answer deep, architectural questions about the codebase. A public blog that documents development as it happens, including the false starts and course corrections.

This is not a vanity exercise in transparency. It's an insurance policy. If I'm hit by a bus tomorrow, the work survives. Someone can pick it up, read why every decision was made, and continue. The documentation isn't supplementary to the project. It's structural. It's the specification.

Igor died and nothing survived it. I'm sixty-four. Therefore the work must be legible without its author.​

I never discuss other notation programs on this blog, and people occasionally find this odd. The reason isn't diplomatic restraint; it's that I genuinely don't think about Ooloi in competitive terms. Ooloi exists because I'm convinced there's a better way to represent music computationally, and I believe I've found a significant part of it, and I want to build it properly and make it available, so that a field that's been stagnant for forty years has a reason to move​. That is the entirety of the motivation.
​
​
What I am doing inside Ooloi is architecturally different. And that's why I've named it after a third-gender space alien. It's a categorically different thing, and it needs to be. The technical argument for why is elsewhere on this blog, at length, and I won't repeat it here. What matters today is that the work exists, that it's open, and that it's legible. Because today is a day for thinking about what survives.

What connects the friend on the ventilator, the ballet at the Teatro Massimo, the small dogmas of programming communities, and the theological void of Easter Saturday – apart from fragility, ossification, and death – is perhaps nothing more than this: that I have no patience for systems of thought that protect themselves from examination. Religious faith (which is wishful thinking fanned by fear of death) does this by design; classical ballet and programming communities accomplish the same thing through ossified aesthetic insularity and social enforcement respectively, but the mechanism is identical. All of them ask you to treat the absence of external validation as evidence of self-sufficiency rather than what it actually is: a warning.

Tomorrow is Easter. The faithful will celebrate a resurrection I don't believe in. My friend may wake up, or he may not. The view from this mountain is unchanged. Sicily doesn't care about any of it.

​That, too, is a kind of honesty.
5 Comments

The Instrument Library

12/3/2026

7 Comments

 
Picture
"Brünnhilde, a Franciscan friar, and a Moog synthesiser walk into a library."
Undo and redo landed a couple of days ago. The next item on the plan was the Piece Settings window – the place where a score's global configuration lives. But Piece Settings requires instrument assignments, and instrument assignments require a working Instrument Library. So the sequence revealed itself, as it does: not Piece Settings next, but the thing Piece Settings depends on.

This is not an unwelcome discovery. The Instrument Library turns out to be the cleanest possible entity to build at this stage, precisely because it depends on nothing. No piece identity, no piece lifecycle, no window hierarchy. It is global and singleton: one library per backend, shared across all connected clients, persisting across restarts. It holds templates, not instances – when a musician is assigned an instrument, the piece receives a copy of the template and the two are independent from that point. Renaming a template later has no effect on instruments already in scores.

That independence from the rest of the system makes it the right place to establish something that will govern every piece of shared state Ooloi ever manages: the invalidate/fetch/replace pattern.

The Pattern

The pattern is this. When the library changes on the backend, the server broadcasts a notification that any copies 'out there' are now stale. Clients that receive it mark their local cache stale. If the Instrument Library window happens to be open, they fetch immediately and re-render. If the window is closed, nothing happens until it opens – at which point the fetch occurs and the window renders from fresh data. A client that never opens the window pays no cost at all, even if the library is modified repeatedly during the session by other clients. 
​
The sending client follows the same path as everyone else. It does not update its local state from the success response; it waits for the event it will receive as a subscriber, then fetches like any other client. There is one code path for state updates regardless of where the change originated.

In practice

Working through the implementation surfaced several problems, each with a clean answer. Two users editing the library simultaneously must not silently overwrite each other's work, so a change is rejected if someone else updated the library first – the conflict is surfaced explicitly, and the second user reapplies their change on top of the current state. Instruments deleted by the user must stay deleted across application updates that ship new default entries, which requires tracking deletions rather than simply omitting items. Display order within a family must survive both user rearrangement and future additions from updates, which requires ordering to be stored explicitly rather than inferred from position.

What the exercise confirmed is the value of the division of concerns itself. The backend stores state, enforces consistency, and broadcasts invalidation events. The frontend gets the data when required, transforms it locally through whatever editing operations the user performs, and submits the result. The backend does not participate in editing decisions; the frontend does not manage persistence concerns.

That division – established here on clean, self-contained ground – is the same division that will govern every piece of rendered state going forward. The Instrument Library is the first authoritative backend entity that is not a piece; its clean behaviour under load, concurrency, and updates confirms that the model generalises. Everything built on top of it will follow the same pattern.

Read all about it in ADR 0045: Instrument Library.
7 Comments

The Lens We Can No Longer Take Off

8/3/2026

2 Comments

 
In which we examine why a habit of mind is mistaken for the nature of things
Picture
​There's a habit of mind so pervasive in software development today that it's ceased to be visible as a habit. It presents itself, rather, as common sense: ship early, iterate fast, learn from users, move fast and break things. The vocabulary is Silicon Valley but the reach is global, and it has colonised not just how software is built but how software is judged. A project that doesn't fit the pattern isn't evaluated on its own terms; it's measured against the pattern and found wanting.

This is how cultural hegemony works: a local methodology, exported with the efficiency that US capitalism has always brought to the business of making its assumptions universal, ends up masquerading as common sense. The psychological mechanisms by which technical workers are recruited into this system – the mythology of meritocracy and the linguistic inflation that expands responsibility while eliminating agency –  are explored at length in 'Help! Help! I'm Being Repressed! (But You're Grateful Instead). What concerns us here is a different consequence of the same hegemony: not what it does to the people inside the frame, but what it makes invisible outside it.

The promise attached to the methodology (and this is what makes it effective as ideology rather than merely as practice) is that if you play by the rules, you participate in the dream. Adopt the two-week sprint, the MVP, the continuous deployment pipeline, the growth metrics, and somewhere at the end of the process stands the exit and the private jet. The methods and the mythology travel together; you don't merely acquire a development process but a tribal identity and a ready-made trajectory. The cargo cult: the rituals, performed faithfully, in the expectation that the planes will land.

They mostly do not land, of course. What most people acquire is the methodology without the outcome. But this doesn't dislodge the faith, because the faith isn't really about the outcome. It's about the framework through which all technical work is now perceived.

What the Lens Cannot See

​Take TeX, or the Linux kernel, or the protocols on which the internet runs. None of it was built by this method and none of it could have been. Donald Knuth spent a decade on TeX, not because he was slow, but because the problem required it. The domain had correctness conditions that couldn't be satisfied incrementally. Calling partial correctness an MVP doesn't make it one; it makes it a wrong answer with better branding.

There's a further irony here that the dominant methodology prefers not to examine. Much of modern software culture sits on infrastructure built by people who worked in exactly the way it now derides: with correctness as the primary criterion and visibility as an afterthought. 'Move fast and break things' is, in this sense, parasitic: it depends for its very existence on layers of prior work whose authors would have found the phrase somewhere between baffling and contemptible.

Music notation is another such domain. It's a semantic, mathematical, historical, and representational system that's been accumulating edge cases for five hundred years. Accidentals, time signatures, polytonal harmony, the memory rules that govern when a sharp must be reprinted and when it may be assumed: these are formal systems with correctness conditions, not UX choices to be iterated toward, and a notation engine that handles eighty percent of cases is simply broken.

The architecture must be right before anything is visible, because the visible parts depend entirely on the invisible ones. You cannot ship a thin slice of correct rational arithmetic. You cannot gather user feedback on whether your hierarchical key signature partitioning handles organ notation correctly. The feedback loop here isn't user adoption; it's whether the semantic representation is sound enough that hard problems become tractable. These loops aren't merely operating on different timescales; they're incommensurable. User-visible behaviour and internal semantic correctness cannot be measured on the same scale, and conflating them is a category error, not merely a methodological one.

The particular cruelty of the dominant methodology is that it treats foundational work as delay. Eighteen months of semantic infrastructure before a window opens is by the startup template a red flag, evidence that the team can't ship. By any other standard, it's exactly what the domain requires.

The Moral Dimension

The methodological misreading would be merely irritating if it were innocent. It's not quite innocent, which is what gives it its edge.

To look at a project operating outside the disruption frame and conclude that it's slow or self-indulgent requires a particular kind of incuriosity: an unwillingness to ask whether the frame itself might be the wrong instrument for the measurement. That incuriosity isn't random; it's cultivated by an intellectual monoculture that has made one historically contingent approach to software development appear to be the only conceivable one.

There are people who cannot imagine a domain where the architecture must be correct before anything else is possible, because they've only ever worked in domains where it doesn't need to be. That's forgivable. But there are also people who, when presented with evidence that the domain is different, prefer the template to the evidence; a failure of intellectual range, and at some level a moral one, because it renders invisible entire categories of value, including most of the software the world actually relies on.

What the Domain Actually Demands

The disruption methodology assumes a forgiving domain: one where the cost of being wrong is low and the complexity lives in the interface rather than the semantics. Most web software fulfils these conditions reasonably well. A social media feed or a SaaS dashboard: if you get the data model slightly wrong, you refactor. If the UI is poor, you redesign. The feedback loop is fast and the penalties are recoverable.

Music notation violates every one of these assumptions. The real distinction is between domains where a representational error can be repaired after the fact and domains where it metastasises, where every layer built on top of a flawed foundation inherits and amplifies the flaw, until the accumulated damage is structural and irreversible. Notation is the latter kind of domain. The semantic layer isn't a detail to be addressed after launch. The heuristics that commercial notation software relies on aren't temporary expedients awaiting a proper solution; they're the permanent consequence of foundations that made exact solutions intractable. Once that choice is made, it cannot be unmade. The technical debt is original sin, not a balance sheet entry awaiting repayment.

The correct solutions exist, and not because of clever algorithms: a correct representation makes correct solutions possible. The problems didn't require heuristics. They required a foundation that sprint-cycle development philosophies, tuned for domains where being wrong is cheap and reversible, reliably select against.

Music notation has been accumulating edge cases for five hundred years. It doesn't care about your sprint velocity.

​(This article is also available on Medium)
2 Comments

A Reading Path

6/3/2026

0 Comments

 
Picture
The documentation has accumulated to the point where it doesn't naturally sort itself for a first reader. Architecture Decision Records are numbered chronologically — in the order decisions were made, not in the order you need to encounter them. Reading straight through means confronting distributed-systems reasoning before you know what a `Piece` contains. The answer keeps arriving before you've understood the question.

There's now a guided path that reorganises the material by conceptual dependency: nine phases, starting with motivation and working through data model, language choices, addressing, pitch representation, the plugin system, rendering, and the frontend. Each phase builds the vocabulary the next one requires.

Think of it as an adjunct to the Librarian — where the Oracle answers specific questions, this path builds the context that makes those answers land correctly. It's now the canonical entry point to the technical documentation. It's here.
0 Comments

It's All Greek to Me

2/3/2026

6 Comments

 
Picture
Open Settings, switch the locale. Every open window follows: menus, the About dialog, Settings itself. English to German to French to Danish, pausing on Greek. Then it repeats.

This is chrome – wiring things up, giving Ooloi a face.

The obvious next step seemed to be collaboration. The architecture has supported shared backends since day one; the collaboration ticket and ADR systematise how they are handled, add authentication, authorisation, piece invitations.

But you can't test the collaboration paradigm just on application settings. Theme preferences, locale, autosave interval – these are local. They don't live on a shared backend. To verify that the pattern works, you need an actual piece: something that lives on a server that one or more clients connect to.

So a sequence of work comes first.

Undo and redo for settings changes. Every setting change already records the old value alongside the new; the undo stack accumulates and replays inverse events. No piece dependency. This establishes the mechanism before anything more complex exists.

Then creating and opening pieces — not in a naïve single-user way, but written from the start for an environment where the backend may be shared. Even locally, the model has to reflect that reality.

Then undo and redo for piece content: a single authoritative stack on the server, shared across everyone editing simultaneously. The undo gesture in the frontend inspects which tier holds the more recent operation and routes accordingly.

Then the Piece Settings editor, with the Instrument Library window.

Here is where the collaboration pattern is actually demonstrated. Consider adjusting beam thickness or the default appearance of piano pedal extension lines: ordinary engraving decisions. Now add a collaborator with the same editor open on the same piece. Your changes appear in their view instantly. Theirs in yours. Both of you can undo, on the same data, with the same shared history.

This is not a special case requiring special handling. If the preceding layers are in place, this is what falls out of the existing architecture. But it should be done as early as possible, before additional layers complicate things, so now is the right time.
6 Comments

The Frontend Architecture Guide

18/2/2026

0 Comments

 
Picture
I've published a guide to Ooloi's frontend architecture – long, precise, and detailed, written for architects and developers who want to understand the system from the inside.

It covers window lifecycle, event architecture, the rendering pipeline, fetch coordination, localisation, collaboration, and the testing model. Together with the Timewalking Guide and the Polymorphic API Guide, it forms the main conceptual gateway into Ooloi's internals.

Worth flagging for experienced frontend developers: this architecture inverts several instincts. The frontend holds no semantic authority. It is computationally strong and structurally subordinate; that subordination is precisely what makes distributed collaboration, transport independence, and first-class plugin composition possible without architectural compromise. The guide explains why. I think the argument is sound, though I'm aware it might feel distinctly odd, or even regressive, to a developer whose instincts were formed in React or a similar ecosystem.

If you work in notation – as a musician, engraver, or editor – and find this kind of writing opaque, current large language models are genuinely useful here. Feed the guide to one and ask what it means for your daily work. It's a reasonable use of the technology, and the answers tend to be surprisingly good – when they're not inventing things I'd never claim ;)

Worth stating plainly: the subject here is art, and the technical rigour exists in service of that, not despite it. This blog documents the reasoning behind Ooloi as it develops – for future contributors, researchers, maintainers, and critics. Systems like this have a lineage and a psychological architecture as much as a technical one. I want that visible. This blog has a voice because it was written by someone who considers authorial invisibility a failure mode rather than a virtue.
0 Comments

Surface Tension

17/2/2026

10 Comments

 
Picture
Ooloi is now a running desktop application on macOS, Windows, and Linux. It opens, it responds, it remembers where you left it. A concrete thing, like any other program.

But that's not enough. The next step is teaching it to talk. Ooloi's frontend and backend have always been separate components communicating through a defined protocol; in standalone mode, they simply appear as one and the user needn't think about it. But this disciplined split personality was designed that way for a reason, and it's now time to act on it.

ADR-0036 lets the two halves run apart — so one Ooloi can connect to another, or to a centralised server. You invite a collaborator; a secure connection opens. They disconnect; the application returns to working alone. No restart, no mode switching.

The separation was always there. Now it becomes visible.
Picture
10 Comments

Where the Freedom Comes From

15/2/2026

2 Comments

 
Picture
That room. The tape drives in their cabinets along the far wall, the operator's console in the centre, the line printer to the right. It looks like something from Kubrick: the same antiseptic geometry, the same monolithic seriousness, the same implication that whatever happens here is important enough to require its own architecture.

This is a Philips P1100. In 1973, I was twelve years old, and this is where I started programming.

I got the opportunity through school. The P1100 was a Dutch mainframe, not widely known outside Northern Europe, and the fact that a twelve-year-old was permitted to approach it probably says more about 1970s Scandinavian attitudes to children than about any pedagogical intention. But approach it I did. COBOL and FORTRAN, mostly. Punch cards. Line printers. Typewriter console input. Thirty-six-bit words and 32K words of core memory – actual magnetic cores, the kind you could theoretically see with a magnifying glass. Spinning tape stations and disk units. The whole physical apparatus of computation as it existed before anyone thought to make it personal.
Picture
P1100 product reveal
What the photographs convey, and what no description quite captures, is the physicality of early computing. These were rooms, not screens. You walked into the machine. The tape drives were furniture. The noise was constant. Programming in that environment carried a particular weight; there was nothing casual about it, nothing disposable, nothing you could do from bed with a laptop. The seriousness was built into the space.

I don't mention this for nostalgia. I mention it because something from that period has stayed with me through fifty years of subsequent work, and I think it matters for understanding why I build the way I build.

A Language That Trusts You

Six years after the P1100, in 1979, I encountered Lisp. I was eighteen, and the encounter came through the legendary August 1979 issue of BYTE magazine, devoted entirely to Lisp. I've written about this before, so I won't repeat the full story here; but the experience deserves its place in this genealogy because it changed the direction of everything that followed.

I should say what computing felt like in the late 1970s, for anyone who wasn't there. The dominant paradigm was procedural: FORTRAN, COBOL, Pascal, various assembly languages depending on your hardware. Programming meant telling the machine what to do, step by step, in a language designed for the machine's convenience as much as the programmer's. The programmer was an operator. The metaphor was industrial. I had spent six years in that world. It was what programming was.

Lisp was different in kind, not just in degree. What I encountered was a language that didn't tell me what to do. It waited. It provided a set of primitives so minimal they seemed almost absurd; seven or eight special forms, a way to define functions, a way to combine them, and the extraordinary implication that this was sufficient for anything I could imagine. The language was smaller than what I'd been using, and it could do more. That paradox has never stopped being interesting to me.

What Lisp offered, and what I wouldn't have the vocabulary to articulate until years later, was freedom. Not freedom as a marketing term or a political slogan, but freedom as an engineering condition: the ability to reshape the language to the problem rather than reshaping the problem to fit the language. In Lisp, the programmer was not an operator. The programmer was a maker of tools, a builder of languages, a creative agent trusted with the full power of the system.

The culture around Lisp carried this same ethos. The people who shaped it (Steele, Gabriel, Kiczales, Norvig, among others) were not priests. They were builders, hackers, improvisers. They valued expressiveness, wit, elegance in the mathematical sense, and above all, the conviction that the person at the keyboard was intelligent enough to be trusted with dangerous tools. The community was irreverent, argumentative, often brilliant, and entirely uninterested in policing anyone's tone or orthodoxy. You were judged by what you built, not by which catechism you recited.

I mention these names because they are not abstractions to me. I was eighteen. I was reading their papers. I was trying to implement what they described. Immediately after that BYTE issue, I implemented a Lisp in 6502 assembler, using an assembler I had written in BASIC. I tell you this not to impress but to convey the intensity of the response. When something is that good, you build it yourself to understand it.

CLOS and the Workshop Tradition

The progression from basic Lisp to the Common Lisp Object System (CLOS) in the mid-1980s was not, for me, a move from one paradigm to another. It was an expansion within the same tradition.

CLOS is difficult to explain to anyone whose experience of object-oriented programming was formed by Java, C++, or Python. In fact, some of us maintained even then that CLOS was not object-oriented at all; the name was a concession to the terminology of the period, not a description of what it actually was. Those languages treat objects as containers: data and methods bundled together, accessed through interfaces, extended through inheritance. The programmer works within the object. CLOS transcends this entirely. Methods don't belong to objects; they belong to generic functions. Dispatch can happen on any combination of argument types. Method combinations (:before, :after, :around) let you layer behaviour without modifying existing code. The Metaobject Protocol (MOP) lets you redefine how the object system itself works.

The result is a system of extraordinary power and openness. You could override anything. You could extend anything. You could reshape the behaviour of the system at any level, including the level at which behaviour is dispatched. CLOS treated the programmer not as a user of the object system but as its co-author. The system was a workshop, not a cathedral, and the door was always unlocked.

This mattered to me practically, not just aesthetically. When I built HotLisp for the Royal College of Music in Stockholm (a Common Lisp implementation written in C that treated MIDI events as first-class citizens), I was already integrating music directly into the computational core. When I later built Igor Engraver, CLOS became the substrate on which the entire semantic model rested. The ability to extend, compose, and reshape; to add new types of musical objects that participated seamlessly in existing operations; to layer specialised behaviour onto generic frameworks without breaking them: these weren't theoretical luxuries. They were the reason the software worked.

I taught algorithmic composition using Lisp at the Royal College. My students, who were composers, not computer scientists, understood the freedom immediately. You could express musical ideas directly, without the mediation of a language designed for someone else's problems. The gap between thinking and making was as small as I have ever seen it in any medium.

Having Been There

​I am, as of this writing, sixty-four years old. I have been programming for over fifty years. I say this not as credential but as context, because having been there changes what you see.

I was there for the AI winter. I watched a field that had promised the moon retreat into a decade of funding cuts and reputational collapse, dragging Lisp's standing down with it. The language didn't deserve the association; it was punished for the sins of researchers who had over-promised. I kept using it anyway, because the language hadn't changed, only its fashionability. This was the first time I learned that technical merit and cultural standing are largely unrelated.

I was there for the object-oriented conquest of the 1990s. I watched as C++ and then Java established OOP as orthodoxy, complete with design pattern catechisms and UML diagrams that looked more like religious iconography than engineering documentation. CLOS, which had solved the problems these languages were struggling with years earlier, was invisible because it ran on the wrong platform and belonged to the wrong tribe. The industry chose what was marketable over what was good. This was the second time I learned that lesson.

I was there for the web's arrival, which changed everything about deployment and nothing about the fundamental problems of building complex software. I was there for the enterprise Java years, when 'architecture' meant XML configuration files and dependency injection frameworks and a peculiar conviction that more abstraction was always better, regardless of what it cost in comprehensibility. I was there for the functional programming renaissance, which rediscovered principles that Lisp had embodied since the 1950s, often without acknowledgement and frequently with a missionary zeal that replicated the very dogmatism it claimed to reject.

I am here for the current AI explosion, which rhymes with the original AI boom in ways that make my skin prickle. I have seen this arc before: extraordinary technical promise, sweeping claims about imminent transformation, and a market that moves faster than the underlying science. Time will separate what is durable from what is fashionable.
​
None of this is complaint. It is observation. When you have been programming for fifty years, you develop a particular relationship with trends: you have seen enough of them arrive, dominate, and recede to know that the arrival tells you very little about the value and the dominance tells you nothing about the longevity. What remains after the trend passes is what was true about it. Immutability was true about functional programming. The JVM was true about Java. Interactive development was true about Lisp all along.

The thing that has remained true for me, through every cycle, is the freedom principle. The conviction that the programmer should be trusted, that the tools should serve the maker rather than constrain them, that the ability to reshape your instruments is not a luxury but a precondition for doing serious work. I did not learn this from a manifesto. I learned it from a mainframe in 1973 and a Lisp prompt in 1979, and everything since has confirmed it.

What Was Lost

Igor Engraver was the fullest expression of this principle I had achieved. Eighteen people, $7.5 million in investment (2024 equivalent), a music notation system built entirely in Common Lisp that introduced semantic modelling to the field. Conductors were interested. Composers were interested. The architecture was sound.

The project ended in 2002 after a combination of venture capital pressure, management-imposed feature creep, and the collapse of M&A activity following September 11, 2001. The technical architecture was sound; the business environment was not.

I won't rehearse the full history here; it's documented elsewhere. What matters for this essay is what was lost and what survived. What was lost was the software, the team, and the years of accumulated work. What survived was the knowledge: of what works in music notation architecture, of what CLOS makes possible, of what fails when business logic overrides engineering discipline. And what survived, underneath all of it, was the conviction that the freedom principle was correct and that the work deserved to be done properly.

Most people, having had a project of that scale taken from them, do not try again. I understand why. The rational calculus says the risk exceeds the reward. But the rational calculus doesn't account for the fact that you still know how to do it, that you've learned what you didn't know the first time, and that nobody else is going to build this.

The Return

When I came to Clojure, I came home.

I don't mean this sentimentally. I mean it technically. Clojure is a Lisp; it thinks in the same shapes, provides the same minimal-but-sufficient primitive set, and trusts the programmer with the same radical freedom. But it adds things that Common Lisp never had and that my work desperately needed: immutable data structures by default, Software Transactional Memory for concurrent coordination, and the entire JVM ecosystem for deployment, GUI development, and cross-platform reliability. I've written extensively about these on the blog, so I won't rehearse the technical details here. What matters for this essay is the genealogical thread.

I've called this 'Clojure for closure', and the wordplay is deliberate. It is a return to a native language after decades away, carrying everything those decades taught me.

The CLOS patterns came with me. Ooloi uses Clojure's multimethods with the Methodical library to achieve CLOS-like method combinations: :before, :after, :around modifiers that layer behaviour exactly as they did in Common Lisp. The hierarchical type system uses Clojure's `derive` mechanism to create inheritance-like relationships without classes, mirroring CLOS's approach to multiple inheritance through composition rather than rigid hierarchy. Generic functions dispatch on argument types just as they did in CLOS, with operations defined outside the data they manipulate.

Rich Hickey, Clojure's creator, made choices that resonate specifically with the tradition I come from. 'Mutability in bounded contexts is fine'. 'Solve problems first'. The emphasis on what you can build, not on what catechism you recite. Clojure didn't inherit the Lisp purity culture that never really existed in the first place; it inherited the Lisp builder culture, the one that judged you by what you shipped rather than which monads you could name.

But Clojure also gave me something new. Immutability as a default changes how you think about time, state, and coordination. In Common Lisp, you could write immutable code, but the language didn't push you toward it. In Clojure, immutability is the grain of the wood. This inversion turned out to be profound for music notation specifically, because a musical score is a temporal structure where the meaning of any element depends on everything that precedes it. The semantic engine that results from this is something Igor Engraver, for all its strengths, could never have achieved.

Where the Genealogy Arrives

Ooloi's plugin system is, in a specific sense, the culmination of everything this essay describes.

The core engine is written in Clojure and architecturally closed. I've written about this elsewhere as the 'monastic core' principle. The freedom is at the perimeter: any JVM language can write plugins, and the plugin API speaks each language's idioms natively. A Java developer writes Java. A Kotlin developer writes Kotlin. Nobody is forced to learn Clojure or to understand the internals. The benefits of the architecture propagate without requiring conversion.

This is the CLOS principle, expressed architecturally. The system trusts the developer. The developer works in their own language, with their own patterns, and the underlying architecture handles the coordination. The monastery uses Latin internally because Latin is precise; but when teaching farmers improved agriculture, you speak their language. This is not compromise. It is proper boundary design, and it is the lesson of fifty years in the workshop tradition.

When I sit at this keyboard today, fixing a macOS menu bug or stripping debug symbols from a jlink runtime, I am using skills accumulated across the full span of what I've described. The mainframe taught me that serious work requires serious infrastructure. Lisp taught me that freedom is an engineering condition, not a slogan. CLOS taught me that the system should trust the programmer. The loss of Igor Engraver taught me that good architecture survives the business that funded it, if someone is stubborn enough to rebuild. Clojure taught me that immutability changes what's possible. The whole genealogy is present in every design decision, whether or not it's visible.

I don't know if Ooloi will succeed, whatever that is. I know the architecture is right. I know the foundations carry what needs to be built on top of them.

​And I know where the freedom comes from, which is what I wanted to write down.
2 Comments

The Bazaar and the Cathedral

30/1/2026

0 Comments

 
Picture
​In a blog post from some time ago, Penitenziagite!, I wrote about linguistic impedance in software: the way systems shout incomprehensible demands at contributing developers, forcing them to think in the wrong language. That post was about architecture as translation – the scriptorium at the centre speaking Latin for precision, and the gate speaking the vernacular so people can actually participate.

This post is about governance.

Specifically: why Ooloi is open source, but not open in the way some people expect. And why it cannot be.

The Myth of the Open-Source Bazaar

​There is a persistent belief among developers that open source implies a particular governance model. In this vision, design is open, debate is open, architecture is open, decision-making is open, and even the rewriting of foundations is treated as normal community activity. The project becomes a bazaar: a bustling marketplace of patches and opinions, competing visions and negotiated direction.

This model works for some kinds of software. It fails for others. Music notation software belongs firmly in the latter category.

The problems Ooloi addresses – temporal coordination, concurrent structural mutation, global layout decisions, semantic correctness across voices – are not decomposable into independent opinions. They cannot be negotiated piecemeal. They require conceptual unity, which is another way of saying they require architectural closure.

A bazaar cannot produce that. However, a cathedral can.

What 'Closed' Means Here

Ooloi's core is closed – but not in the sense of secrecy. The code is MPL-2.0: inspectable, forkable, modifiable. What I mean by closed is something more precise: closed in the sense of authority over semantics.

The musical model is fixed. The invariants are fixed. The staged pipeline is fixed. The authority boundaries are fixed. This does not mean that development has stopped, or that new functionality will not appear. It means that the conceptual foundations are no longer under negotiation.

Closure is not Stagnation

​A closed semantic core is often misread as a claim of finality: finished, complete, unchangeable. That is not what I mean.

What is closed is the space of permissible reinterpretation. New features will arrive, new capabilities will arrive, refinements and extensions will arrive – but they must fit within the established model rather than redefine it.

This distinction matters because most architectural failure in open projects does not come from lack of innovation. It comes from uncontrolled semantic drift: the gradual erosion of guarantees as each contributor reinterprets the foundations to suit local convenience. Ooloi explicitly forbids that drift.

Why the Core must be Closed

​Ooloi's correctness depends on architectural decisions that only work as a coherent whole.

Immutability is treated as a semantic guarantee rather than a performance trick. STM is not merely one possible concurrency implementation among several; it is the concurrency model, because the alternatives import failure modes that Ooloi is explicitly designed to eliminate. Structural sharing is not optional; it is a prerequisite for the kind of scale where a full orchestral score can be edited in real time without the system grinding to a halt. Computation is staged because global layout decisions are otherwise intractable. Piece state is single-authority because reconciliation architectures – the kind where multiple sources of truth must be merged – create exactly the class of bugs that have plagued notation software for forty years.

These are not interchangeable components. They are constraints, and constraints that reinforce each other. You cannot selectively replace one of them. You cannot introduce mutability 'for performance'. You cannot swap out the concurrency model. You cannot dissolve authority boundaries. Each such change collapses guarantees that are only meaningful because the other constraints hold.

The cathedral stands because the architecture must stand.

Why This Enables Real Openness

​Here is the paradox: by closing the core, Ooloi makes everything around it genuinely open.

Because the centre does not move, APIs stop being provisional. Semantics do not drift. Performance characteristics remain predictable. Plugin contracts remain valid across versions, which means plugin authors can build with confidence rather than anxiety about the next breaking change.

Plugins become first-class citizens rather than tolerated afterthoughts. Any JVM language can participate without translation into a single house dialect. Boundaries stay explicit. Failure modes stay contained.

This is the same pattern that made TeX durable, SQLite coherent, and the JVM stable enough to host entire ecosystems. A fixed centre enables freedom at the edges.

The Cuckoo-Chick Problem

Visibility attracts contributors. Most are helpful; some are enthusiastic but misaligned; a few arrive with expectations the project cannot and will not meet.

I have learned to recognise two failure modes.

1: Misaligned help

These contributors mean well. They propose changes that would, in effect, produce a different system entirely.

They might want the core rewritten in another language. They might want STM replaced with actors, or argue for mutability 'for performance'. They might want the semantic model redesigned, or the staged pipeline replaced with heuristics. They are not malicious – they simply assume the architecture is provisional, a starting point for negotiation rather than a finished foundation.

As you have seen, it is not.

2: The arena-seeker

Here the motivation is not technical at all. The project becomes a venue for resolving old arguments, validating preferred tools, or reenacting disputes imported from elsewhere. When the architecture does not bend, pressure escalates. When boundaries hold, frustration surfaces.

At that point refusal is often reinterpreted as hostility, silence as arrogance, and constraint as personal exclusion.

This is a category error. Refusal is governance. In Ooloi, it is exercised deliberately.

Why Refusal is Non-Negotiable

If the core were open to redesign, plugin contracts would break. Semantics would drift. Stages in the rendering pipeline would start interfering with each other. Concurrency guarantees would dissolve. Correctness would become probabilistic – which is to say, the system would regress to precisely the type of architecture Ooloi was built to escape.

By enforcing architectural closure, contributors learn where pressure will not be applied. Plugin authors learn what will not change. Users get stable behaviour. The system remains intelligible.

Boundaries are not unkind. They are protective.

Open Source Without Negotiation

​Ooloi is open source in a precise, literal sense: inspectable, forkable, extensible, durable.

It is not open in the sense of open-ended architectural debate. It is not open in the sense of consensus-driven semantics. It is not open in the sense of renegotiating authority with each new voice that arrives.

The core is not a public workshop. It is a composition. Collaborative compositions exist – devised theatre, collectively improvised music – but they are specialised forms suited to particular purposes, not universal methods. Ooloi's architecture is not that kind of work. Its structure must be fixed before others can build upon it.

The bazaar is not inside the cathedral; it is outside, at the gate, where plugins flourish without threatening the foundations.

The Gate & the Bazaar

The Penitenziagite! post argued that architecture should not shout incomprehensible demands at its users. This post argues the complement: governance should not shout incomprehensible demands at its contributors.

Ooloi's structure is explicit. There is the cathedral/scriptorium: the core, architecturally closed and semantically fixed, where the hard problems are solved. There is the library: canonical plugins and reference implementations, where patterns are demonstrated rather than debated. And there is the gate and the bazaar beyond it: the plugin ecosystem, open and multilingual and creative, but separated from the core by a boundary that preserves the integrity of the whole.

This is not a contradiction. It is a design pattern.

A stable centre enables a flourishing perimeter. A closed semantic core enables an open ecosystem. A cathedral enables a bazaar – but only if the gate between them holds.

0 Comments

The Final Bit of Engine in Place

25/1/2026

0 Comments

 
Picture
Today, hierarchical key signature overrides were completed. Global key signatures can now be overridden at the instrument level as well as the staff level, as many times within a measure as required, with fully deterministic accidental handling. The updated ADR-0035 documents the algorithm.

This sounds narrow. It isn't. The ability to handle this correctly, without manual intervention or heuristic guesswork, required everything underneath it to be correct first. The last piece going in means the semantic aspect of the engine is complete.

Eighteen months of invisible work, ended.

The focus now shifts to the frontend event system, then to opening windows, etc. The infrastructure phase succeeded by disappearing; the next phase will succeed by appearing. Different disciplines, different anxieties.

I find it difficult to articulate what this moment feels like. The closest analogy I have is from composition: finishing a score and waiting for the first rehearsal.
​
A carefully prepared potentiality, awaiting concretion. 
0 Comments

Why Ooloi Doesn't Need a DAW

21/1/2026

9 Comments

 
Picture
For decades, orchestral playback has been organised around an absence of trust.

The score was not considered precise enough to stand on its own, so it had to be translated. MIDI became the intermediary. DAWs became the place where music was made to behave. Templates grew until they resembled orchestras-in-storage. Machines were added not because the music required it, but because the protocol did.

Ooloi takes a different position.

The score is treated as the authoritative description of the music. Pitch, timing, articulation, phrasing, microtonality and humanisation are not annotations awaiting interpretation elsewhere. They are part of the structure. Playback does not correct the score. It follows it.

For that reason, Ooloi has no MIDI output in the core. Audio is produced entirely by frontend plugins: VST/AU instruments, SoundFonts, synthesis engines. The backend never allocates channels, never bends pitches, never streams audio. It manages musical structure. Plugins turn that structure into sound. The instruments run inside Ooloi, not in an external DAW waiting for MIDI.

Pitch is represented symbolically and exactly. A note is not a frequency approximation or a MIDI pitch with heavy makeup but something like "C###4+50". A sustained chord can contain a continuously glissing inner voice without splitting staves, duplicating instruments, or consuming additional channels. There is no pitch-bend choreography, no controller bookkeeping, no library-specific workaround logic. The DNA soup is gone.

For readers coming from notation, this restores the score's authority. Slurs, dynamics, accents, register and phrasing are no longer hints for later repair. They are the performance.

For readers coming from virtual instruments, this architecture removes entire categories of work that have become normalised:

- No permanent DAW templates holding a full orchestra 'just in case'
- No slave machines preloading instruments that never play
- No CC drawing to approximate phrasing already present in the notation (the work that currently keeps Hans Zimmer's assistants employed)
- No channel allocation strategies for divisi or microtonality
- No waiting for templates to load before hearing a single note

Because the semantic model captures what is actually written, playback plugins can analyse the score and load only what is required. If a piece contains no contrabassoon, no contrabassoon need exist in memory. If a technique is missing in one library, another can be invoked for that passage alone. Routing, balance and reverberation can follow from structure, not from global assumptions.

This is why large template-based setups become unnecessary. Not because of optimisation tricks, but because they were compensating for a semantic gap between notation and sound. The architecture closes that gap.

Do DAWs still matter? Yes, but later. Mixing, sound design, video synchronisation and final delivery remain DAW territory. What changes is that the DAW is no longer required to make the music behave like music.

Ooloi does not replace the DAW.

It removes the need for the DAW to do a job it was never meant to do.
9 Comments

Music for Computer Scientists

10/1/2026

0 Comments

 
Picture
​A few weeks ago I wrote a post explaining computer science terminology to musicians. This is the inverse: an explanation of music notation for programmers who assume the domain is simpler than it is.

That assumption is common. It's also expensive. Most notation software has been written by very competent engineers who underestimated the problem. The result is systems that work impressively until they don't – at which point they fail in ways that seem arbitrary to users but are, in fact, architectural.

This post explains why.

Notation Is Not MIDI with Graphics

Picture
The most damaging assumption programmers bring to this domain is that music notation is a visual layer over time-stamped events. Work with audio software, game engines, or digital music for long enough, and this seems obvious: notes have start times, durations, and pitches. Notation just draws them nicely.

This is wrong in a way that corrupts everything downstream.

Consider two representations of identical duration:

  • (a) quarter note
  • (b) dotted eighth tied to sixteenth

These produce the same acoustic event. They do not mean the same thing. The first is a single rhythmic impulse sustained. The second implies a specific re-articulation point encoded in the notation itself. Performers read these differently. Conductors might even beat them differently. The semantic distinction is not ornamental.

MIDI cannot represent this distinction. It knows only NOTE_ON, NOTE_OFF, and duration. Notation systems built on MIDI-like assumptions inherit this blindness and spend their entire existence fighting it.

Music notation encodes musical meaning, not acoustic events. The implications of this single fact occupy the rest of this post.

Time Is Not Integers

Picture
Programmers reach instinctively for integer representation. MIDI uses ticks – typically 480 or 960 per quarter note. DAWs use samples. The assumption is that any temporal precision can be achieved by choosing a sufficiently small quantum.

This is false for music notation.

Consider a simple case: a quarter-note triplet against regular eighth notes. The triplet divides the beat into thirds; the eighths divide it into halves. To represent both exactly in a tick-based system, you need a tick count divisible by both 2 and 3. Fine: use 6 subdivisions.

Now add a quintuplet in another voice. You need divisibility by 2, 3, and 5: LCM = 30.

Now consider what real music does. Chopin writes nested tuplets. Ferneyhough writes 7:6 against 5:4 against 3:2. Elliott Carter writes simultaneous streams at different tempi. The required tick resolution grows combinatorially:

Picture
where di are tuplet denominators and k is your base resolution. For complex contemporary music, this can exceed 10^6 ticks per beat, at which point integer overflow becomes a real concern and accumulated floating-point error in tempo calculations produces audible drift.

The solution is obvious once stated: rational arithmetic. A quarter-note triplet is exactly 1/3 of a half note. No approximation. No accumulated error. The arithmetic is exact because the representation matches the domain.
​
  • triplet quarter = 1/3 × 1/2 = 1/6 of whole note
  • regular eighth  = 1/8 of whole note

These don't share a finite decimal expansion. They don't need to. Rationals are closed under the operations music requires.

Clojure provides this natively. Most notation software doesn't use it. The consequences appear everywhere timing matters – which in notation is everywhere

The Structure Is Not a Tree

Picture
Programmers expect hierarchical data. XML, JSON, ASTs, DOM trees – the mental model is that complex structures decompose into nested containers. Parent nodes own child nodes. Traversal is well-defined.

Music notation has multiple simultaneous hierarchies that do not nest cleanly.

A single note participates in:
  • Metric structure: beat → measure → section
  • Rhythmic grouping: tuplet → beam group → phrase
  • Pitch organisation: voice → chord → harmonic progression
  • Measure layout: measure → staff → instrument → system → page
  • Notational conventions: slur, dynamic hairpin, articulation group

These hierarchies intersect. They do not nest. Consider cross-staff notation: a chord where some notes appear on the treble staff and others on the bass. Which staff 'owns' the chord? Neither. Both. The question assumes a tree structure that doesn't exist.

Or consider voice exchange: soprano and alto swap pitches mid-phrase. The melodic line (a horizontal relationship) contradicts the voice assignment (a vertical relationship). Both are musically meaningful. Neither subsumes the other.

Grace notes exist precisely at the intersection of conflicting hierarchies. They have pitch (placing them in a voice) but steal time from adjacent notes (making their metric position contextual). They may or may not take accidentals from the main-note context. The rules vary by historical period.

Systems that force music into a single hierarchy spend enormous effort on edge cases that are only 'edge' cases because the data model is wrong.

Context Is Non-Local: The Accidental Problem

Picture
​Here is a concrete example of why music notation is harder than it looks.

Question: Does this F need a sharp sign?

Answer: It depends on:
  1. The key signature (established possibly pages earlier)
  2. What happened earlier in this measure in this voice
  3. What happened earlier in this measure in other voices on the same staff
  4. What happened earlier in this measure in other staves (in keyboard music, an F♯ in the left hand may affect an F in the right)
  5. Whether there's a tie from the previous measure
  6. Whether this is a grace note (different rules apply)
  7. Publisher conventions (some always mark cautionary accidentals, some don't)
  8. Historical period (18th-century conventions differ from 20th-century)

A naive algorithm checks each note against previous notes in the measure. This is O(n) per note, O(n^2) per measure. Fine for simple music.

Now add multiple voices. The F in the soprano might be affected by an F♯ in the alto, depending on conventions. You need to track alterations across voices. If voices cross staves (as in keyboard music), the interactions multiply.

Now add simultaneity. What happens when two voices sound the same pitch at the same instant but with different accidentals? Yet another edge case difficult to catch when imprisoned in the Jail of Recursive Descent.

Now add grace notes. A grace note at the beginning of a measure might be notated at the barline but sound at the end of the previous measure. Does its accidental affect the next measure? Does the next measure's key signature affect it?

The dependency graph for accidentals is not a simple sequence. It's a directed acyclic graph with edges determined by temporal position, voice membership, staff assignment, and notational category. Correct handling requires topological sorting with domain-specific edge semantics.

Most notation software handles this with heuristics: rules-of-thumb that cover common cases and fail on uncommon ones. The failures aren't bugs in the usual sense. They're consequences of architectural decisions made decades ago when the problem was less well understood.

Ooloi handles this through what I call the 'timewalker' – a transducer that processes musical events in semantic order (not storage order) allowing full temporal context accumulation. The accidental algorithm becomes stateless over its input stream: given the same sequence of musical facts, it produces the same decisions. Deterministically. Every time.

The complexity is still O(n · v) for n events and v voices, but the constant factor drops dramatically because traversal and processing are separated. The transducer handles navigation; the algorithm handles meaning.

Concurrency Is Not Optional

Picture
​Here is where most notation software fails silently.

A modern computer has multiple cores. Users expect responsiveness: type a note, see it appear immediately. But notation isn't like a word processor where inserting a character affects only nearby layout. Changing one note can alter accidentals throughout a measure, reflow an entire system, or invalidate cached engraving calculations across pages.

The traditional approach is to serialise everything. One thread handles all mutations. The UI blocks or queues. This is why notation software freezes when you paste a large passage, or why collaborative editing remains essentially unsolved in this domain.

The problem deepens when you consider what 'correct' means during an edit. If a user changes an F♮ to an F♯ in measure 4, the accidental state of measure 5 depends on this change. But if the rendering thread is currently drawing measure 5, what does it see? The old state? The new state? Some inconsistent mixture?

Mutable state with locks doesn't solve this; it creates a new category of bugs. Deadlocks. Race conditions. Heisenbugs that appear only under specific timing. A very difficult work environment indeed, and one where diminishing returns becomes a real impeding factor.

The functional approach is different. If musical data is immutable, the question dissolves. The rendering thread has a consistent snapshot – always. The editing thread produces a new snapshot. There's no moment of inconsistency because there's no mutation.

This is what Software Transactional Memory provides in Clojure. Transactions compose. Reads are always consistent. Writes are atomic. The system guarantees that no thread ever sees a half-updated score.

The performance implications are substantial. Ooloi's STM handles heavy contention without the coordination overhead that serialised systems pay constantly. The bottleneck shifts from synchronisation to actual work.

Real-time collaboration becomes architecturally possible. Two users editing the same passage don't corrupt each other's state; they produce alternative futures that the system can merge or present as conflicts. This isn't a feature bolted on afterwards. It's a consequence of representing music correctly from the start.

Engraving Is Constraint Satisfaction

Picture
Ravel being difficult
​Programmers sometimes assume that once you have the musical data, displaying it is straightforward. Calculate positions, draw glyphs, done.

This underestimates the problem by roughly an order of magnitude.

Consider accidental placement in a dense chord. Multiple accidentals must avoid colliding with each other, with noteheads, with stems, with ledger lines, and with accidentals in adjacent chords. The placement rules aren't arbitrary – they encode centuries of engraving practice optimised for readability.

This is a constraint satisfaction problem. In the general case, it's NP-hard. Real systems use heuristics, but the heuristics are subtle. Gould's Behind Bars – the standard reference on notation practice – devotes many pages to accidental stacking alone.

Now multiply by every other engraving decision: beam angles, stem lengths, tie curves, slur routing, dynamic placement, tuplet brackets, ottava lines, system breaks, page breaks. Each interacts with the others. Change a stem length and the beam angle needs adjustment; beam ends need to follow sit-straddle-hang rules and might both move. Change a beam angle and adjacent voices may collide.

LilyPond's engraving quality comes precisely from treating this as a serious optimisation problem. Its page-breaking algorithm runs a variant of the Knuth-Plass line-breaking algorithm (developed for TeX). Its spacing uses spring-based constraint systems. The 'simple' rendering layer contains some of the most sophisticated code in the system.

Interactive notation software faces an additional challenge: these calculations must be fast enough for real-time editing. You can't spend ten seconds optimising layout every time the user adds a note. The tension between quality and responsiveness has driven much of the architectural evolution in this field.

Historical Conventions Are Not Bugs

Picture
​A programmer encountering music notation for the first time will find countless apparent inconsistencies. Surely these could be rationalised?

No. Or rather: not without destroying information.

A fermata on a note means 'sustain this note'. A fermata on a barline means 'pause here'. A fermata on a rest means something slightly different again. These distinctions emerged from practice. Composers use them. Performers understand them. A system that normalises them into a single 'fermata event' loses musical meaning.

Grace notes in Bach are performed differently than grace notes in Chopin. The notation looks similar. The performance practice differs. A system that treats all grace notes identically will produce correct output that performers find misleading.

Clefs change meaning by context. A C-clef on the third line is an alto clef; on the fourth line, a tenor clef. Same symbol, different staff position, different meaning. This isn't legacy cruft – orchestral scores routinely use multiple C-clef positions because different instruments read different clefs.

Mensural notation (pre-1600) has entirely different rules. The same notehead shapes mean different things. Accidentals follow different conventions. Barlines serve different purposes. A truly comprehensive notation system must handle this or explicitly disclaim it.

The temptation to clean this up, to impose regularity, to treat historical conventions as technical debt – this temptation must be resisted. These conventions encode musical knowledge. Destroying them in the name of consistency destroys the knowledge.

The Performer Is the Point

Picture
​All of this serves a purpose that has nothing to do with computers.

Notation exists to communicate with humans. Specifically: performers who bring interpretation, physical constraints, and centuries of inherited practice to their reading.

This isn't sentimental. It has engineering consequences.

A page turn in the wrong place forces a pianist to stop playing or memorise a passage. This is a functional failure, not an aesthetic preference. The page-breaking algorithm must allow this to be modelled.

Awkward beam groupings obscure metric structure. The performer reads beats through beam patterns. Wrong beaming means wrong emphasis. The beaming algorithm must model this based on the currnent time signature.

Unclear voice leading forces performers to decode the texture before they can interpret it. The system must make voices visually distinct in ways that match musical distinctness.

Engraving choices that are mathematically equivalent may not be performatively equivalent. Two layouts might use the same space and avoid all collisions, but one reads naturally and the other requires active deciphering. The difference matters.

This is why notation is not a solved problem. The specification is 'communicate musical meaning to human performers'. That's not a format spec. It's a design constraint that touches every decision in the system.

Conclusion

Picture
Music notation looks like a data format problem. It is actually a meaning representation problem with a millennium of accumulated context.

The systems that fail – and most fail in some way – do so because they treat notation as something to be processed rather than something to be understood. They model the symbols without modelling what the symbols mean.

The systems that succeed take the domain seriously. They represent time exactly. They handle multiple hierarchies without forcing false containment. They process context correctly even when context is non-local. They treat engraving as a real problem, not a rendering detail. They preserve historical conventions because those conventions carry meaning.

This is why Ooloi's architecture looks the way it does. Rational arithmetic isn't ideology; it's the only way to represent tuplets exactly. Immutability isn't fashion; it's the only way to guarantee that accidental decisions are deterministic. Transducers aren't showing off; they're the cleanest and most efficient way to process a semantic stream with accumulated context. STM isn't over-engineering; it's the only way to achieve both correctness and responsiveness without sacrificing either.

The domain dictates the tools. Not the reverse.

0 Comments

And Only Then

9/1/2026

6 Comments

 
Picture
We have a new year, and it's time to make plans. Ooloi's architecture is now closed, and the work enters a new phase. What follows is a road map, not a schedule – no dates, no promises – but a logical progression.

These stages are different in nature from what has gone before. Conceptually simpler than the deep semantic work (what the music is, how it's represented and manipulated) or the infrastructure (server/client transports and roundtrips, monitoring, certificates). Everything below is comprehensively architectured and implementation-ready. The thinking is done; what remains is execution.

The Preparations

Event-Driven Client Architecture: The nervous system that lets the interface respond without freezing. (ADR-0022, ADR-0031)

Windowing System: Windows, menus, palettes, dialogs, notifications, connection status. The application becomes something you can see and touch. (ADR-0005, ADR-0038)

Multi-Mode Clients: Standalone operation, peer-to-peer connection, shared server connections. (ADR-0036)

Skija / GPU Rendering: The drawing substrate. GPU-accelerated graphics, paintlist caching, lazy fetching. The machinery for putting marks on screen. (ADR-0005, ADR-0038)

Hierarchical Rendering Pipeline: The transformation from musical structure to visual layout. (ADR-0028, ADR-0037)

Plugin System: First-class access from any JVM language. (ADR-0003, ADR-0028)

MusicXML: The first, limited version, implemented as a canonical plugin. Real scores entering the system. (ADR-0030)

And then – and only then – will the first staff line appear.
6 Comments

Six Degrees of Unification

6/1/2026

2 Comments

 
To implement what was described in the last blog post deterministically, ADR 0028, on the hierarchical rendering pipeline, and ADR 0037, about measure distribution, both have been updated. The rendering pipeline now has six stages rather than five.

The additional stage enables the behaviour discussed in the previous post, but also allows slurs, lyrics, dynamics, and other elements that require vertical space to adapt dynamically without introducing layout jitter.

What follows is a brief tour of the six pipeline stages. Each has a clearly defined role, a fixed direction of information flow, and no hidden feedback paths. Together, they transform musical structure into pages without iteration.

If you are a musician, you should be able to follow how the music moves from essential meaning to visual expression. If you are a developer, the architectural choices should be readable between the lines. In both cases, the ADRs contain the full technical detail.

Stage 1: Atoms

Each measure is processed independently and turned into engraving atoms: indivisible musical units that already contain noteheads, augmentation dots, accidentals, stems, articulations: everything that belongs together. A note or a chord is an atom, including everything at a fixed distance from that note or chord. Once an atom is computed, its constituent parts never move relative to each other, though they can be adjusted manually or converted by the user to pure graphics for Illustrator-like manipulation.
​
From these atoms, the system derives three facts about the measure stack: a minimum width, where atoms are simply lined up after one another (guaranteeing that nothing ever collides); an ideal width, based on proportional spacing from rhythmic density; and a gutter width – extra space required only if the measure begins a system. Courtesy accidentals caused by system breaks are accounted for here as space requirements, not as symbols. Ordinary courtesy accidentals are already part of the atom.

​Stage 2: Rhythmical Alignment

All measures in a stack contribute a rhythmic raster: the sequence of atom positions implied by their rhythmic content at ideal spacing. Stage 2 takes these per-staff rasters and aligns them into a single, stack-wide rhythmic grid, so that 'the beat structure' is shared across staves rather than each staff carrying its own local spacing. Because the system has both the rasters and the atom extents from Stage 1, it can adjust the aligned positions so that collisions are avoided while still preserving the intended proportional structure.

​Stage 3: System Formation

With rhythmical alignment complete, the score collapses to a one-dimensional sequence of measure stacks, each described only by stack minimum width, stack ideal width, and possible stack system-start gutter. System breaks are chosen using dynamic programming, and a scale factor is computed for each system so that ideal proportions are preserved as well as possible within the available width. At the same time, system preambles (clefs, key signatures) are fully determined. Apart from these, the stage produces only break decisions and scale factors; no measure content is positioned yet.

​Stage 4: Atom Positioning

The abstract decisions from system formation are applied. Each atom is positioned horizontally by scaling its ideal spacing according to the scale factor of its system. Because atoms were defined with purely relative internal geometry, they can be moved without recomputation. This stage is a pure projection from musical proportions to absolute coordinates.

​Stage 5: Spanners

All connecting elements are resolved against the fixed atom positions: ties, slurs, hairpins, ottavas, lyric extenders. They adapt to the horizontal geometry they are given and never influence spacing upstream. System-start decorations live here as well: courtesy accidentals now appear in the gutter space that was explicitly reserved earlier.

​Actual system heights emerge at this stage as a consequence of what must be drawn – slurs, ties, lyrics, pedalling indications, hairpins – each claiming vertical space as required to avoid collisions. In this stage, braces, brackets, and instrument name indications are also computed for the left margin, as all information required to determine system grouping and extent is now available.

​Stage 6: Page Formation

Systems are distributed across pages using dynamic programming, this time over the actual system heights produced by the previous stage. As in Stage 3, the distribution is optimal with respect to the available space. Once page breaks are chosen, layout is locked and no earlier stage is revisited. At this point, all rendering decisions have been made. The score can now be drawn directly from the cached paintlists.
2 Comments

System-Break Courtesy Accidentals

5/1/2026

0 Comments

 
Picture
Standard practice in many house styles is to show a courtesy accidental at the start of a new system when a tied note continues across a system break. In traditional notation workflows, this is handled manually: the engraver adds or forces the accidental after layout.

That manual step has consequences. A courtesy accidental has width. Adding it can push spacing past a threshold, move a system break, or reflow measures. A small local correction can trigger a cascade elsewhere, sending the engraver back to the same passage again. This is not a matter of care or skill. It is a side effect of heuristic layout combined with post-hoc correction.

Ooloi takes a different path.

Courtesy accidentals that exist because of a system or page break are derived, not edited. Ooloi has user settings to control preferred behaviour – such as whether these accidentals appear at all system breaks, page breaks only, or not at all. The required space is known before layout begins. System and page breaks are explicit decisions, and the distribution pass incorporates these accidentals exactly once.

Edit the score and the layout is recomputed.
Change a preference and the layout is recomputed.

There is no iterative correction loop, no heuristic adjustment, and no manual cleanup phase. Layout-dependent notation is a deterministic consequence of the score, its breaks, and the engraver’s stated preferences.

0 Comments

Arc

4/1/2026

0 Comments

 
Picture
I keep an internal development log. It's not public, partly because publishing it would create expectations about sequence and timing that I have no intention of meeting. But something happened to its structure recently.

For over a year, the log tracked numbered phases. Phase 1 through Phase 6, each with clear boundaries, some of them with sub-phases. The numbers implied I knew what came next.

After Phase 6, I was supposed to implement Phase 7 (event-driven client architecture) and Phase 8 (windowing system). Opening a window was 'weeks away'. That's what the plan said.

That's not what happened.

Something – veteran instinct, fifty years of pattern recognition too deep to articulate – said: complete the semantic layer first. Not the plan. Not what 'agile' methodology recommends. But the foundations needed to handle real musical complexity before anything was built on top of them.

So instead of windows, I implemented the full semantic layer. Time signatures, key signatures, remembered alterations – the systems that determine what accidentals print and when. The hard problems in notation software, the ones that have resisted clean solutions for forty years.

I've written about that work as it happened. What I hadn't written about, until now, is what it revealed about the plan itself.

​The development log no longer uses numbered phases for future work.

This isn't abandonment of structure. It's recognition that the work has entered a different mode. The section heading now reads 'Future Stages' with a note: 'Not numbered phases – sequencing may shift based on what the work reveals.'

That sentence took eighteen months to earn. The plan implied I knew the sequence. The reality is that the architecture now tells me what it needs. The foundations are proven; what follows is revelation, not construction. And I now need to listen.

​For eighteen months, the work was architectural: designing possibilities, extending the system, proving it could handle what I asked of it. That mode of thinking was adaptive. It built what now exists.

That mode of thinking is now dangerous.

The guardrail, however, is surprisingly simple. A single principle: if rendering something changes the semantics of what's being rendered, the logic is wrong. Not 'possibly wrong' or 'worth reconsidering'. Wrong as in 'don't go down that path; retrace your steps entirely'.

This sounds obvious, but it's deceptively easy to violate. The instinct to adjust, to consider alternatives, to make something 'look better' by feeding visual decisions back into musical ones – these reflexes served construction. They now threaten what's been built.

The architecture is now closed. It’s no longer fluid in any sense.​ Rendering reveals; it doesn't renegotiate. I may return to this topic, because the implications run deeper than they first appear.
0 Comments

Twice

31/12/2025

2 Comments

 
Picture
Two weeks ago, the remembered alterations algorithm proved that accidental rendering could be solved deterministically. I wrote about needing to breathe, to get my head around it.

Today, I formally specified measure distribution as ADR-0037. The Knuth-Plass algorithm – the same dynamic programming approach TeX uses for paragraph breaking – applies directly to distributing measures across systems. Not through cleverness, but because Ooloi's five-stage rendering pipeline decouples concerns that traditional architectures leave coupled. By the time the distribution algorithm runs, collision detection and vertical coordination are already resolved. What remains is a one-dimensional sequence with scalar preferences. Textbook problem. Textbook solution.

The algorithm itself is not news. Knuth-Plass has existed since 1981.

The news is that this is the second time.

Two problems the industry treats as inherently heuristic – requiring manual correction, special cases, user-facing options to tune approximations – collapsed into straightforward algorithms. Same architectural properties both times: immutable data, rational arithmetic, explicit stage boundaries, semantic determinism before layout.

Once might be coincidence. Twice suggests the architecture itself is doing something.

For engravers, if this holds: no accidental weirdness. No measure distribution cleanup. No jitter when editing – changing bar 47 won't mysteriously reflow bar 12. Layouts that are globally optimal, not locally adjusted. Proportional spacing preserved by construction. Adjustments for taste, not necessity.

I keep writing 'if this holds' because the proof is mathematical, not yet empirical. The algorithm is sound; the question is whether real scores – Gesualdo madrigals through Berg's Orchesterstücke or Ligeti's Requiem – expose edge cases the staged pipeline doesn't fully decouple. The remembered alterations work is tested. Measure distribution needs equivalent validation.

But the pattern emerging is harder to dismiss than any single result. If two 'impossible' problems turn out to be straightforwardly solvable, how many others?

I don't know yet. That's the honest answer.

Roland Gurt's comment last week – about spacing instability in the programs he uses, where editing one element mysteriously reflows unrelated staves on following pages – started me thinking seriously about this problem. Thanks, Roland.

Happy New Year, everyone.
2 Comments

Knuth-Plass

30/12/2025

0 Comments

 
Today I discovered that measure distribution over systems and pages, thanks to Ooloi’s architecture, can be solved deterministically using the Knuth-Plass algorithm. 

Wow.
Picture
0 Comments

Breather

17/12/2025

5 Comments

 
Picture
If anyone wonders what the above is, it's the deterministic solution to a hairy engraving computer science problem nobody thought was solvable. It's been like that since the 80s. Everybody has just kind of capitulated and lived with a 'it's 95% okay, the rest is impossible' type of mindset and then, as one must in that situation, used rules-of-thumb to reach that 95%. Which is a lot, and a very respectable achievement.

However, it turned out that Functional Programming and immutability - and non-consing push transducers - could solve the problem deterministically. For the first time ever.

​I think it holds. We shall see.

What it would mean for notation? No accidentals weirdness. No cleanup. Consistency. Adjustments for taste, not necessity.

The power of Clojure, of treating the piece as a transforming semantic stream, and of not working with OOP and mutable state.

Now I need to sit down and breathe for a while. Time to go into Hammock Mode. I need to get my head around this.
5 Comments

The Eternal Return

5/12/2025

2 Comments

 
Picture
GX ligatures
​When I began coding Igor Engraver around 1995, the choice of platform was straightforward. Macs were where creativity lived. Windows – clumsy, unintuitive, user-hostile – was for accountants and management consultants. I needed to escape Finale's stranglehold, and I needed the best possible foundation for professional music engraving.

That foundation was QuickDraw GX.

Apple had released something genuinely remarkable: a complete 2D graphics and typography system with sophisticated font handling, Bézier curve operations, transformation matrices, and sub-pixel anti-aliased rendering. For music notation – which is essentially complex typography with thousands of precisely positioned curves – GX was perfect. Not adequate, not sufficient: perfect.

Igor Engraver was built on QuickDraw GX from the beginning. Mac-only, by choice and by necessity. Windows didn't matter. We founded NoteHeads, shipped the software, and believed we'd eventually need to address cross-platform support. But that was a distant concern.

Apple Pulls the Rug

Picture
Then Apple announced that Mac OS X would not include QuickDraw GX.

The technological bedrock simply disappeared. Everything Igor depended upon – the font handling, the curve rendering, the transformation system – would not exist in the next operating system. We weren't just facing a port; we needed to find something equivalent to GX's capabilities whilst making Igor work on both Mac and Windows.

In 1999 and 2000, that combination was extraordinarily rare. Most graphics libraries offered either good typography or good 2D graphics, rarely both. Cross-platform support usually meant compromising on quality. We needed the full GX feature set: anti-aliased Bézier curves, sophisticated font rendering, transformation matrices, professional typography. And we needed it to work identically on Mac and Windows.

I searched. Found something. Used it for the Windows port and the post-GX Mac version. And then, over the following decades, the name simply slipped away into that particular fog where technical details go when you've moved on to other problems.

Twenty-Five Years Later

In 2025, building Ooloi, I wanted to document Igor's history properly. But I couldn't remember the name of the library we'd used to replace QuickDraw GX. I could describe it – commercial, cross-platform, sophisticated 2D graphics, professional typography – but the name was gone.

So I did what one does in 2025: I asked Claude to search the web archives.

The answer came back: AlphaMask Graphics Library.

And then I read who had founded the company.

The Lineage Revealed

Picture
​AlphaMask Inc. was founded in 1999 by Mike Reed and Oliver Steele. Reed had been the tech lead on Apple's TrueType and font system. Steele had been on the QuickDraw GX development team and had led the Apple Dylan project at Apple Cambridge – the former Coral Software, where Macintosh Common Lisp originated.

The people who built QuickDraw GX had left Apple and founded a company to continue that work. When Apple made what I considered a profound mistake in abandoning GX for OS X, the GX team apparently agreed – to the point of leaving Apple entirely to focus on their superior graphics engine.

Whether I knew about Steele's Lisp background when we chose AlphaMask, I honestly cannot recall. I like to think the choice was purely on merit: AlphaMask offered GX-level capabilities in a more decoupled, portable form. It did what we needed. The fact that someone who understood both graphics and Lisp had designed the API might explain why it integrated so cleanly with our Lisp codebase, but that may simply be a pleasant historical detail rather than a decision factor.

Either way, when QuickDraw GX disappeared, I had unknowingly followed the people whose work I trusted.

The Pattern Continues

Picture
​Years later, when designing Ooloi, I chose Skia as the graphics foundation. Modern, open-source, GPU-accelerated, excellent typography, sophisticated path operations, cross-platform. I chose it on technical merit, comparing it against alternatives and finding it superior.

I had no idea that Skia was founded by Mike Reed and Cary Clark – another QuickDraw GX team member – a few years after AlphaMask. Or that Google had acquired Skia in 2005 and made it the graphics engine for Chrome, Android, and Flutter. Or that billions of devices now use Skia for their rendering. Or that the internal name at Apple for Quickdraw GX was - Skia.

QuickDraw GX has had three incarnations: first as itself, then as AlphaMask, then as Skia. The same design philosophy that made GX excellent – abstract graphics model, resolution independence, professional typography – survived through each transformation. I recognised that quality in 1995, in 2000, and in 2025, without realising I was choosing the same team's work each time.

Perhaps this indicates that certain kinds of graphical excellence are simply necessary for music notation, a constant need that has persisted since the last millennium. Or perhaps I'm simply stubborn enough to arrive at the same solutions regardless of how much time passes.

A Curious Timing

​Another detail emerged from the research. AlphaMask was acquired by OpenWave around 2001–2002, and the desktop product was discontinued. OpenWave wanted the technology for mobile browsers, not for professional graphics applications. Support ended, updates ceased.

2002 was also when NoteHeads fell silent.

Whether that timing was coincidental or causal, I cannot say with certainty. Finding a replacement for AlphaMask's capabilities in 2002 would have been extraordinarily difficult – arguably impossible. The engineering effort to rebuild on different foundations would have been substantial. Perhaps the ponytailed pop zombies running NoteHeads at that point gave up when the graphics engine disappeared. Perhaps they simply declined to invest in solving the problem. I don't know if we'll ever have a definitive answer, and frankly, the question is less interesting than the pattern it reveals.

What This Means for Ooloi

​The reassuring aspect of this circle is that it cannot break the same way again.

Skia powers the rendering in Chrome, Android, Flutter, and countless other applications. It has billions of users. It's open-source, BSD-licensed, maintained by Google and a broad community. Even if Google stopped development – which won't happen, as Android depends on it – the codebase is available, the expertise exists, and the user base is large enough that maintenance would continue.

Similarly, Ooloi runs on the JVM, which has multiple vendors: Oracle, Azul, Amazon, Microsoft, IBM, Red Hat, Eclipse. Battle-tested is a trite phrase, but it's accurate here. The JVM has been refined for nearly three decades across billions of deployments. It provides capabilities – proper concurrency models, cross-platform consistency, mature tooling – that enable much of Ooloi's architecture.

Everything Ooloi depends upon is either open-source with massive adoption or has redundant commercial vendors ensuring longevity. This isn't accidental. This is architectural design informed by what happens when foundations disappear.

The Unifying Thread

Looking back across thirty years, there appears to be a unifying pattern that I wasn't consciously aware of whilst making these decisions. A consistent need for graphical and typographical excellence. A recognition of quality when it appears, regardless of who built it or where it came from. A preference for sophisticated abstractions over quick implementations.

Perhaps I've learnt something during that time about building software that endures. Or perhaps I'm simply persistent enough to keep arriving at similar solutions when faced with similar problems. The distinction might not matter.

What matters is that the circle closes. The technology that made Igor Engraver possible in 1995 has evolved, through the hands of its original creators, into the technology that makes Ooloi possible in 2025. And this time, the foundations cannot be deprecated on a whim or acquired into oblivion.
2 Comments

The Belly of an Architect

5/12/2025

0 Comments

 
Picture
And my heart yearns baffled and blind, moved vainly toward thee, and moving  
As the refluent seaweed moves in the languid exuberant stream


— Algernon Charles Swinburne, 'Hesperia' (1866)
​Oh dear. Here's another quasi-philosophical blog post from the developer who still hasn't put anything on screen. You know the genre: intricate justifications for why invisible work matters, peppered with references to Victorian poets to make the whole enterprise seem less like procrastination and more like... what? Research? Vision? Architectural discipline?

I get it. A year and a half in, I want to see five lines and a treble clef. Instead, I get Swinburne and ruminations about streams. Fair enough. But something actually happened in the last few weeks that might explain why there's still nothing to look at — and more importantly, what that absence created space for.

After completing the backend infrastructure, I could have drawn the staff. Five lines, treble clef, a few notes on screen — visual feedback that would make the work feel real, tangible, moving forward. Every instinct pointed that direction. Show something. Prove it works. Keep momentum.

I chose accidentals instead.

The Architect's Hunch

​Why? Because after decades of building systems, you develop a sense for what needs to happen when. Not through conscious analysis, but through pattern recognition too deep to articulate. The commercial world calls this procrastination: 'Show something now. Validate with users. Iterate visibly'. But architectural intuition operates on different timescales. Sometimes the right move is to resist visibility until foundations are provably correct.

I felt compelled — and I mean that seriously, as in compelled — to solve accidental calculation before implementing rendering. Not for rational reasons I could enumerate in a planning document, but because something about the architecture whispered that accidentals would either validate everything or expose problems that couldn't be patched later.

Scary stuff, that. Betting 18 months of invisible work on a hunch.

​What Accidentals Actually Are

PictureThe usual suspects
​If you're not a musician, accidentals are the sharps, flats, and naturals that modify pitches within a measure. Simple enough in principle: once you write a sharp on a note, subsequent notes at that pitch remain sharp until cancelled or the measure ends.

​In practice, however, it's fiendishly complex.


Every notation program faces having to handle this complexity. Most handle it through special cases, heuristics, and lots of user-facing options for 'accidental behaviour in situation X'. The complexity emerges not from incompetent developers — the best commercial systems are built by brilliant people — but from architectural constraints where mutable state and rendering-first design make comprehensive solutions intractable.

I wanted to know if Ooloi's architecture could do better. Not through cleverness, but through correctness. If you find the word 'correctness' problematic, as in 'hyper-focused proto-Aspie obsession', bear with me for a paragraph or two so I can explain.

The Languid, Exuberant Stream

​Swinburne's metaphor turns out to be architecturally precise. The 'languid exuberant stream' operates on two dimensions simultaneously.

There's the mechanical dimension: Ooloi's timewalker is exuberant, powerful, a computational firehose processing enormous amounts of musical data. The machinery underneath is doing extraordinary work — coordinating temporal sequences, managing state across hierarchies, maintaining correctness through thousands of simultaneous operations.

And there's the conceptual dimension: languid, unhurried, effortless. When I actually write code, I'm asking simple questions: 'Does this note's accidental equal what we remember at this pitch and octave?' No wrestling with complexity, no thinking about coordination, no managing the machinery. Just natural musical questions that flow without effort.

The division is absolute. The architecture provides the exuberant power. I provide the languid purpose. They meet somewhere in the middle, and problems that should be hard simply... aren't.

The Great Contrast

​Commercial development forecloses this path. When visual pressure exists, you cannot separate these dimensions. You're forced to think about rendering whilst solving semantics, to optimise display whilst establishing correctness, to show something whilst building everything.

The result isn't wrong exactly — it's approximate. When architecture makes comprehensive solutions intractable, you end up with many options that tune approximations. The settings don't control behaviour; they compensate for architectural constraints.

This isn't criticism of commercial developers. They're working under pressure that makes this separation structurally impossible. Ship something. Show progress. Iterate based on user feedback. Entirely rational within commercial constraints; completely incompatible with certain architectural possibilities.

Working 'in the dark' created space for pure separation. The semantic foundation could reach mathematical correctness without visual compromise. When those dimensions finally unite, capabilities emerge that mixed approaches cannot achieve.

Not different in degree. Different in kind.

The Achievement

​The real achievement isn't solving accidentals. It's proving that separation of concerns works. That languid conceptual flow on exuberant mechanical power enables solutions that mixed approaches cannot reach. That working in the dark creates space for architectural correctness that commercial pressure forecloses.

The accidentals solution is evidence, but he architecture is the achievement.

The stream is languid and exuberant simultaneously. The mechanical power flows abundantly underneath. The conceptual purpose moves effortlessly above. They meet where music becomes mathematics and mathematics becomes music, and problems that should be hard turn out not to exist at all.

Because the architecture is right.

And now I can begin to trust it in earnest.
0 Comments

Re-Reading Read

2/12/2025

2 Comments

 
Picture
​I'm re-reading Gardner Read's Music Notation from 1974. I bought my copy in 1977, which makes me 16 at the time – old enough to take it seriously, young enough to believe comprehensive understanding was achievable through diligent study. Later, this book would influence Igor Engraver's formatting decisions, though not always in ways I'd care to defend today.

What strikes me now is what Read doesn't cover. There's nothing about ledger line thicknesses, actual distances in spaces between noteheads and accidentals, sit-straddle-hang rules, slur curvatures, or tie formatting. None of the engraver-level detail that Elaine Gould's Behind Bars (2011) and Ted Ross's The Art of Music Engraving & Processing (1970, but I didn't discover it until much later) document so comprehensively. Read gives you musical orthography – what symbols mean and when to use them – but not typographical execution.

What Igor Got Away With

​When Magnus Johansson published examples of Igor's output on NOTATIO recently, I experienced that particular species of discomfort that comes from seeing your 25-year-old work through 2025 eyes. The ledger line thicknesses were wrong. The beam slants were inconsistent. We clearly knew nothing about sit-straddle-hang.

So what made Igor well-received? Not typographical perfection, that's certain.

First, integrated parts. Only Composer's Mosaic had them at the time, and Igor had them long before Finale or Sibelius. This alone solved a workflow problem that cost professional copyists days of manual labour.

Second, the user experience didn't fight the music. After spending years with Finale on The Maids – full score, parts, piano reduction – I ended up hating Finale. I've called it 'as user-friendly as a cactus' more than once. Creating something that didn't actively work against the creative process was evidently a sufficient innovation.

Third, note entry was fast and powerful. The modal Flow Mode interface that would later vanish completely from notation software for 23 years gave professional users substantial note entry and editing speed improvements. When you're saving many hours per score, you'll forgive a few ledger lines being slightly too thin – and there was a setting for that anyway.

Fourth, we had stellar MIDI playback and a semantic model that made things consistent rather than a collection of rules-of-thumb. That alone provided predictability. And everything could be adjusted – the absence of automatic sit-straddle-hang rules just meant more manual interventions.

The landscape in 1996 made these trade-offs reasonable. The streamlined experience outweighed what we today immediately see was missing.

The Bar Has Been Raised

​2025 is not 1996.

The leading programs have improved considerably since 1996. They're genuinely competent at beam placement and formatting – not flawless, but competent enough that egregious errors are rare.

A new program entering this landscape must get the foundations correct from day one. Beam placement, slurs, ties, accidental positioning – these must be flawless, not 'good enough to ship'. The field has progressed, and users' baseline expectations have risen accordingly.

This is as it should be. But there's something deeper that hasn't been solved.

The Semantic Deficit

Here's what I've stated repeatedly: Ooloi is not about 'disruption' or market share. The entire motivation is to escape what commercialism leads to and create something modern, scalable, and architecturally correct from the ground up. Why? Because music notation is an extremely messy and difficult field of computation, and it requires correctness to address its long-standing problems of scalability and accuracy.

This starts with internal representation.

The old programs – and many modern ones still in broad use – were all based on a paradigm inherited from MIDI. MIDI was the standard for pitch representation at the time, and all notation software needed MIDI output for playback anyway. This meant pitches were MIDI numbers (0-127) with attachments indicating whether they were sharp or flat. Figuring out musical context – for instance, to determine what accidentals to draw – had to be derived from something with no connection to musical structure whatsoever. It had to be inferred from context each time.
​
That's at the root of the problems programs still have with accidentals. The internal representation is designed around the presentation – the visual aspect – not around the meaning, the semantics, of the music.

And of course, MIDI has no concept of microtonality, which is why notation programs struggle with microtonal entry, presentation, and playback.

Furthermore, for duration, these early programs based their rhythmic representation on a raster of 480 subdivisions – ticks – of a quarter note (TPQN: Ticks Per Quarter Note). A quarter note is 480 ticks long in some arbitrary tempo, an eighth is 240, and so forth. This is the equivalent of pixels in a JPEG, which means there's a limit to what the raster can represent.

The number 480 isn't evenly divisible by very many factors. This leads to all the problems we're still seeing in music notation programs. Various kinds of duct tape – rules of thumb, arbitrary rounding, tolerance spans – have to be used. When tuplets are nested, the approximation errors compound. We're still seeing the effects of this unfortunate MIDI heritage in 2025.

A MIDI-derived representation centred on presentation – the visual aspect – will always have difficulty interpreting what the music means. That interpretive layer is essential for presenting it correctly and consistently.

For that, you need a semantic model, which turns this on its head. The representation is 'musically correct' and detached from its presentation. Once this is in place, you can make informed decisions instead of relying on rounding and rules-of-thumb. It also makes things like playback trivial, which in MIDI-based systems is paradoxically complex.
​

Igor's Semantic Foundation

​Igor Engraver was, I believe, one of the first programs – possibly the very first – to use a fully semantic internal model. It was also the first to model the real world by using Musicians playing Instruments, which allowed new and powerful abstractions.

It's interesting that Dorico also has this arrangement, though they call their Musicians 'Players' – but it's the same thing. I have no idea whether Daniel Spreadbury was inspired by Igor here, but it's not unlikely. On the other hand, introducing Musicians/Players into the representational hierarchy is a logical choice once you commit to semantic modelling.

I'm not certain Dorico has a fully semantic model, though it's closer than any other program I know of. LilyPond doesn't, despite its sophisticated batch nature. One telling diagnostic: look at how they handle remembered accidentals for grace notes, and how they treat them rhythmically. Another: how durations are represented. If they're floating-point numbers, they're approximations. For true accuracy in all situations, you need rational numbers – infinite precision, always correct. Anything else eventually leads to problems.

If a program has problems with edge cases or behaves inconsistently when dragging things cross-staff, check how it represents pitch and duration. If floating-point is involved, or rasters of ticks (480 or otherwise), the representation isn't semantic. The program might still handle 95% of hairy accidental placements competently. But when it starts having problems with tied notes across key changes or grace notes at measure starts, you know rules-of-thumb are involved – which means results can never be fully deterministic.

Ooloi is fully semantic with the explicit intention of making results fully deterministic.

This might not matter if you're satisfied with what capable commercial programs achieve today. That's legitimate – they handle about 95% of cases well. But if you depend on the remaining 5%, or if you spend your days as an engraver adjusting those 5% repeatedly, then you understand what I mean by deterministic results saving considerable time.

This Time: No Compromises

Now, on to Gould and Ross – books that weren't available when Igor was created. We'd inevitably have implemented their specifications had we had time. But as you know, I was ousted from my own company by venture capital pop zombies before we could. They thought guitar tablature was more important than correct engraver-level beaming.

This time, there will be no guitar tablature at the expense of correct beaming and orthography. All things in their proper order. Lead sheets are kid's stuff, comparatively speaking, and will be added later as plugins.
2 Comments
<<Previous

    Author

    Peter Bengtson –
    Cloud architect, Clojure advocate, concert organist, opera composer. Craft over commodity. Still windsurfing through parentheses.

    Search

    Archives

    April 2026
    March 2026
    February 2026
    January 2026
    December 2025
    November 2025
    October 2025
    September 2025
    August 2025
    July 2025
    June 2025
    April 2025
    March 2025
    September 2024
    August 2024
    July 2024

    Categories

    All
    Accidentals
    Alfred Korzybski
    Architecture
    Beaming
    Benchmarks
    Clefs
    Clojure
    CLOS
    Common Lisp
    DDD
    Death Of Igor Engraver
    Documentation
    Donald E Knuth
    Dorico
    Dynamic Programming
    Finale
    Fonts
    FrankenScore
    Franz Kafka
    Frontend
    Functional Programming
    Generative AI
    GRPC
    Igor Engraver
    Instruments
    Jacques Derrida
    JVM
    License
    LilyPond
    Lisp
    Localisation
    MIDI
    MPL 2.0
    MuseScore
    MusicXML
    Ooloi
    Ortography
    Pitches
    Platforms
    Playback
    Plugins
    Python
    QuickDraw GX
    Rendering
    Rhythm
    Rich Hickey
    Road Map
    Scheme
    Semiotics
    Sibelius
    Silicon Valley
    Site
    Skia
    Sponsorship
    Transposition
    UI
    Umberto Eco
    Vertigo
    VST/AU
    Wednesday Addams

    RSS Feed

Home
​Overview
Documentation
About
Contact
Newsletter
Ooloi is an open-source desktop music notation system for musicians who need stable, precise engraving and the freedom to notate complex music without workarounds. Scores and parts are handled consistently, remain responsive at scale, and support collaborative work without semantic compromise. They are not tied to proprietary formats or licensing.
​
Ooloi is currently under development. No release date has been announced.


  • Home
  • Overview
    • Background and History
    • Project Goals
    • Introduction for Musicians
    • Introduction for Programmers
    • Technical Comparison
  • Documentation
  • About
  • Contact
  • Home
  • Overview
    • Background and History
    • Project Goals
    • Introduction for Musicians
    • Introduction for Programmers
    • Technical Comparison
  • Documentation
  • About
  • Contact