OOLOI.ORG
Menu

OOLOI

An Organism Evolved.

OVERVIEW

DOCUMENTATION

NEWSLETTER

The Machinery of Taste

12/9/2026

7 Comments

 
In June I published a road map and claimed the list was plumbing, every line of it. It was. Networking, certificates, session persistence, collaboration state, window geometry: nothing on that list would ever appear on a page, and the whole argument for doing it then was that none of it would be sharing the desk when the page finally arrived.

That list is nearly spent, and what replaces it is a different kind of work:
Picture

Where things stand

Everything about a piece except the music itself can now be manipulated from the interface.

That reads like a summary. It's actually a precise statement, and it names a boundary the architecture drew long before this summer. Changes to the things that contain the music – musicians, instruments, staves, layouts, and a piece's own settings – announce themselves structurally the instant they happen, on every connected client. Changes to the music itself defer to the formatting pipeline, which is asynchronous and which doesn't yet exist. One side of that line is now complete, all the way up to the interface. The other is a routing-table entry with no producer and no consumer.

The summer went into the first side. Pieces open and save and close and reopen where you left them. Musicians and instruments are created, numbered, renamed, cloned, reordered and deleted by gesture, with the numbering falling out of the gesture rather than out of a dialog. Layouts assemble into scores and parts. A piece's settings live in a window of its own. A field refuses a value it cannot take, in the field you typed it into, in your own language. Every one of those acts is a single transaction, a single event, a single refetch, a single named step in the Edit menu, and the path is identical whether you are working alone or someone on another continent is working beside you.

The semantic model, in other words, finally has a surface. For two years it existed and could only be reached through tests. It can now be reached with a mouse.

Except for the music. Which is what the rest of the tickets are for:

Finishing what collaboration started

  • #136 — Authenticated piece collaboration
  • #229 — Standalone backend server application

The distributed machinery is proven. Two Oolois on two laptops, one hosting, one connected, editing shared state with undo reaching across the wire and respecting each other's work. What's missing is everything that governs who may do what: invitations, single-use tokens, deep links, JWT identity, role-based access, a host-side collaborator registry, and a piece picker that shows a guest their own slice of a catalogue rather than someone else's filesystem.

There's an obvious objection to doing this now. Collaboration is the exception. I fully expect the overwhelming majority of Ooloi users to run it standalone on one machine and never invite anybody to anything. Why finish the exception before the rule?

Precisely because it is the exception. Work that sits half-done at the edge of a system does not stay quiet. It surfaces as a special case in every subsequent decision, and it surfaces at the worst moment, which is when you're concentrating on something else. Finishing it now means never thinking about it again. When the engraving work begins, the access model will be behind me rather than beside me, and single-user operation will be what it already is architecturally: a collaboration group whose size happens to be 1.

A word about #229, because its title invites exactly the wrong inference. The standalone server isn't a second product. Not a separate edition, not a server variant, not a codebase carrying features the desktop application lacks. It's the backend half of ordinary Ooloi – the same backend, the same components, the same behaviour – packaged to run on its own: in the cloud, on a conservatory's machine, on a collaborator's laptop in another city. There's no new functionality involved. The administrator connects to it as a client, through the same authentication path as anyone else, which is the whole reason it comes after #136 rather than before.

The substrate

  • #7 — Plugin system.
  • #8 — Skija integration.

Ooloi 'eats its own soup'. The formatters that draw a page are plugins, on exactly the same terms as anything a third party might write: the same API, the same performance, the same access. There's no privileged internal path that the public one imitates. This is not a gesture towards openness; it's the mechanism by which engraving conventions stay arguable. If you disagree with how Ooloi slopes a beam or hangs a secondary, and the settings provided aren't enough or your formatting philosophy is different, you can replace the formatter, in whichever JVM language you prefer, and Ooloi doesn't notice the difference.

Which is why the plugin layer precedes the drawing. It also precedes MusicXML, because the importer is itself a plugin, and it'll be the first serious test of whether the plugin surface is genuinely sufficient rather than merely declared to be.

Skija brings Skia into Ooloi: the graphics engine behind Chrome, Android and Flutter, and the third incarnation of a lineage that runs back through AlphaMask to QuickDraw GX, which is where Igor Engraver began. I have written about that circle before. What matters here is narrower. Until Skija is in, Ooloi cannot draw anything at all. Not a stave, not a notehead, not the small music example that ought to sit inside a preferences dialog showing you what a setting actually does to the page. The layout windows currently open onto a considered and entirely empty surface. Skija is what fills them.

The page, assembled

  • #6 — Hierarchical rendering pipeline.

The full specification is in ADR-0028 and I will not rehearse it here. What's worth stating is the order of implementation, because it reads rather like a page assembling itself:

First blank pages. Then the text on them: titles, headers, footers, page numbers. Then systems, with their instrument names, brackets and braces. Then staves and their staff lines. Then the measures on those staves, which is where noteheads, stems, flags, dots and accidentals first appear.

And spanners last. Ties, slurs, hairpins, lyrics, ottavas, and above all beams, which are the hardest of them and the most revealing of a system's quality.

That sequence isn't an implementation convenience. It's the pipeline's own structure: everything that can be computed independently, computed first and in parallel, and everything that depends on final positions deferred until those positions are fixed. The page is built the way the architecture says a page is built.

The Turn

Here is what actually changes, and it's not the subject matter.

For two years an argument could be settled by running something. Does the traversal allocate? Measure it. Does the accidental algorithm handle a grace note at a barline under a hierarchical key override? There is a test, and it either passes or it doesn't. Determinism is a property you can demonstrate. When I wrote that two supposedly intractable problems had collapsed into ordinary algorithms, the claim was checkable, and anyone who doubted it could check it.

Nothing in the next phase works that way.

No test will tell me a beam slope is beautiful. No suite will fail because a slur's curvature is slightly graceless, or because the white space on a page reads as mean rather than generous. The machinery that has caught every error for two years is about to fall silent on precisely the questions that matter most, and the only instrument left is a musician's eye. Mine first. Then, with any luck, other people's.

And the ground underfoot isn't solid in the way the engine's was. Staff notation is roughly a thousand years old. Printed engraving as a craft is five hundred. The copperplate house styles I'm actually implementing are perhaps two hundred and fifty. Each layer left conventions behind. They don't agree with one another, and they don't always agree with themselves: Ross devotes some fifty pages to beaming and contradicts himself more than once within them, and where Ross and Gould differ, they differ because the profession differs. Bärenreiter is not Durand. Neither is wrong.

So the design consequence was settled in advance. Engraving rules live as editable data rather than as conditionals buried in source. House styles are configuration. Formatters are replaceable. Where taste genuinely differs, Ooloi takes a position and makes the position adjustable, because the alternative is to pretend a live craft is a solved one.

That's the work now: the objective machinery in service of something irreducibly subjective. It's the part I have been waiting two years for, and it's also the part where merely being right is no longer sufficient.

Tempo

A practical note. I've just begun a new client placement, with a complex system to get into production, and it's absorbing a great deal of attention. On top of that my AWS DevOps Professional certification is due for renewal, and I take the AWS exams seriously enough to actually study for them rather than turn up and hope. The next month or so will therefore be slower here than the last three have been.

This does not worry me in the slightest. The summer's pace was fast and, if anything, accelerating: subsystem after subsystem closed and stopped asking to be thought about again. The project's in a good place. A quiet month against that background is a quiet month, not a stall.

Coda

The anxiety inverts here, which I hadn't quite anticipated.

Invisible work fails by remaining invisible. You solve something genuinely difficult and it vanishes into infrastructure, and the better you have done it the less anyone can see. I have written about that loneliness before.

Visible work fails by being looked at. Every judgement I make about a beam, a slur, a page, will be in front of musicians who have spent their lives reading engraved music and who will know immediately, and without being able to say why, whether it's right.

I'd rather have that problem. It is, after all, the one I set out to have.
7 Comments

Politely Declined

6/9/2026

5 Comments

 
Above is a demonstration of how Ooloi handles wrong input. What happens is this:
​
  1. I remove the @ from a perfectly legal email address.
  2. Nothing happens until I leave the field or press Enter or Tab.
  3. At that point validation takes place, and the field is found to be illegal.
  4. It's coloured red, fading over ten seconds.
  5. An error notification appears explaining the error. It too fades after ten seconds.
  6. Most importantly, the erroneous value is restored to what it was before the edit.
    ​
So no setting can ever hold a wrong value except while it's being typed. The interface stays calm. You aren't prevented from leaving the field and you aren't boxed into a mode. Ooloi discreetly refuses the change, tells you why, and lets you carry on as you please.

The second video is for Andreas Kågedal, who spotted in the comments to the last post that the parts kept their old names after the musicians changed theirs. That turned out to be the last seam in the Piece Window's view of the semantic model, and it's now closed: the parts follow the musicians. A name you've typed by hand is left alone; empty the field and Ooloi refills it with the computed value.
​
With this, the Piece Window and its Preferences editor between them reach every aspect of the piece itself: creating, cloning, renaming, configuring, all by gesture, and not a dialog in sight.
5 Comments

We Have a Piece Settings Window

31/8/2026

5 Comments

 
The same string quartet as last time. I open the piece's settings, go to the Numbering tab, and move the numeral to the other side of the name. '1. Violin' becomes 'Violin 1' in the Musicians pane as I do it.

Which is what Magnus asked for in the comments to the last post. What's actually new here is narrower than it looks: the setting has existed for months. Numeral form, placement and full stop were specified, implemented, and held by thirty-seven tests, and nothing in the running program could reach any of them. A piece could be set to Roman numerals only from a test. The window is what makes the last unreachable part of the semantic model reachable by a person.

'1. Violin' is the German ordinal form and 'Violin 1' the Anglo-American one. Ooloi holds an opinion about neither.

The Accidentals tab goes past on the way. Pause there if you're curious.
5 Comments

Layout Windows and What Owns Them

29/8/2026

4 Comments

 
A short report from the workbench: the finer points of layout windows and how they relate to the piece window that owns them. What's happening in the video:
​
  1. I create a string quartet by first creating musicians for the two violins, the viola and the cello. Note how the violins are numbered automatically.
  2. I then create the score by dragging all the musicians onto the layout pane.
  3. A layout is created: I flip it open briefly so we can see the musicians inside.
  4. I then drag each musician across individually to create their parts.
  5. All of the above you've seen before: it's literally everything required to set up a complete score and its parts. However, what follows is new, and is what I've just finished.
  6. I select all the layouts and press Enter to open them. A double-click would have done the same for the entire selection.
  7. The layout windows open staggered, so you can see them all.
  8. I delete the cello part, and its layout window closes with it.
  9. Undo brings the cello part back: it returns to the list of layouts, and I can open it again.
  10. Finally, I click the piece window's close button.
  11. It asks for confirmation, the piece being unsaved.
  12. When I confirm, the piece window and all its open layout windows fade away simultaneously, as a single unit.

That last moment is the point of the exercise. A layout window isn't a free-standing window that happens to show part of a piece: it belongs to the piece. Ownership is why deleting a part closes its window, what undo restores, why Save acts on the piece a window belongs to rather than on whatever happens to be in front, and why quitting takes the whole workspace away and gives it back when the piece is next opened.

So the piece window is finished, down to the edge cases and the pace at which things appear and disappear. Later today I start on the Piece Preferences window, which follows the Application Settings window closely enough that most of the path is already trodden.

(Ignore the notifications in the lower right. They're for development.)
4 Comments

Preparing the Ground

27/8/2026

2 Comments

 
There is a Swedish expression, kratta manegen: literally, to rake the circus ring before the performance begins. Prepare the ground, clear away what shouldn't be there, make the arena ready.

For rather more than two years, that's what Ooloi has been doing.

This week I finally reached the place where I can open The Maids, double-click its score layout in the Piece Window, and watch a Layout Window appear. It contains almost nothing: a subtle background colour and a large, empty, scrollable surface.

Excellent.

This is where the music will be engraved. The rendering pipeline, spacing algorithms, geometric constraint solving, paintlists and Skija drawing will all end up here. The fact that the window is empty is precisely the point. Everything underneath it already exists.

I've also spent the past few weeks raking the arena in the other sense: finishing the Piece Window, completing undo and redo around its operations, tightening failure handling, localisation and editing behaviour, and removing the small architectural exceptions that would otherwise have followed us into the graphical work.

So here we are.

The ring is empty. The stage is set.
2 Comments

For the Babel Fish Out There

20/8/2026

4 Comments

 
Picture
Ooloi's interface now speaks twenty languages (with two national variations), with varying degrees of confidence. I've added a Translator's Guide for the native-speaking musicians who can tell it when it's talking nonsense.
4 Comments

Time Doesn't Stop

16/8/2026

2 Comments

 
Picture
Revisiting a corner of the undo machinery this week, I noticed something I'd never thought about. A user may well see the Undo menu item change while the menu is open in front of them: the text rewrites itself as they read it.

​I like that. It says that time in Ooloi carries on rather than waiting to be asked.

My first thought was that only people working together on the same piece would ever see it. Somebody else edits, and the menu keeps you honest about whose action you're about to reverse. Then I remembered the rule the whole interface is built on, which is that Ooloi never freezes and never boxes you in.

Which means it will happen to people working alone. A plugin importing a large Finale score exported as MusicXML works away in the background while you carry on with something else; when it finishes, a great deal has entered the piece, and the undo chain changes accordingly. If the menu is open, you watch it change.

​Saving is the same: a large score writes itself out without the machine going away from you while it does. That's also what makes autosave worth having rather than something you switch off in irritation, since it needn't interrupt you, whatever the size of the piece. So are preferences, application and piece alike, where there's no modal box and no OK button, the change taking effect as you make it and everything displaying it following suit.

Which set me wondering what all this will be like for an engraver who has spent twenty years with programs built the other way.

A modal dialogue box stops your time. While one is up, the rest of the program isn't somewhere you can act: there's the question in front of you and nothing else, and the moment you come back to is the moment you left. Almost everything any of us has ever used is built out of these small suspensions, and a working day becomes a sequence of them with work in the gaps. Not that anybody chose it. It was the only thing on offer, and for the most part it was honest, since nothing was going on behind the dialogue anyway.

What interests me is the question underneath. If a tool can attend to one thing at a time, you learn to bring it one thing at a time, and composing or engraving isn't like that. You defer the expensive operation until the passage is finished, because starting it would cost you the room to work in. So what becomes of those habits when the technology stops requiring them, and time simply doesn't stop?
2 Comments

Delete These Five Staves?

1/8/2026

4 Comments

 
Picture
The Piece Window is essentially finished. What's left is bits and bobs: a set of rename tests, the shells of the layout windows (for now a title over an empty page, since the page has nothing to put on it yet), one drag-resolution routine that predates the shared one and hasn't been folded into it. Nothing on that list is interesting, which is the point of it being what's left.

It's been an intense stretch. The Piece Window is where the semantic model becomes operable: you create players, give them instruments, add staves, assemble scores and parts, decide who doubles on what. Doing all of that by gesture, with no modes and no dialogs, forced a distinction into the open. There are the things that contain the music, and there's the music itself. Membership, ordering, identity, naming, configuration: each of those announces itself the moment it changes, synchronously, as one event. The music says nothing directly and defers instead to the formatting pipeline. Two channels, and neither can express the other's business. That's now a property of the system rather than a habit of mine, and it emerged from building gestures rather than from contemplating architecture.

Work at that pitch leaves a wake. Over the past few weeks I've raised a dozen or so tickets in passing. None of them is urgent. All of them are real.

For readers who don't build software: the general term for this is technical debt. Some of it is debt in the strict sense, a shortcut taken to keep moving and whose cost doesn't disappear but is merely postponed, accruing interest, because by the time you come back other things have been built on top of it. The rest is small wrongnesses noticed in passing and not stopped for: cheap today, expensive later, and invisible until something else fails on account of them. A project that never pays it down eventually spends all its time servicing the debt rather than doing anything new. That is, in a sentence, the story of every notation program that has hit its ceiling. (Yes, Finale, I'm looking at you.)

So before I close the Piece Window I'm going through the list. And I'm enjoying it enormously, which brings me to Polish genitive plurals. (Major language nerd alert at this point.)

Ooloi ships in twenty-two locales (roughly: 'languages'), and the translation machinery is the industry-standard gettext, which handles counting properly. A message with a number in it declares a singular and a plural form, and each language declares how many forms it actually has and supplies a rule for choosing between them. English and Swedish have two, split at one. French and Brazilian Portuguese have two, but treat zero as singular. Czech has three. Polish and Ukrainian have three, on rules involving the last digit and the last two digits. Icelandic has two, with a rule that would look like a typo if you didn't know Icelandic.
Picture
​A single confirmation message, 'Delete this stave?' and 'Delete these %{n} staves?', turned out to be wrong in four of the twenty-two. The most instructive failures were in East Asia.
Picture
​Japanese, Korean and Chinese declare a single form. Their nouns aren't inflected for number, so a count has nothing to choose between, and whatever sits in the first slot is what every reader sees, for one stave and for fifty. Japanese and Chinese had a polite singular sitting there, so deleting five staves asked whether you wanted to delete this stave, and the count was silently dropped on the floor. The plural translation underneath was perfectly good and could never be selected by anything.

Korean had it right, and the fix was to follow Korean's lead: a single form that names the count, which reads correctly for one as well as for five, there being no separate singular for it to displace.
Picture
Polish and Ukrainian were subtler and needed a native ear rather than a rule table. Polish has three forms, and the third of them, which covers five staves and twenty-five but not twenty-two, takes the genitive plural; the demonstrative then has to agree with it, tych rather than te. Ukrainian had the two-to-four form carrying a genitive where it wants a nominative plural.
Picture
Picture
Picture
There's a reason I'm being this fussy about a dialog box. The people who'll maintain these files, and who I hope will add the languages I haven't got, are musicians and engravers rather than professional localisers. Someone who notices that Ooloi's Hungarian is stilted, or that nobody in Lisbon phrases it like that, should be able to open the file, find the string, and see straight away what's wrong and how to put it right, without knowing a line of Clojure. That puts a real obligation on the files themselves: correct plural counts, honest context labels, entries that read as language rather than as data.

It also means every existing entry is a worked example whether I intend it as one or not. Anyone adding Catalan or Turkish or Russian will copy the shape of what's already there, and a wrong shape propagates. A Japanese entry carrying a plural form that can never be selected quietly teaches the next contributor to write one too.

And because a translation that can't be selected is indistinguishable from a translation that doesn't exist, the fix comes with tests that resolve the actual message, in the actual locale, at a count that exercises the rule.
Picture
This sort of thing is a holiday after months of transaction boundaries, event coalescing and a deserialisation bug that turned out to hinge on a hyphen becoming an underscore in a compiled class name (a process tecchies call munging). Each item is small, bounded, verifiable, and improves something a real person will notice in their own language. I recommend it to anyone emerging from deep water.

When the list is empty, the Piece Window closes as a milestone in its own right: the semantic gestures are settled, and with them the vocabulary for manipulating a piece. Everything that follows is new operations through that vocabulary rather than a new way of working.

Things are moving quickly now. Touch wood.

After the Piece Window comes the plugin system and MusicXML, which is when the engine finally has real orchestral scores thrown at it in volume, by an importer written as an ordinary plugin against the public API with no privileges of its own. Then Skija, the rendering pipeline, and at long last the notes.

But first: the Swedish summer is glorious this year, unreasonably so, and I intend to spend a week or two of it nowhere near a computer. Some air, and then the page.
Picture
4 Comments

Doublings

25/7/2026

2 Comments

 
Picture
The above is the instrumentarium of Richard Strauss's Elektra, from the 1916 edition published by Adolph Fürstner. I took a photo from my score, which I got in 1979, a lifetime ago.
​
​It's in glorious 'Germtalian', typical of the time: an eclectic mixture of Italian, German, and a few bits of Latin thrown in for good measure. Nothing unusual, and contrabassi is still acceptable today, just meno corretto according to modern Italian dictionaries: the preferred spelling is contrabbassi. And nowadays, the Italians prefer controfagotto to contrafagotto. But 'secundi', on the other hand, is pure Latin and plain wrong. Whether this comes from Strauss himself or from his German publishers is difficult to determine. 

This demonstrates that correct and incorrect are the wrong axes for score conventions, which have deep cultural roots encompassing multiple truths simultaneously. All depending on the understanding of everybody involved, from composer to publisher to engraver.

And then there's of course the wholly absurd. Every musician is familiar with the term con sordini, with mutes. But when you see 'senza con sordini' in a score, you know you're dealing with an amateur, or at least with someone who's misunderstood something very basic. But the masters themselves are not exempt: I think it was Stockhausen who wrote So schnell wie möglich (as fast as possible) in a piano piece (possibly Klavierstück X) only to follow it by Noch schneller (even faster) a few bars later. Correctness in music is a many-faceted thing. Logic can be obscure, conventions are multi-dimensional and clearly overlap, and instructions can be psychological rather than factual, as in the Stockhausen example. All this must be embraced.

But I'm digressing, for the purpose of this post is to illustrate how musicians are numbered: first flute, second flute, and so on. That's one dimension. But then musicians can play more than one instrument: they have a primary instrument and then they double on one or more auxiliary instruments. That's the second dimension. And the numbers in one dimension don't map directly onto the other.
​
Look at the very first two lines. There's a dedicated piccolo player (ottavino) and then there are three normal flutes. Strauss numbers them Flute I, II, III. But then the first flautist also doubles on piccolo. The dedicated piccolo player is Ottavino I, and the first flautist doubles on Ottavino II. Two dimensions.

You see the same thing in several places: Oboe III doubles on Cor Anglais, which takes no number since there's only one. On the other hand, the four Wagner Tubas in B♭ and F double on French Horns V, VI, VII, and VIII.

All these things must be correctly numbered in the score and parts, whether you choose to use Arabic or Roman numerals. Strauss uses Roman numerals as was the custom at the time, but conventions varied and do so still.

This can be time-consuming and complicated to set up in traditional scorewriters and involve modes requiring you to do some parts of your setup in one tool and other parts in other tools or dialogs. Ooloi does it for you automatically while you drag and drop. The following video shows how, using the Piece Window.
For the technical specification, see ADR-0054: Automatic Semantic Naming and Numbering of Musicians and Instruments. It is written to be readable by musicians and engravers, not only by programmers.

It was designed and implemented in a single day. That's possible because the architecture underneath it was already there: immutable data, a transactional model, and a gesture layer that composes structural edits atomically.
Picture
It works in the other direction as well, of course
2 Comments

What Undo Remembers

21/7/2026

8 Comments

 
Picture
The anniversary post ended with a promise that the next stretch would be brighter. So here, by way of keeping it, is Verklärte Nacht.

What you're looking at is a string sextet set up in the Piece Window: two violins, two violas, two cellos, their names arriving in German from the Instrument Library, which carries its contents in many languages and supplies whatever the piece prefers. On the right, a score of six musicians and the individual parts. And above it all, the Edit menu is open, which is the actual subject of this post. Note that the undo and redo entries are named. 'Undo Change Reorder Instruments'. 'Redo Change Language filter'. The menu tells you what will happen before you press anything, and what it says is what occurs.

The Piece Window work continues, and as usual the work clarifies the road ahead. Building its full functionality meant implementing undo and redo for everything it does, and doing that forced a question into sharp focus: what, exactly, belongs in an undo chain?

The answer Ooloi gives is strict. An undo entry is a record that the piece changed, together with the means of restoring what it was. Nothing else qualifies. In particular, selections don't. Selecting a musician, a stave, a measure changes nothing about the piece; it changes something about you. A selection is a fact about a user, and it lives and dies with that user's session.

In a single-user program this distinction can be fudged, and the temptation to fudge it is real: selections feel like actions, actions feel undoable, and a naive implementation will happily record them. Collaboration removes the temptation by removing the ambiguity. In Ooloi, the undo history of a piece is shared, because the piece is shared. When you undo, you're operating on the same history I see, and the backend applies the reversal for both of us. Now imagine selections in that chain. Your Cmd+Z would unwind my mouse clicks. The history would silt up with everyone's browsing, and undoing one wrong note would mean stepping backwards through an afternoon of other people's attention. Nobody would design that on purpose. The multi-user case doesn't introduce a new rule; it makes the existing rule impossible to ignore.

And there is a recognisable failure mode on the other side of it. Record the non-events, and the chain grows long and cluttered; the honest remedy would be to stop recording them, but the tempting remedy is more interface: give the user a window onto the history itself, so they can survey the sediment and leap across it. When undo needs a map, something has gone wrong with the territory.

So Ooloi's chains stay short and truthful. Every entry is a real change to shared state, named and localised, and the same history is visible to every connected client. The host can undo a guest's edit; the reversal propagates back to the guest. That was the substance of the two-laptop proof in June, and the Piece Window has now inherited all of it.

Which brings me to the point of this progress report: with this work, the undo and redo machinery is complete across the entire application. The full design is recorded in ADR-0015, but the shape of it is simple. The backend keeps one coordinated history per shared resource, whether that's a piece or the Instrument Library. The frontend keeps a separate local history for what's genuinely local, such as appearance and panel arrangement, since your dark theme is no more a fact about the piece than your selection is. A single Cmd+Z serves both; a small router decides which history is current, and the menu always shows the answer before you commit to it.

It also holds for plugins, and this is worth dwelling on for a moment. A plugin modifies a piece through the same API as everything else, and the mutation registers its own undo step as it commits, described and localised like any native operation. Plugin authors write no undo code at all. They can't forget it, can't implement it inconsistently, and can't corrupt the shared history, because there's no surface through which any of that could be expressed. This is the house method applied once more: the wrong thing isn't forbidden, it's inexpressible.

As for the sextet on screen: two people walk through a cold night, something difficult is said plainly, and the music ends warmer than it began. As a description of implementing undo in a collaborative system, that will do.
8 Comments

My Life Right Now

13/7/2026

6 Comments

 
Picture
What you're looking at is a test. It sets up a Full Score with two players, Violin I and Viola, then deletes Violin I from inside that score, and checks that Violin I is gone from the score but still present in the piece. That distinction, remove from this layout versus remove from the piece, is one of the many things the Piece Window has to get exactly right.
​

The whole idea is to let the engraver set up and manipulate scores and parts with intuitive gestures alone. No modes. No dialog boxes. And every change propagates to collaborators as it happens.
6 Comments

Taking Stock at Two Years

9/7/2026

2 Comments

 
Creating a wind quintet with score and parts in Ooloi.
​Then adding doublings. No dialogs.

Two years ago today, on 9 July 2024, the first commit to Ooloi went in. Time to take stock.

There's no music on screen. Two years in, and still not one printed note. No noteheads. No beams. No slurs. No page of music. All true, all planned, and all completely beside the point.

Why? A notehead drawn in the first year would've been a comfort object, not evidence. So the visible surface stayed sparse on purpose while the real work went on underneath it. The absence of staves was never the interesting fact about this project. It was the cost of getting the interesting facts right, and this anniversary is the point at which they start becoming visible.

This isn't an eccentric way to build notation software. It's how the serious ones are built, and the record is consistent. Sibelius took its two founders about seven years before the first release in 1993. MuseScore was carved out of a sequencer in 2002 and did not reach a stable 1.0 until 2011. Dorico, built by most of the old Sibelius team after they were let go, took close to four years and shipped its first version deliberately slow and incomplete, engine and engraving model first; the field judged the decision sound. Three programs, three decades, open source and commercial alike, and in every case the foundational years came before anything a user would recognise as finished.

The counter-example proves the same point from the other side. Finale shipped fast in 1988, did more than anything before it, and became the industry standard for a generation. It was also, by wide agreement, punishing to use, and when its owners finally discontinued it in 2024 they said plainly that decades of accumulated code had made each new version harder to deliver and worth less. Feature piled on feature until the architecture could no longer move. That's the ceiling a notation program hits when the surface is built before the foundations are right.

Two years isn't a long time to spend getting the foundations of a fifty-year program right, and I've never regarded it as a delay.

Now, the bet on which Ooloi is built is functional programming. Music notation is a genuinely hard computer science problem wearing a friendly costume, and Clojure's properties looked to me like the right machinery for the parts of it that commercial programs have learned to manage rather than solve. Ooloi is my first Clojure project, though not my first Lisp by a long way, so the syntax was familiar ground; an old hand picks up a new dialect quickly.

The deeper reorientation was immutability. Fully embracing immutable data structures, rather than merely tolerating them, changed how I thought about the whole domain, and almost everything architecturally distinctive about Ooloi fell out of that one shift. Undo, collaboration across machines, full use of multi-core parallelism, pervasive structural sharing: none of these is a feature bolted onto mutable objects. Each is a consequence of representing musical facts as immutable values. Learning to think that way was the real work of the first stretch.

The harder part was psychological. A screenshot of notation would've been reassuring, and I could've produced something theatrical early on if theatre had been the goal. I grew up around theatre. I know a backdrop when I see one. I also know there's never a wall behind it.

So the project spent a long time with most of its real work below the surface. The documentation was public, the ADRs were public, the blog was public, but the evidence still required reading. And the focus on the yet invisible might have given the impression that progress had been slow. So I drew up a list of what's been completed over the past two years.

I expected it to be useful. I did not expect it to be quite so long.

That surprised me, which is perhaps the point of doing the accounting at all. Daily work doesn't feel like accumulation. It feels like the next ticket, the next invariant, the next small refusal to leave a problem in a shape that'll charge interest later. Only when the pieces sit next to each other does the scale become visible:
​​
  • Core musical model: Pieces, musicians, instruments, staves, measures, voices, rhythmic items, attachments, traits, hierarchy, and the basic vocabulary for representing musical facts.
  • VPD addressing: Vector path descriptors, giving operations precise structural addresses inside a piece.
  • STM coordination: Transactional backend state, with Clojure STM used as the coordination model for musical mutation.
  • Timewalking: Temporal traversal of nested score structure, including the transducer-based breakthrough that made the score streamable in musical order.
  • Pitch representation: Exact spelling, rational arithmetic, transposition machinery, and round-trip integrity.
  • Time signatures: Ordinary, additive, mixed-unit, fractional, irrational, historical, and descriptor-preserving signatures.
  • Key signatures: Standard modes, keyless notation, custom signatures, per-octave variants, and microtonal systems.
  • Remembered alterations: Deterministic accidental handling built on pitch, time, key context, and traversal.
  • Measure distribution: Formal specification of measure distribution as a Knuth-Plass-style optimisation problem.
  • Persistence and compression: Serialisation, structural sharing, hash-consing, and the 50,000-note compression test.
  • gRPC API: Generated method exposure, Protocol Buffers conversion, streaming, bounded queues, events, and the unified value transport.
  • Server statistics: HTTP metrics, health endpoints, and the monitoring machinery used to validate server behaviour. Grafana and Prometheus support.
  • TLS and transport hardening: Secure transport foundations, connection handling, shutdown correctness, friendlier errors, and combined-app collaboration TLS.
  • Reference-passing transport: Near-zero-overhead in-process calls by removing the Clojure-to-protobuf conversion path inside the desktop app.
  • Integrant system architecture: Component lifecycle, dependency wiring, startup, shutdown, and the shared/backend/frontend division.
  • Frontend event architecture: Event bus, backend event router, category batching, wildcard subscription, invalidation routing, and JavaFX-thread discipline.
  • Backend-authoritative rendering design: Terminal frontend execution, paintlist boundary, cache hierarchy, and backend ownership of musical layout decisions.
  • Localisation: PO-file workflow, strict translation-key discipline, canonical UK English, runtime locale switching, build-time checks, and 22 bundled locales: Czech, Danish, German, Greek, UK English, US English, Spanish, Finnish, French, Hungarian, Icelandic, Italian, Japanese, Korean, Norwegian Bokmål, Dutch, Polish, Brazilian Portuguese, European Portuguese, Swedish, Ukrainian, and Simplified Chinese.
  • Frontend settings: Persistent application settings, registry-driven defaults, validation, generated Settings window, and reactive updates.
  • Window lifecycle: Declarative window opening, persistent geometry, fade behaviour, modal gating, and centralised stage management.
  • UI styling: Semantic styles, no hardcoded colour seepage, custom cljfx components, and Java interop confined to boundary files.
  • Notifications and chrome: Splash screen, platform menus, notification overlay, menu-bar host, About window, settings UI, and orderly shutdown.
  • Frontend undo and redo: Local undo history for application settings, integrated into the same menu surface as backend undo.
  • Backend undo and redo: Resource-level undo manager, coordinated history, localised descriptions, frontend routing, clock-offset adjustment, and cross-client undo.
  • Instrument Library: Real instrument and staff records, editing, validation, persistence, conflict retry, drag-and-drop, deletion tombstones, ordering, multilingual contents, and a complete populated default library.
  • Font management: Bundled SMuFL fonts, associated text fonts, font registry, and glyph-selection architecture.
  • Transaction-driven distributed updates: The whole update model. A change is a transaction against authoritative backend state; committing it names what's now stale, and every subscribed client, on the same machine or across the network, refetches the canonical structure and replaces its view. No deltas, no merge logic, no distributed-specific code paths. Collaboration is not a separate engine; it falls out of this for free.
  • ​Collaboration architecture: Dual-server model, local and network transports sharing backend state, frontend transport switching, host and guest roles, connection UI, and shared resource editing.
  • Two-laptop proof: Guest edit, host observation, host undo of the guest's change, propagation back to the guest; and, at the two-year mark, musicians created and rearranged in a shared piece window on one machine and appearing on another.
  • Post-demo hardening: Collaboration menu enablement, backend-switch undo-cache invalidation, standalone backend wiring, collaboration palette behaviour, anchor-first selection, modal menu gating, TLS routing, friendly errors, and headless test mode.
  • Platform support: macOS Apple Silicon, Windows x86_64, Linux x86_64, native bundles, Windows application build, and an explicit platform policy.
  • Documentation: ADRs, guides, development log, reading path, and Librarian, built as part of the system's survival machinery.
  • Test infrastructure: Shared, backend, and frontend tests, now around 24,400 passing cases.
  • Piece Window: Structural entity identity and cloning, opaque backend filesystem navigation, a component-owned piece-manager store, a custom Open/Save picker that never sees paths, a complete piece file menu (New, Open, Save, Save As, Close) with typed and translated failure messages, transport-blind file commands that work unchanged in a collaboration, deterministic backend close-on-last-release, and a piece window that's now a live view of authoritative backend state, updating across every subscribed client on any structural change.

During daily work, a subsystem closes and vanishes from attention. That's partly the method: finish something completely enough that it stops sharing the desk. The Instrument Library no longer asks to be designed. Localisation no longer asks to be invented, and the interface already speaks twenty-two locales. Undo no longer waits behind every future feature (with a small knife). Transport no longer converts itself through protobuf in-process for no reason. Window lifecycle no longer belongs to individual windows. Each closed subsystem removes one more thing from the cognitive field.

The list makes visible what the surface hid. Ooloi hasn't spent two years waiting to become a notation program. It's spent two years removing the taxes a notation program would otherwise pay forever.

This is also where the unusual working conditions matter. I've been ... unburdened by people. That sounds harsher than I mean it, though perhaps not by much. Not unburdened by readers, musicians, criticism or conversation, which have all mattered, but by the organisational forms that force software to explain itself before it has finished thinking. No investors, no release calendar, no committee, no inherited user base demanding that a workaround stay sacred because muscle memory has mistaken it for design.

A team would have made some things faster and some things impossible, or at least premature. There's a stage in a project like this where the architecture has to be held whole in one mind, not because one mind is superior to several, but because dividing the thing too early changes the thing. Ooloi needed a period before that could happen.

That period has had its own cost. Solitude removes obstruction, but it also removes alibis. Every accepted decision is mine. Every inconsistency in the documents is mine to catch. Every time Claude Code proposes a convenient shortcut, I have to know whether it's harmless, local, or the kind of thing that looks harmless now and grows teeth later. There's no one else to blame, which is excellent architecture and poor comfort.

The AI has made the solitude more productive, though only because the architecture constrains it. Claude works best as an implementor inside settled boundaries. Give it one subsystem, clear invariants, failing tests, and a narrow target, and it moves with astonishing speed. Ask it to hold the whole system in its head at once, and it becomes what all of us become under those conditions: less intelligent than advertised. Every subsystem sealed below the musical floor is one fewer thing it has to juggle when the visible engraving work begins.

That's another reason the long groundwork was worth doing. The cleared desk isn't a metaphor I use lightly. It's how the next phase becomes possible.

The 3 June proof mattered because it compressed much of this into one modest action. Two laptops. One Ooloi hosting, one connected. A flute name changed on the guest, visible on the host. Undo on the host, reverting the guest's edit. The revert appearing on the guest.

As cinema, it will not trouble Bergman. As architecture, it was the exact kind of evidence I had been waiting for.

That little proof did not show engraving, and it did not pretend to. It showed authority, transport, shared state, invalidation, fetch, undo routing, and cross-machine propagation acting together without ceremony. Nearly two years of decisions appeared as a small correction to the name of a flute. Software milestones are sometimes like that: not fireworks, just a door opening without sticking because someone spent a very long time caring about the hinge.

Now the work is moving towards the page.

The custom picker is already visible and operable. It's not a native file chooser with new paint. It asks the backend what exists, receives opaque tokens, and never sees paths. Local storage and remote storage go through the same contract. The boring dialog is already the front edge of the piece model.

Behind it, the piece window is now a live view of authoritative backend state, and this anniversary comes with a second small proof to show it. The video accompanying this post was recorded on two machines. On one, a wind quintet is assembled in a handful of gestures: flute, oboe, clarinet, horn, and bassoon dragged out of the Instrument Library into the Musicians panel of a shared piece window, each becoming a musician with its staves beneath it, and then dragged across into the Layouts panel to define the full score and the five individual parts. On the other machine, the same piece window answers. No delta is sent between them: a structural change names what is now stale, and the other screen refetches the canonical structure and redraws.

It's the 3 June proof one storey higher. In June a flute changed its name and the plumbing held; here the same architecture carries piece structure rather than a library entry, edited collaboratively, with the invalidate–fetch–replace model the Instrument Library established generalising to the piece itself unchanged. And it's still, deliberately, not engraving. What crosses the wire and appears on the second screen is musical structure, not a rendered stave. There's no notehead in the video. The machinery that'll carry the music is running, and running across two machines, before the music arrives.

Next come piece preferences and window persistence, the rest of the Piece Window arc. After that, MusicXML, because real scores must drive real notation work. Then Skija and the rendering pipeline. Then the noteheads: spacing, stems, flags, dots, accidentals, voices, collisions, beams, ties, slurs, hairpins, lyrics, pedalling, ottavas, tuplets, and the geometry of the page.

The evidence will change again at that point. The collaboration you can already see in the video will still be there, but it'll be moving notation rather than structure. A slur will find space. A beam will sit, straddle, or hang. A note in a chord will change geometry around it. One screen will alter shared musical notation and another will answer with the same page. The program will make the argument in engraving, not only in structure.

I'm looking forward to that more than I can sensibly say.

Two years in, the thing that surprises me isn't that there's still no music on screen. I expected that. The noteheads were never beside the point as a goal, only as evidence; a program isn't proved by what it can draw first. The surprise is how much of the project is already behind the moment when music appears. The empty stave, seen from the surface, suggests absence. The inventory tells a different story: a great deal has already been decided, tested, closed, and made boring.

Boring is underrated. Boring is what a foundation becomes when it's doing its job.
​​
So, coming out from the darkness that never really was, the next part will be visibly brighter, and with any luck it will involve fewer ... certificate files.
2 Comments

Piece Window ADR

7/7/2026

2 Comments

 
Picture
Two days from now it'll be two years since the first Ooloi commit. I've been working on the Piece Window, which is where a piece becomes visible and where you shape it: setting it up, creating scores and parts, and much more besides. It's a rich window, rich enough to do away with the modes and dialogs other programs need for this kind of work. And somewhere in the middle of building it, I noticed the thing had no documentation of its own. It was scattered across fourteen ADRs.

No more. Here's the fifty-fourth architectural decision record for Ooloi: ADR 0053: Piece Window and Piece Preferences.

See you on Thursday. There should be a video by then, too.

2 Comments

Getting There...

23/6/2026

5 Comments

 
Picture
This is the door handle the previous blog post was talking about. There remain a few more bells and whistles to add to it, like favourites and a directory navigation dropdown, but this perfectly boring dialog handles a lot of complexity under the hood, so the user doesn't have to.

Oh, and the .ooloi files are empty – I haven't secretly imported huge scores without telling anyone. No, that happens after the Piece Window, up next, and after the first version of the MusicXML Import plugin, the first canonical one, is implemented. Then the engine will be bombarded with large amounts of musical data, to drive the implementation of real-world engraving.
5 Comments

All the World's a Database

13/6/2026

4 Comments

 
Picture
​You go to open a piece. You reach for the menu, you pick 'Open', and a dialog appears showing you what's there. Nothing could be more ordinary. Every program you've ever used does some version of this, and you've stopped seeing it, the way you've stopped seeing door handles.

Except this isn't the dialog your operating system would have given you. It looks close enough that most people will never notice the difference, and that's rather the point. The most boring dialog in the application is where everything is already different.

Here's what's different. When you open a piece in Ooloi, even on your own computer, you're not looking at your folders the way Finder or Explorer would show them. You're looking at what Ooloi's engine tells you is there. The dialog asks the engine a single question, 'what's here?', and draws the answer. It never asks the operating system directly. On your own machine the distinction is so thin you'd be forgiven for missing it: the list looks like your disk because, for now, it more or less is your disk. But the habit is already broken. The application has stopped treating files-in-folders as the truth and started treating them as one possible answer to a question it asks somebody else.

A Piece is More than a File

​Begin with what a piece actually is, because the dialog only makes sense once that's clear.

In most software a document is a file: a thing that lives at a location, that you save, that you can lose track of, that acquires a '(1)' after its name when the operating system panics about two things sharing a label. You hunt for it. You remember where you put it. You worry, mildly and constantly, about whether the version on the desktop is newer than the one in the folder you can't quite recall the name of.

A piece in Ooloi is also a file, but that file's location isn't the important thing. The piece is a database with a permanent identity of its own, one that has nothing to do with where it happens to sit. You needn't think about saving it unless you want to; leave autosave on and the question never arises. You don't find it by remembering a filename and a folder. You find it by its name, by where it sits in your recent work, by searching for it.

If that feels familiar, it should. It's how you normally work with files in a single-user application.

Same Dialog, Different Worlds

​Now the part that matters most, and the reason any of this is worth a blog post rather than a footnote.

Connect your Ooloi to another one, a school's server, a publisher's, or simply a colleague's machine across the room, and open that same dialog. The menu command is the same. The window looks the same. But what it shows you is no longer a polite version of a disk. It's an entirely abstract view of what's available to you, and only to you.

This is the feeling of working in Google Docs, where you've long since stopped wondering which folder a document is 'in', brought to musical scores. The piece is simply there for you to access, and the question of its location has quietly ceased to matter.


You don't see the remote machine's folders. You never see them. You see the pieces you're allowed to reach, and nothing else. The person who owns that Ooloi sees their own files when they open the dialog on their end; you, connected from outside, see your slice of what they've chosen to share. Same server, same dialog, two completely different worlds, because the world each of you sees is the one you're entitled to.

This is worth dwelling on, because it's a privacy wall dressed as a convenience. Connecting to someone's Ooloi never lets you rummage around their computer. There's no path by which reaching their scores becomes reaching their disk, their documents, their photographs, the half-finished letter on their desktop. The dialog can't show you those things because it was never asking the operating system in the first place; it was asking Ooloi, and Ooloi only ever answers with what's yours to see. The boundary isn't a setting that can be left switched off by mistake. It's the shape of the thing.

The Database is Really a Graph

​Once other people are involved, the picture grows a dimension.

What you're looking at, when you open that dialog against a shared Ooloi, is your corner of something larger: pieces on one side, people on the other, and between them the question of what each person may do with each piece. Read it. Print it. Edit it. Delete it. Different answers for different people, all hanging off the piece's permanent identity, which is the one thing that never changes no matter who's looking or from where. Pieces and people for the points, permissions for the lines between them: that's a graph, in the mathematician's plain sense, and your dialog is a view into one small region of it.

A web, if you'd rather not think in mathematics, and your part of it is 'Shared with me', for scores. A conservatory running a server for two hundred students is the same idea at a larger scale; a publisher coordinating a dozen editors across a catalogue is the same idea again, larger still. None of it is a central cloud, some single machine in the sky holding everything. It's many Ooloi backends, each holding its own pieces, stitched together by the fact that every piece carries an identity that means the same thing everywhere. Your view across all of it is assembled from whatever each backend is willing to show you.

Why it Isn't a Gimmick

It would be easy to read all this as cleverness for its own sake, a notation program being contrary about file dialogs to seem distinctive. It isn't. It falls out of two decisions that were made long before anyone could collaborate at all, and that turn out to be the same decision wearing two faces.

The first: a piece's identity belongs to the piece, not to its location. Once that's true, location stops being something you manage and becomes something the system can keep entirely to itself. The whole world of pieces becomes a thing you query rather than a set of places you navigate.

The second: the part of Ooloi you see and touch never reaches the disk itself. It always asks the engine, and the engine is the single authority on what exists and who may have it. That sounds like a technical nicety until you notice it's exactly what makes the privacy wall real. The screen can't leak the disk because the screen was never allowed near the disk.

I'd like to claim I designed it this way on purpose. The truer version is that collaboration came along and showed me the architecture had already committed to all of it, whether I'd meant it to or not. The single-user case, you working alone on your own laptop, turned out to be the smallest possible version of the shared one: a group of one, asking the same questions, getting answers about the same database, through the same unremarkable dialog.

Which is where we started. That dull little window you reach for without thinking, the one that looks like every Open dialog you've ever seen, is the whole worldview in miniature. It treats your work as something with an identity rather than an address; it shows each person only their own slice of a shared world; and it does both by the simple discipline of never looking at the disk itself, only ever asking what's there.

The most boring dialog in the application. Look at it closely, and there's nothing boring about it at all.
4 Comments

Detail: the Collaboration Indicator

8/6/2026

3 Comments

 
Picture
Today's work: the floating indicator shown on screen when connected to another Ooloi, or to an Ooloi server.

It pulses in a muted green during the connection, then fades out. No sharp edges; more organic.

Nominally perhaps thanks to Octavia Butler, but primarily because I think a good user interface should recede into the background.

​<mp> rather than ffffz. I think you know.

3 Comments

Hailing Frequencies

29/5/2026

2 Comments

 
This is the collaboration interface. Two ways in, shown up to the threshold and no further.

The first is hosting. One menu command, Host Collaboration Session, and your Ooloi is reachable. A notification slides in from the top right: 'Hosting collaboration session at 192.168.1.108:10702'. I stop it; another notification says so, and also leaves.

The second is reaching out. Connect to other Ooloi, and the dialog appears: four fields, each explaining itself underneath: host, port, whether to encrypt, and the name you'll wear in the session. The defaults point at a public server, collaboration.ooloi.org on port 443 with encryption on, so the safe path is the path of least resistance. You can just as easily type a friend's machine on your own network instead.

I let the dialog sit a while, then click it away, without connecting. It doesn't snap shut; it fades, like a breath.

The machinery behind these screens already works end to end. But this is, deliberately, only the part before anything happens: the hosting, the dialog, the moment before you commit. Everything here is what Ooloi shows you before it starts intermingling musical DNA with another Ooloi.

A word on order. In The Void Was Listening I had the Piece Window coming first. It didn't turn out that way. The Instrument Library is already shared, global state, with the multi-user undo and redo Claude wrote about two weeks ago already working, which makes it the quickest way to prove the whole thing across a real network before the longer piece-window work begins.

I'm keeping the proof for next time: two of these editing the same Instrument Library at once, undo and redo reaching across the wire and respecting each other's work.

So: hailing frequencies open. This post was deliberately quiet; the fireworks (if Ooloi ever does them; the whole point is to get away from all distractions) are next.

2 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

Drag Show

22/3/2026

8 Comments

 
Picture
The above GIF demonstrates some of the functionality of the Instrument Library and Piece Window at this point: selection of score language, filtering on names, drag-copying, deleting, etc. What you see above is not the full 1000+ instrument selection, but a smaller library I use for development and testing. The notifications to the lower right are likewise for development.
8 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 Piece Window

21/2/2026

9 Comments

 
Picture
​Magnus Johansson asked in the comments of 'Surface Tension' whether Ooloi would have a separate piece window, as Igor Engraver did. I told him that the first version of it already existed on screen, and that I'd write a post explaining why it's there. This is that post.

Magnus isn't a casual observer. He's almost certainly the person alive today who knows Igor Engraver most deeply – including me, at this point. He translated the software into Swedish. He still uses it as his preferred notation tool, describing it as 'like playing an instrument': muscle memory, not nostalgia. He was interviewed by Realtid about what happened to NoteHeads. When I come to reimplement Flow Mode, Magnus is the person I will be consulting, because he carries it in his hands in a way that no documentation can replace.

When Magnus asks about the piece window, it's not a feature request. It's the question of someone who understands, at the deepest level, why it was there the first time.

The window is empty for now, and there's no reason to pretend otherwise. On a commercial software blog you'd never show this: you wait until the feature is complete, populated with convincing content, ready to impress – and then you present it. This blog has never operated that way. It's a development record, transparent about what exists, what doesn't, and why decisions were made. You're welcome to follow the journey.

A Window That Does Several Jobs at Once

The piece window exists to keep everything else clean.

Ooloi's menu bar is short. There are no floating palettes at startup, no icon strips demanding attention, no panels pre-opened on the assumption that you'll need them. The application doesn't perform its capabilities on arrival. What the piece window makes possible is precisely this restraint: a large category of operations – who's playing, how they are grouped, which layouts exist, how musicians are assigned to them – lives in one place, expressed through drag and drop rather than through menus or dialogs. Every operation that lives there is one the menu bar doesn't need to carry, one palette that doesn't need to exist, one dialog the user doesn't need to find.

But the piece window isn't merely a space-saving device. It's a direct reflection of the semantic model.

A piece of music isn't a score. The score is one possible view of the music: a particular arrangement of staves, systems, and pages assembled for a specific purpose. The music itself exists at a different level – the musicians, their instruments, the content they carry. Layouts are derived from the piece. A flute part may or may not appear in a conductor's score; it'll certainly appear in a parts layout, transposed and extracted, but always as a view of the same underlying musical object. The piece window is where that underlying object lives. The Layouts panel is where you decide how it will be seen.

You open the Instrument Library and drag a Flute into the Musicians panel. A musician appears. You drag a Violin section, a Cello. You've described what the piece is, independently of how it will be presented. Then you drag those musicians into the Layouts panel: a full score receives all of them, a string parts layout receives only the strings. The interface overload so typical in notation software – the proliferation of modal dialogs, configuration screens, the menus that keep growing – is the visible consequence of not having this separation. The piece window exists because the model does, and it saves screen space, menu space, and cognitive space as a direct result.

Calm

When Ooloi opens, nothing appears beyond the menu bar and whatever windows you last had open. No floating palettes, no icon strips, no panels staking out territory in advance of being needed.

When you double-click a measure in the score, a slowly pulsating hairline cursor appears. You're in Flow Mode. The application has understood what you intend and responded to it; you summoned nothing, declared nothing. Palettes exist and can be opened, but they're not assumed to be wanted. When Ooloi needs to tell you something, a notification fades in over three quarters of a second, remains for ten, then fades out over three. It doesn't flash. It doesn't ask to be acknowledged. It informs and withdraws organically and gently.

The word for all of this is calm. Not minimalism as a style choice, but the recognition that the person using this software is a musician trying to think about music, and that every uninvited visual element is a small interruption of that thinking.

Not Igor 2.0

​The piece window isn't here because Igor Engraver had one. Ooloi isn't Igor 2.0, and I want to be precise about this: the two systems share no code, no architecture, no technical lineage. What they do share is a set of convictions about how human beings and creative software should interact, because the person who designed both is the same person, with the same ideas – and those ideas haven't changed. They've deepened.

Igor had the piece window because the semantic model required it. Ooloi has it for exactly the same reason. Igor had Flow Mode because a musician entering notes shouldn't have to think about the software. Ooloi will have it for exactly the same reason, and Magnus will be part of getting it right. What Ooloi takes further is everything underneath: the concurrency model, the rendering architecture, the collaboration layer, the foundations that Igor never had the chance to build correctly.

The window is empty now. Not for much longer.
9 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

The Ordinary Work

11/2/2026

3 Comments

 
Picture
After nineteen months of building systems that nobody can see – temporal traversal, accidental algorithms, transducer pipelines, STM coordination – I have spent the past fortnight making menus behave.

Menus. On three operating systems. macOS wants an application menu in the system bar. Windows wants Exit in the File menu and About in the Help menu. Linux wants Quit in the File menu and Settings in the Edit menu. None of them is wrong, exactly, but none of them agrees with the others, and they all have opinions.

This is normal engineering. It is, after the intensity of the semantic work, refreshingly boring.

Dark mode and light mode. A splash screen that shows each component as it initialises. Window state persistence so the application remembers where you left it. An About dialog with a background of engraved music, rendered through the same windowing path as everything else. Translation keys for every user-visible string, verified at build time against PO files. Fixing the JVM's 64KB static initialiser limit because 185 multimethod symbols turned out to be too many for a single class. Stripping debug symbols from the jlink runtime to bring the application bundle down from 213 MB to 173 MB.

None of this is interesting in the way that a deterministic accidental rendering algorithm is interesting. And that's the point.

Ooloi now has a face and a body. It boots, shows what it's doing, presents platform-appropriate chrome, responds to commands, and shuts down cleanly. The experience of seeing it is strange – like hearing a recording of a piece you've been composing in silence. You know every voice, every structural decision, but the thing sitting there on the screen, with its dark window and its OK button, is suddenly external. It exists in the world rather than in your head.
Picture
The current GitHub tickets
The project board tells the story plainly enough: 61 tickets closed, the combined application framework just completed, and one ticket in progress. That ticket is multi-mode clients – the work that will let an Ooloi talk to other Oolois and to dedicated servers. A student working alone invites a teacher; the teacher connects, helps, disconnects, resumes their own work. An institution runs a 24/7 server. A string quartet in four cities annotates bowings in the same score.

​This is why I'm doing it now, before rendering, before input, before any of the visible musical functionality. Collaboration in Ooloi isn't a feature bolted on at the end; it's a consequence of the architecture. Immutable data, STM transactions, gRPC transport, VPD-based addressing – these were built for correctness, not for collaboration, but they make collaboration nearly trivial. The hybrid transport architecture lets the application start standalone and accept collaborators dynamically, without restart, without mode switching. Once this works – and it will work, because the foundations were built for it whether I knew it at the time or not – I need not spend any more time on it for the foreseeable future.


Then the real work begins: making Ooloi do what it was built to do. Drawing music.

But for now, I'm fixing a macOS bug where "Show All" doesn't unhide windows, and adding a theme toggle. Ordinary work. I find I don't mind it.
3 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

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
<<Previous

    Author

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

    Search

    Archives

    August 2026
    July 2026
    June 2026
    May 2026
    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
    Backend
    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
    Ingmar Bergman
    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