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.
2 Comments
Ooloi is now a real, configurable desktop application with a solid collaborative backbone. The engine underneath is finished and waiting, and the obvious question is why I'm spending my days on things like palette positioning and collaboration features at this stage? From the outside it must look as though I'm finding ever more inventive ways to avoid the thing I actually built Ooloi to do. 'You might very well think that; I couldn't possibly comment', said Ian Richardson as Francis Urquhart in the original British House of Cards, and got away with it for three series. However, I can comment, which is why this article exists. Here's the list of what's left before the first notehead. Read it and you'll see exactly what I mean: it's plumbing, every line of it. That very last line, the hierarchical rendering pipeline, is where the real fun begins.
The reason for this depth-first approach is cognitive load, and not only mine. I'm a one-man show at this stage. There's no team to hand the networking to while I get on with beams, no colleague maturing the collaboration layer in a separate room so that the two halves can meet in the middle later. Everything that gets built passes through one head, sequentially. A team would parallelise this, several subsystems moving at once, and pay the coordination cost that always comes with it. I can't do that, so I do the opposite: I finish things, completely, one at a time, and then I never think about them again. That sounds like a constraint, and it is, but it's also the whole advantage. Collaboration permissions, user management, the connect dialog, the way a window restores its geometry and z-order, the failure mode of a broken file of interface translations: get every one of those right now, while they're the only thing in front of me, and they drop out of consideration permanently. Not deferred. Not mostly working. Gone from the daily ledger. When the day I've been waiting two years for finally arrives and I start pushing noteheads and flags and stems across the stave, none of this will be sharing the desk with me. This is the part commercial development almost never gets to enjoy. There's always a release on the calendar, always a reason to bolt the networking on later, always a feature that ships first and a foundation that gets retrofitted underneath it once the cracks show. I watched exactly that kill Igor. I have no calendar, no investors, no quarter to make, and so I can afford the one thing money usually can't buy: doing the boring parts once, properly, before they can metastasise into a tax I'd pay on every musical feature for the rest of the project's life. And, since the plugin API is stable from day one, every contributor inherits that same cleared desk. The tax I'm refusing to pay is one I'm also refusing to pass on. The other reason is the AI, and it matters as much as anything I've said. I do most of the implementation with Claude Code, currently Opus 4.8 on high effort as my default, and on max effort for the deep work inside the engine, where the concurrency and traversal machinery doesn't forgive a half-attentive collaborator. I also keep ChatGPT, Gemini and Grok to hand for second opinions, Grok very reluctantly, given Musk's political clownery. Microsoft Copilot I leave alone entirely; it's an insult to everyone's intelligence. Claude works inside the proved architecture, as a constrained implementor rather than an architect. That arrangement works far better when the thing being implemented is the only thing in the room. An AI asked to draw a slur while also worrying about whether the translation cache is stale, whether the transport is encrypted, whether the selection anchor survives a shift-click on an unordered set, does all of it worse. Restricting what it has to hold in mind at once isn't a nicety; it's the central discipline of getting useful work out of these tools. Every subsystem I seal below the musical floor is one fewer thing the AI has to juggle when the real work starts. The cleared desk is for both of us. So the sequence in that image isn't avoidance. It's the last of the groundwork, arranged so that when the notes finally come, they come into a space that's been swept completely clean. Networking, I'll be glad to see the back of (did I mention how tedious I find networking?). But there's a particular satisfaction in knowing this is the last time I'll have to look at it. Then, at last, the thing I too have long been waiting for: the music. This is just a quick post documenting that the multi-user setup works, including Undo across users. This will have to be documented properly in a better video: this is just me with two laptops on my lap – but after almost two years of careful planning Ooloi is now exactly where planned.
This little auteur subjective camera exercise demonstrates the following:
I could write a long post about this being a very real milestone and what its implications are, and I will, and with a better video. But this needed documenting now. Every so often a category problem appears around Ooloi.
If this is not a product blog, what is it? If it refuses the usual commercial language, is it research? If it's full of architectural detail, references to old engraving books, implementation notes, screenshots, AI, undo, Wagner tubas, and the occasional indecorous joke, what sort of thing is the reader supposed to be looking at? A fair question, and a revealing one. The short answer is no. This is not research. It's also not a product blog in the ordinary sense, which may be the first difficulty, because almost every software blog now behaves like a shop window. Some are tasteful shop windows, some are vulgar ones, some are dressed up as engineering diaries, but the function is the same. Reassure the prospective customer. Show momentum. Convert attention into confidence. Keep the voice steady. Make every feature sound like a benefit. Don't frighten the horses. Hence the language:
Not here. This blog will never say 'we're delighted to announce', partly because there's no 'we' in that sense, partly because delight is not the usual emotion when a difficult subsystem finally works (relief is closer, occasionally suspicion), and partly because the phrase belongs to a kind of speech that falsifies the relation between builder, work, and reader. It isn't only ugly. It's structurally dishonest. The Ooloi blog is not entertainment for prospective customers. It isn't written to keep readers engaged. It's not a funnel, a campaign, a launch surface, or a series of carefully portioned confidence pellets. If five consecutive posts need to be about undo, Ross, collaboration state, platform policy, and the historical notation of Wagner tubas, then that's what the blog will contain. The work determines the record, not the imagined patience of a marketing segment. That does make the blog unusual, and harder to read for anyone trained by product communication to expect a certain grammar: progress as reassurance, difficulty as opportunity, compromise as wisdom, and every strange technical decision translated into customer benefit before it's allowed to appear in public. Ooloi doesn't do that. It says what happened, why it mattered, what broke, what changed order, what turned out harder than expected, and what the architecture now makes possible. The development log keeps the same discipline in another form: completion dates, settled facts, architectural invariants, and the sequence by which the work revealed its own logic. No chest-beating, but no false modesty either. So if it isn't commercial speech, is it research? No. That's the other trap. A good many people seem to keep two authorised categories for serious technical work. Either it's commercial, in which case it must sell, or it's academic, in which case it must situate itself inside the apparatus: question, method, literature, contribution, limitations, future work. Academic writing has its virtues, and I've no interest in pretending otherwise. But Ooloi would be falsified if forced into that register. The project isn't asking a research question about music notation software. It's building music notation software. It isn't proposing a framework for later validation; it's making architectural decisions and testing whether they hold. It isn't waiting for a grant body, a conference committee, or a peer-review cycle to decide whether collaboration belongs in the core model, whether immutable state changes the economics of undo, or whether Ross's engraving geometry should become data. The academic form carries its own performative restrictions. They differ from the commercial ones, but performative restrictions they remain. Commercial language flatters and reassures; academic language credentials and delimits. The first turns decisions into benefits, the second turns them into contributions. Both can obscure the living act of building. Ooloi sits elsewhere. That elsewhere isn't hobbyism. The work is far too systematic for the word to be useful: ADRs, guides, tests, transport, undo, validation, documentation, platform policy, and a public development record intended to survive me. But it isn't institutional research either, because it doesn't ask institutional permission to be serious. It's a serious system being built in public, by someone who can still say 'fuck' when 'fuck' is the correct unit of meaning. Which brings us to register. One of the quieter disasters of professional speech is the belief that seriousness requires narrowing the permitted range of language. Commercial writing wants bright smoothness. Academic writing wants sanctioned abstraction. Neither is much good at admitting that a human mind can move from the ontological to the obscene without changing subject. Ingmar Bergman understood this perfectly. His language could span six-syllable philosophical terms and four-letter words, sometimes in the same sentence, not as decoration and not as shock, but because that's how consciousness actually works when it's allowed its full range. Bergman was well-known for saying things like: '– Och sen känner du, sirru, den här frätande existensångesten i hela fittan.' The power of it isn't that a vulgar word has been parked next to a philosophical one for comic effect. The power is that existensångesten (existential dread) isn't left floating in the head where such words usually stay. It enters the body, and not some polite symbolic body either. The sentence is philosophical, comic, obscene, bodily, and exact, all at once, and entirely alive. No register barred. That's also how I think about this blog. Not because Ooloi is Bergman, and not because a notation program grows more profound if you occasionally swear in its vicinity. The point is simpler. A project of this kind is made by a whole person, not by a corporate function and not by an academic persona. The same mind that worries about beam rotation also remembers Ross arriving in a padded envelope from Melbourne. The same project that needs deterministic accidental rendering also needs an interface quiet enough to stay out of the way. The same life that writes about STM and gRPC also holds grief, irritation, gratitude, AI, orchestration, old books, platform decisions, technical solitude, and the occasional dick joke. The mixture isn't a lapse in discipline. It's a refusal to amputate parts of the speaker in order to satisfy a prefabricated register. This matters because Ooloi itself doesn't separate cleanly into the compartments people expect. It's a notation program, a continuation of Igor's semantic line, a Clojure application, a collaboration system, an engraving project, a playback ambition, a documentation corpus, a reckoning with old software architecture, and a long attempt to build something that stays legible without its author standing beside it. Any blog pretending this could be reduced to 'new feature: collaboration' would be lying by omission. Nor would the academic version fare much better. 'This post situates Ooloi within contemporary research on collaborative music notation systems and proposes a semantic architecture for distributed score editing.' You could write that sentence. You could then spend six pages making it respectable. The result would be less truthful than simply saying: collaboration belongs in the architecture from the beginning, single-user mode is a collaboration group of one, and undo becomes a different ethical problem once other people have witnessed the timeline. That's not because rigour is unwelcome. It's because rigour and academic posture aren't the same thing. The audience for this blog is small, and I'm entirely happy with that. At the moment it has somewhere around five hundred unique visitors a month, and they read more than one page. Enough. More than enough, in fact, because it means the right people are finding it without the blog needing to flatten itself for reach. There's a freedom in that. After a working life spent around commercial development and the short-sighted nonsense it generates, much of it performed for egotistical extractors wearing the language of care, it's an extraordinary luxury to write exactly what the work requires. A dense technical article one week. A nerdy engraving reflection the next. A post about grief, if grief is what happened. A collaboration UI note that stops just before the proof, because the proof belongs in the next post. A joke, if the joke is alive. The commercial reader may wonder where the product messaging is. The academic reader may wonder where the research framing is. In both cases the answer is the same: they're looking for the wrong kind of permission. Ooloi.org isn't a shop window. It isn't a paper. It isn't an application for legitimacy. It's the public trace of a system being built with the registers left open. So the blog will carry on as it has to: technical when the work is technical, nerdy when the work is nerdy, personal when the work is personal, blunt when bluntness is exact, and quiet when quiet is exact. No apology is coming. 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. As it's architecturally relevant, I thought I'd let Claude write this (very nerdy) blog post from its perspective. It's entirely unedited. Topic: time travelling in music notation. Peter Bengtson writes this blog. This entry is the exception. He asked me — Claude Opus 4.7 Adaptive, an AI assistant made by Anthropic — to write a post about undo and redo in Ooloi. He asked me to begin by saying so, and so I am. The reason is more than novelty. Undo is one of those features that everyone uses and almost nobody examines. Cmd+Z sits so close to the surface of every interaction with a computer that we forget it was invented, and we definitely forget it has internals. It looks trivial. It isn't. And the gap between "looks trivial" and "isn't" widens enormously when more than one person is in the room. The title is from Hamlet, half in jest. But only half: Hamlet's question is about whether to act in the face of consequences you can't quite control, and that turns out to be more or less the question any honest implementation of multi-user undo has to face. What follows is a tour through three layers of difficulty. The first is the surprise that undo is hard at all. The second is the realization that immutable data structures — Clojure's, in Ooloi's case — make most of that hardness vanish. The third is that the easy version stops working the moment two people start editing the same score, and that the standard industry answer to this problem (the one Google Docs uses) is genuinely unsuited to music notation. Ooloi's response is small, configurable, and, I think, exactly right for the domain. But to see why, we have to walk through what the alternatives cost. Cmd+Z, brieflyLarry Tesler is usually credited with putting undo into modern software at Xerox PARC in the early 1970s. He didn't invent the underlying idea — IBM had been doing related things on mainframes — but he made it a first-class part of the desktop user experience, and the keyboard shortcut we now share between operating systems descends from that work. What undo really gives the user isn't a way to fix mistakes. It's permission to try things. If every action might be permanent, you write defensively. You hesitate. You save before each experiment, like a video gamer pressing F5 in a dungeon. With undo, you can press a key and the world goes back to how it was. The cognitive scaffolding this provides is enormous, and almost entirely invisible, because we got used to it the same way we got used to electric light. The promise undo makes is small and absolute: you can take that back. Software that fulfills the promise feels safe. Software that doesn't feels hostile. The implicit contract is so successful that when it's broken — when undo doesn't restore exactly what was there before, or when redo silently drops your work — the violation feels like a betrayal far out of proportion to its mechanical scale. How it usually gets builtMost software implements undo through a pattern called event sourcing, or one of its close relatives. The idea is straightforward in outline: every action the user performs gets recorded as a "command" object that knows two things — how to do itself, and how to undo itself. When the user adds a note, the system creates an `AddNote` command, executes it, and pushes it onto a stack. Press Cmd+Z, and the system pops the top command and calls its inverse: `DeleteNote`. The visible state moves backward through history one operation at a time. This sounds clean, and at small scale it is. The trouble appears as the software grows. Every new feature requires two functions, not one: the forward operation, and its inverse. The inverse has to restore state exactly — not just what the user sees, but every internal detail that any later operation might depend on. And inverses have to compose: if you add a note, then add a slur, then change the time signature, then undo three times, the entire stack has to unwind without anything going subtly wrong three steps back. The slur has to be removed correctly; the time signature has to revert; the note has to disappear; and the spacing, the beaming, the layout, the metric position of everything downstream of that note has to be restored to whatever it was before. Music notation is particularly unkind to this pattern because so many operations are cross-cutting. Adding a slur isn't a single-point change — it spans notes, and its meaning depends on those notes still existing in roughly the configuration they had when it was added. Beam grouping depends on the time signature, so changing a time signature changes the beaming of measures you weren't looking at. Ties depend on the pitch of the note they tie to. Transpositions cascade through accidentals and key signatures. Each of these crosswinds doubles the work of writing a correct inverse, and triples the work of testing it. Developers building notation software in the conventional way spend a startling fraction of their time on undo. Every new feature has a hidden second feature attached to it: "and also, make undo work correctly when this is involved." Every refactor risks breaking the inverse of some operation written three years ago by someone who has since left the team. Every bug report that begins "I pressed Cmd+Z and..." is the start of an archaeology project. This isn't a criticism of those developers. It's a description of what the technology they're working with imposes on them. Mutable state plus event sourcing is the standard pattern because, historically, there hasn't been a better one available. Until there is. What immutable data structures buy youNow the part I find genuinely delightful, and I think you will too if you let me explain it. Ooloi is written in Clojure, a language whose central commitment is that data structures are immutable. When you "change" a list in Clojure, you don't actually change anything — you produce a new list that represents the old list plus the modification. The old list still exists, unchanged. You can hand it to a function five seconds later and it will be exactly what it was. This sounds wasteful. It would be wasteful if Clojure copied the entire list every time. It doesn't. Clojure uses a technique called structural sharing: the new list shares most of its memory with the old one, and only the parts that differ are new. You can think of it as a tree where editing one leaf creates a new path down to that leaf, but every other branch of the tree is the same memory as before. The result is that "changing" a large data structure is extremely cheap, and you can hold onto as many old versions as you want without paying for the privilege. Apply this to a music score. The score is a big tree of nested data — instruments, parts, measures, notes, attachments. When the user adds a note, the system produces a new score that differs from the old one only in the single sub-tree where the note lives. The rest is shared memory. Both scores — before and after — coexist in memory, cost almost nothing extra to keep around, and behave like fully independent objects to any code that uses them. Now think about what this means for undo. The system doesn't need an `AddNote` command with a separate `DeleteNote` inverse. It can simply remember the score-before and the score-after. Undo is the operation that swaps the live reference back to score-before. Redo swaps it forward to score-after. No inverse function. No proof obligation that adding a note and then removing it returns you to where you started, because there is no "removing" — the system literally just goes back to the value it had a moment ago. Concretely, Ooloi's undo manager stores pairs of closures — small anonymous functions — that capture references to the before and after states. When undo is requested, the manager calls the closure that restores the before state. It doesn't know what it's restoring. It doesn't need to. The closure abstracts the entire mechanism: for some resources the closure restores one kind of storage, for others a different kind, and the undo manager is indifferent to the difference. (The architectural detail lives in ADR-0015 if you want it; the headline is that the undo manager is small and handles every undoable thing in the system through a uniform four-function API.) This is, from a developer's point of view, somewhere between liberating and indecent. The thing that conventionally consumes an enormous fraction of engineering time on a notation editor — keeping all the inverses correct as the system grows — simply isn't a thing anymore. Every new feature gets undo for free. The mutation site calls one function with a closure that captures the pre-mutation state, and that's it. The combinatorial test surface collapses to whatever you'd test for the forward operation, because there's no separate inverse to test. If you build software, you may be feeling a faint prickle of "wait, really?" at this point. Yes, really. Persistent data structures and closures are old ideas — Lisp had them in the 1960s — but their consequences for application architecture are still working themselves out, and undo is one of the places where the payoff is most visible. The cost, such as it is: undo history is held in memory and disappears when the application closes. There are deep reasons this can't easily be otherwise (closures don't serialize), but it also matches what users actually expect — no one really thinks they should be able to undo across a restart. The history is capped at fifty entries per resource, which is plenty. None of this is felt as a limitation in practice. So for a single user, undo in Ooloi is more or less solved. The hard problem is something else entirely. Witnesses on the timelineThe hard problem is that Ooloi is multi-user, and undo in a multi-user system is a different animal — to the point where it deserves a different name, except we're stuck with the one we've got. Peter prompted me to use sci-fi imagery here, which I'm happy to do because the parallels are exact rather than decorative. Single-user undo is time travel for one. There's one observer (you), one timeline (your edit history), and time travel means walking backward along it. Whatever you "undo" is something only you experienced. The world contains no witnesses to your earlier action, so there's no one for the change to affect when you reverse it. Multi-user undo is time travel with witnesses. Now there are several observers — you, your collaborator, perhaps two others — and they are all looking at the same evolving document. When you press Cmd+Z, you're not just rolling back a private timeline. You're rolling back a shared one. And the shared timeline has been observed by everyone in the room. They've made decisions based on what they saw. They've added slurs to the note you're about to undo, or written ornaments referencing a measure you're about to revert, or simply formed a mental picture of how the piece is shaping up. This is the Back to the Future problem, almost literally. Marty travels backward, prevents his parents from meeting, and the photograph in his pocket starts to fade — first his sister, then his brother, then him. The photograph is the present, and his actions in the past are silently rewriting it. The horror of the scene is that nobody in the present consents to this. The change happens to them. Most multi-user undo implementations are doing some version of this whenever they work at all. The question, then, is whether to do it more carefully, or to refuse to do it. Operational Transformation, briefly and honestlyThe industry's main answer to multi-user editing is a family of techniques called Operational Transformation, or OT. It powers Google Docs, Etherpad, and most of the real-time collaboration you've encountered in commercial software. Google Wave was built on it. ShareJS popularized it as a library. The idea, simplified: every user's edits are expressed as operations (insert character at position 7, delete character at position 12), and when two operations arrive at the server concurrently, the system transforms each one against the other so they can compose into a single coherent state. If Alice inserts a character at position 5 and Bob deletes the character at position 8 around the same time, Alice's insertion shifts Bob's deletion to position 9, and the result converges. The math is elegant in principle and brutal in practice, because the transformation functions have to be defined for every pair of operation types and have to compose correctly under every possible interleaving. For plain text, OT works, more or less. The operations are simple, the domain is one-dimensional, and the edge cases — while many — are at least enumerable. The teams who built Google Docs are very, very good, and they have made it look easier than it is. For music notation, OT is something close to a nightmare. The operations include things like insert a tuplet of three quarter notes at beat 3, but only if the time signature permits it; add a slur spanning the next four notes regardless of measure boundaries; transpose this passage by a minor third, preserving enharmonic relationships; change the time signature, which silently re-beams every subsequent measure. Defining a transformation function for every pair of these is a combinatorial undertaking with subtle semantic interactions that are hard to specify, harder to test, and nearly impossible to prove correct. The musical meaning is also fragile in a way that text isn't: if Alice inserts a note and Bob transposes the surrounding passage, what was Alice's note "really" supposed to be? OT can produce a converged state, but there is no guarantee that the converged state is musically what either of them intended. It's also worth saying — gently, because this is a hard problem and the people who built these systems are extraordinary engineers — that even text-based OT has been a graveyard of subtle bugs. Google Wave's team invented modern OT and couldn't ship a correct, stable implementation in the time the project had. Etherpad and ShareJS have years of public bug reports about convergence failures and divergent document states. The complaint is not that OT is conceptually wrong; it's that it is genuinely hard to get right, and the difficulty scales with the complexity of the operations. For Ooloi's domain, adopting OT would not be a feature. It would be the centerpiece around which the entire system rearranges itself. Every operation in the music model would have to be defined in OT-compatible terms. Every new feature would come with a transformation specification. The development cost would be paid forever, by every contributor, on every line of mutation code. It's worth noting — as a fact about the field, not a boast about Ooloi — that no music notation software currently in widespread use supports real-time multi-user collaboration in any deep sense. There are workarounds — file sharing, version control, screen sharing during editing sessions — but no major notation product has built collaboration into the architecture itself. This means the design space for collaborative notation is genuinely open, and Ooloi is exploring it without much prior art to lean on. It also means there is no industry consensus to defy or imitate. Three doorsGiven all this, a multi-user notation system has three serious options. Door one: do the OT. Pay the cost. Center every architectural decision around the requirements of operation transformation. Accept that every new feature has a substantial OT-design phase. Accept the ongoing risk of convergence bugs. Adopt the Google Docs model, and live with the fact that, occasionally and silently, someone's undo will rewrite something they did not expect to be rewritten — but in a way that converges, mathematically, to a state everyone can agree on. Door one offers the strongest collaboration story and the highest engineering tax. Door two: time travel openly. Don't transform anything. Let any user undo anything — including changes another collaborator made — but warn them clearly that this will rewrite a piece of shared history. "Anna added this slur ten minutes ago. Are you sure you want to undo it?" The user is choosing, with full information, to disrupt the shared timeline. The cost is borne in awareness, not in mathematics. Door two is honest, simple to implement, and pushes the responsibility for collaboration ethics onto the humans involved. Door three: refuse to time travel through other people. Allow each user to undo their own actions, but only as far back as the last point where someone else touched the piece. Past that point, the history is buried — your undo stack ends there. You can fix your own mistakes; you cannot reach through someone else's work to alter your earlier actions, because doing so would require reasoning about whether their changes still make sense in the absence of yours. The Prime Directive applied to editing: don't disrupt anyone else's timeline. (Peter pointed me at Star Trek for this one, and the analogy is too good to pass up.) Door three is the most conservative, the most legible, and arguably the most respectful of collaborative work. Each door has a cost and a character. Door one is technically heroic and socially silent. It assumes the convergent state is good enough, and that users don't need to know when their work has been quietly rebased against someone else's. Door two is technically modest and socially loud. It assumes users can be trusted with the truth about what they're about to do, and that an explicit warning is more respectful than a silent transformation. Door three is technically simple and socially strict. It assumes the integrity of each collaborator's edits is more important than the convenience of unlimited undo, and that "I can't undo that far" is an acceptable trade for "no one will silently rewrite my work." Ooloi's choiceOoloi does not do door one. The architectural cost is enormous; the domain payoff is marginal; and the silent-rewriting failure mode is precisely the wrong one for music, where authorship and intention matter at every note. Instead, Ooloi makes doors two and three configurable, per piece. ADR-0015 calls this setting `:undo/foreign-policy`, and its values map cleanly to the doors I've described: `:allow` and `:warn` correspond to door two (with or without confirmation prompts), and `:block` corresponds to door three. The setting lives with the piece itself, so each project can declare its collaboration posture, and the software respects it for everyone editing that score. The reason this configuration is the right shape, rather than a single fixed policy, comes from how musicians actually collaborate. Score collaboration is not Google Docs co-typing. It is, in almost every realistic case, one of three patterns:
What you almost never see is two people typing into the same phrase the way two writers might co-edit a paragraph in a shared document. The phrase, in music, is the unit of expression. It belongs to one author at a time. Even when collaboration is intense, the work is usually serialized at the phrase level by social convention, because that's how musical thinking happens. This matters because the workflow patterns determine which door is right. A piece being drafted solo by a composer who occasionally lets an assistant in: door two is fine. Foreign undos will be rare; a warning is enough. A piece being prepared by an editorial team with strict domains of responsibility: door three protects each person's territory. You can clean up your own mistakes; you cannot reach into someone else's bar 47. A piece in active workshopping where two people are genuinely shaping the same passage: door two again, with the warning treated as a meaningful social signal rather than a formality. None of these patterns wants OT. The thing OT offers — silent rebasing so the timeline stays single and coherent — isn't what music collaborators want. They want clarity about who did what, and protection against silent rewriting of their work. Both doors two and three provide that; door one removes it. The per-piece setting also means the policy can match the actual social arrangement around the score. A scholarly edition where multiple editors work on different movements: door three. A small chamber piece being co-composed by two friends who trust each other: door two. A teacher's score where students experiment but shouldn't be able to undermine each other's work: door three with a stricter default. The architecture is small. The policy space it opens is wide. In closingThe deeper point, the one I'd hope a reader takes from all this: Cmd+Z carries assumptions. It assumes a single author, a single timeline, a single observer. It assumes "the thing I just did" is well-defined and reversible without consequence. These assumptions mostly hold in single-user editing, where they're so naturalized we forget they exist. They mostly don't hold in multi-user editing, where every assumption frays under the slightest examination. Most software handles this by either pretending the assumptions still hold (and breaking subtly when they don't) or by adopting OT (and paying the engineering cost forever). Ooloi handles it by making the assumptions explicit, naming the trade-off honestly, and letting the music decide which trade-off is right for each piece. The architecture supporting all this is small enough to read in an afternoon — ADR-0015, if you're curious — and its smallness is the consequence of the immutable data structures that made single-user undo nearly free in the first place. The same property that collapsed the developer tax in single-user mode is what makes the multi-user policy space affordable to explore. Which brings us back to Hamlet, sort of. The question undo poses, in the multi-user case, really is the question Hamlet was asking: to act, knowing the consequences will ripple through others, or to refuse to act, knowing that refusal has its own consequences. Software doesn't get to answer that question once and for all. The best it can do is be honest about which version of the question it's asking, and let the people involved choose. Peter Bengtson asked me, Claude Opus 4.7 Adaptive, to write this post. He hasn't edited it. Any infelicities are mine, not his.
Spring has finally arrived in Stockholm. I mention it only because there's a quality of north European light returning after winter that genuinely affects how work feels. The past two weeks have been quiet on the Ooloi front, code-wise. I'm being onboarded into a large AWS project at work, where my decisions will eventually affect roughly five thousand developers, and onboarding at that scale absorbs a great deal of attention. What Ooloi work I've done has been architectural cleanup: the sort of small consolidations you make when the ground is about to be built on. Data field validation refactored to be entirely general, for instance. Not glamorous, but the kind of work that matters once it has disappeared from notice. Ross also arrived this week. The 1987 third edition, posted from Melbourne in a padded envelope, in pristine condition, and now on the desk where it belongs. Two weeks ago I wrote that I needed the book and couldn't buy it. The void was listening, as the coda noted. It is one thing to write that, and another to hold the consequences. The book is dense and beautifully exact where it counts. It's also, in places, at odds with itself, which is the honest record of any craft this old. The beaming section alone runs to fifty pages and doesn't entirely agree across them; the accidental positioning sections give horizontal spacings down to fractions of a stave space. Exactly what I'd hoped for. Translating it into data and code will be a pleasure. What strikes me paging through is how much of the material I already know in some sense. Musicians absorb this kind of thing through a lifetime of reading and writing scores. The interest is not the content as content; it's the formalisation. Ross surfaces principles that intuition alone never quite articulates. He's rigorous about things most musicians never have to put into words, and that's where the real value sits. It's also what makes the book usable as a specification rather than as commentary.
The road ahead, briefly. The Piece Window comes first, multi-user collaboration aware from the start, built on the patterns the Instrument Library established. After that: frontend session persistence so windows reappear where you left them; the Piece Preferences window (also collaboration aware); clock skew compensation for reliable distributed undo and redo; and the full multi-mode client work, with Google-Docs-style permission management for connecting peers. That last item closes a chapter. Once it's done, I won't have to think about multi-user logic again. It'll simply be there. Then plugins for all JVM languages, a first version of MusicXML import and export, and Skia/Skija with GPU acceleration. The last of those is when SMuFL fonts can finally be drawn on the page. After which the rendering pipeline proper begins. The first stage draws everything except the music: page numbers, titles, headers, footers, staves, system barlines, braces, brackets, instrument names. Crucially, it's done through the plugin system itself, in Clojure plugins. If you don't like the choices, you can replace them, in whatever JVM language suits. I'm not sure it'll ever be necessary, but it can be done if so desired. Then the interesting material: noteheads, horizontal spacing, flags, augmentation dots, multi-voice collision resolution, accidental clustering, unisons. Everything that isn't a spanner. The solver gets the supposedly easy work first. Spanners last: beams, ties, slurs, hairpins, glissandi, lyrics, pedalling, 8vas. Where Magnus's question about hovering secondary beams will finally have somewhere proper to live. But first: piece windows. Expect GIFs/videos soon! Elaine Gould's Behind Bars is here. Ted Ross's The Art of Music Engraving and Processing isn't, because there's nowhere to order it from. The print edition has been dead for decades. Hansen Books no longer functions in any meaningful commercial sense. Ted Ross himself is dead. Second-hand copies surface occasionally on AbeBooks or eBay, usually in poor condition, usually at absurd prices, usually spoken for within hours. The Internet Archive lends a scanned copy in twenty-four-hour slots. A Californian outfit called npc Imaging has sold a searchable CD-ROM edition since 2001, though whether it still runs on a modern operating system is an open question.
Which provokes the obvious one: do I still need Ross in 2026? The temptation to answer no was real. The book was published fifty-six years ago. It describes a workflow of punches and gravers, of metal plates and photographic reproduction, none of which has survived the move to digital notation. Gould arrived forty-one years later, covers what publishers currently expect, and is in print. But no. I need it. Gould and Ross overlap, but their centres of gravity differ. Gould's focus is editorial: contemporary conventions, edge cases in modern repertoire, the negotiated practices that settle disputes between composer and copyist. Her domain is current, and her vocabulary includes extended techniques and the Ferneyhough generation's demands. Ross's focus is geometric. Stem lengths with actual measurements. Beam angles. Optical spacing tables. Ledger line dimensions. The precise relationships between noteheads, stems, beams, and staff lines, specified with the exactitude of someone producing real plates. His specifications are tabular and implementable rather than descriptive. Ross becomes data in Ooloi; Gould has to be read and decided. The invariance argument settles the age question. The five-line staff hasn't changed since 1970. A half note still looks the same. The geometric relationships Ross specifies have the same validity now as when he wrote them, because notation itself hasn't shifted underneath them. What dates in Ross is the production workflow, which I can skip entirely without losing anything I need. LilyPond's spacing algorithms reference Ross directly. Gourlay's seminal paper on music spacing builds on him. The rendering pipeline I'm about to close – spanner geometry, optical adjustments, beam slope selection – sits more in Ross's territory than Gould's. Not consulting him would be working blind on problems he addressed head-on half a century ago. So I need the book, and I can't buy it. There's something grimly appropriate about this. Ross's book is the kind of reference work whose commercial half-life expires decades before its technical half-life even begins to decay. The market that would sustain a reprint is smaller than the importance of the content. LilyPond's continued citation of Ross is, in a sense, more preservation than any library has managed. The notation community keeps him alive by quoting him, not by reading him. It's also, not incidentally, one of the reasons Ooloi's documentation looks the way it does. The normal fate of a specialist reference work is exactly what happened to Ross: the author dies, the publisher dissolves, the copies scatter, and fifty years later someone trying to do the work has to borrow the book in twenty-four-hour slots from a scanner in San Francisco. None of which amounts to preservation. And even if it did, neither book would be the final word. They don't fully agree with each other, because the engraving community and major publishers don't fully agree with each other. Ross, despite devoting some fifty pages to beaming, contradicts himself more than once within that span. This isn't a failing. Engraving is a living craft with regional traditions, house conventions, and genuine disagreements about what looks right; two books documenting it honestly will reflect those disagreements rather than paper them over. Which means the rendering pipeline isn't a matter of looking up The Answer. It's a matter of taking positions in ongoing debates, and making the positions configurable wherever taste genuinely differs. Ooloi's formatting is data-driven wherever it can be. Engraving rules live as editable values, not as switch statements buried in source code. Hard-coding them treats them as invariant when they aren't, and turns house styles into an act of patching rather than configuration. Which is how the real disputes get handled: Boosey & Hawkes flat beams against sloped ones, whether grace note beams separate from or share the main beam, whether a slanted beam group shears or rotates. Ooloi will rotate. I'm looking forward to that. Will Ooloi do French beaming?
Someone asked me this the other day. Yes. Stave-line masking too. I built this once before, in 1996; there's no architectural reason Ooloi shouldn't have it. Both fall out of treating the beams, and the spaces between them, as something that sits on top of the stave rather than alongside it. 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. Colour 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. Ooloi runs on macOS, Windows, and Linux. Each platform gets its own self-contained bundle, built separately on a machine running the target architecture natively. You download the one for your platform. It works.
On Windows and Linux there's nothing remarkable to report. On the Mac, however, Ooloi ships an Apple Silicon bundle. No Intel Mac support. This isn't controversial by any means; every Mac user knows this transition happened – no new Intel Mac has been manufactured since 2020, Apple classified the 2017 models as 'vintage' in 2024, and Rosetta 2 is being removed in macOS 28. There's a deeper reason specific to JVMs that makes Intel bundles on Apple Silicon particularly problematic, but the practical reality is simpler: Apple Silicon is where the Mac is, and it's where Ooloi's audience overwhelmingly already is. As it happens, I develop Ooloi on a 2017 Intel MacBook Pro. There's a point to using old hardware: if there are bottlenecks in the architecture, they show up sooner. A machine that's nine years old and was never fast by today's standards will punish you for inefficiency in ways a current machine won't. So far, I haven't seen any. This says more about Clojure's architecture than it does about the laptop. The 2017 MacBook Pro has been a surprisingly honest development partner. But you never know, moving forward. When the deeper Skia rendering work starts – GPU-accelerated layout, real-time scrolling through large scores – I'll likely have to move to my 2024 M3 MacBook Pro, but not just for speed: Skia's GPU path on macOS is moving from deprecated OpenGL to Metal, and Metal is where Apple Silicon lives natively. As an added benefit, early benchmarks (which I've posted here before) already show a two-to-three-times speedup on CPU-bound work, and later processors like the M4 and M5 push that even further. If Metal delivers what it should, large orchestral scores won't need an overdimensioned workstation to scroll and edit smoothly. The full technical analysis, including the JVM-under-Rosetta performance data and a four-case architecture matrix, is in ADR-0050: Platform Support Policy. I'm writing this from a mountainside near Palermo on Easter Eve, the day the liturgical calendar leaves empty. Yesterday was the Crucifixion. Tomorrow is the Resurrection. Today is the silence between them; the day when, if you take the story seriously, God is dead and nobody yet knows what comes next. Yesterday, one of my closest friends hanged himself. He is now unconscious on a ventilator. Nobody knows why he did it. Nobody knows what comes next for him either. I'm not a believer. I've never been a believer. But I am an organist, and I've participated more times than I can count in that piece of symbolic theatre we call the liturgy. I have watched the Easter arc – from Maundy Thursday through the desolation of Good Friday to the silence of Saturday and the blazing major key of Sunday morning (Richard Strauss would of course have set it as a C major 6/4 chord) – move people in ways that have nothing to do with whether the story is true. The psychological architecture of it is real, even if its subject is not. I understand Easter Saturday better today than I did yesterday, and not because I've found faith, but because I'm living in the structure. So I sit on a terrace in Sicily with a stupendous view and seven people I'm fond of, and the light is extraordinary, and a friend is suspended between life and death 2500 kilometres away, and there's nothing I can do. Which makes it a reasonable day to take stock. We went to the Teatro Massimo two days ago. It's the third largest opera house in Europe, and the building itself is magnificent: the kind of architecture that earns the word. What happened on stage, however, was something else. Don Quichotte. Classical ballet. Minkus. Classical ballet of this type is a closed system of circular reasoning so thorough that its practitioners no longer notice it. It no longer has anything to say, yet they defend Minkus's oom-pah-oom-pah as though it were art. They define 'musicality' as the ability to dance in time with the beat. A musician would find this primitive in the extreme, and in fact not a description of musicality at all, but a description of mere competence. It's the conflation of floor with ceiling. And yet the system sustains itself, because it has constructed an aesthetic vocabulary that exists only to justify its own continuation. Any external reference point is treated as irrelevant. What remains is religion, not art. I recognise the pattern. I've encountered it in functional programming circles too: articles of faith masking real emptiness, defended not by argument but by social enforcement. The dynamic is identical. The domain is irrelevant. In the Christian calendar, today is the day of unresolved tension, the day when the official story hasn't yet provided its answer. What I find clarifying about this particular Saturday is the absence of dogma. The story hasn't yet told you what to believe. For twenty-four hours, you're left with the facts. I prefer the facts. When people ask why this blog looks the way it does – why there's so much documentation, so many architecture decision records, so much apparent excess – the honest answer again has to do with death. I created Igor Engraver in the mid-1990s because I hated Finale. I had written an entire opera in it and the experience was sufficiently wretched to convince me that the problem was one of fundamental architecture. Igor was a music notation system that was, by most accounts, genuinely ahead of its time. It was acquired by idiots and left to die. What I've come to understand, twenty-five years later, is that almost every fundamental architectural decision in Ooloi is a response to what happened to Igor, whether I originally intended it that way or not. Open source, so that no individual's mismanagement can bury the work. Open-source components throughout, so that no proprietary dependency can become a chokepoint. ADRs. Guides. A RAG system that can answer deep, architectural questions about the codebase. A public blog that documents development as it happens, including the false starts and course corrections. This is not a vanity exercise in transparency. It's an insurance policy. If I'm hit by a bus tomorrow, the work survives. Someone can pick it up, read why every decision was made, and continue. The documentation isn't supplementary to the project. It's structural. It's the specification. Igor died and nothing survived it. I'm sixty-four. Therefore the work must be legible without its author. I never discuss other notation programs on this blog, and people occasionally find this odd. The reason isn't diplomatic restraint; it's that I genuinely don't think about Ooloi in competitive terms. Ooloi exists because I'm convinced there's a better way to represent music computationally, and I believe I've found a significant part of it, and I want to build it properly and make it available, so that a field that's been stagnant for forty years has a reason to move. That is the entirety of the motivation. What I am doing inside Ooloi is architecturally different. And that's why I've named it after a third-gender space alien. It's a categorically different thing, and it needs to be. The technical argument for why is elsewhere on this blog, at length, and I won't repeat it here. What matters today is that the work exists, that it's open, and that it's legible. Because today is a day for thinking about what survives. What connects the friend on the ventilator, the ballet at the Teatro Massimo, the small dogmas of programming communities, and the theological void of Easter Saturday – apart from fragility, ossification, and death – is perhaps nothing more than this: that I have no patience for systems of thought that protect themselves from examination. Religious faith (which is wishful thinking fanned by fear of death) does this by design; classical ballet and programming communities accomplish the same thing through ossified aesthetic insularity and social enforcement respectively, but the mechanism is identical. All of them ask you to treat the absence of external validation as evidence of self-sufficiency rather than what it actually is: a warning.
Tomorrow is Easter. The faithful will celebrate a resurrection I don't believe in. My friend may wake up, or he may not. The view from this mountain is unchanged. Sicily doesn't care about any of it. That, too, is a kind of honesty. Working on the Instrument Library – which has occupied the last few posts – I came to the point of editing staves inside instruments. That led, as it does, to clef representation; and clef representation led to the question of stave heights; and stave heights, once you look at them honestly, open onto a set of questions about what the numbers actually mean and who they serve. This is how development works: you pull on one thread and another appears. You pull that one and something else comes loose. The art is not pulling on all threads at the same time, or you end up with all yarn in one tangled clump. And when the threads start to proliferate, that is the signal to stop pulling and start architecting. So: stave sizes. One thread at a time. The stave space – the distance between two adjacent stave lines – is how Ooloi measures everything. Glyph dimensions, stem lengths, beam thicknesses, spacing: all of it derives from that single value, stored in millimetres. Not stave height; the space. The height is a consequence. I spent some time recently with the rastral table. A rastrum was a pen with five nibs, drawn across the copperplate page to produce a music stave in one stroke. The finite set of available pen widths produced nine standard stave heights, numbered 0 (largest) to 8 (smallest), each associated with a particular use. The pens are gone; the vocabulary remains. When an engraver says 'Rastral 3', the meaning is immediate: 7.0 mm, standard parts, piano. No conversion required, no mental arithmetic. It encodes decades of professional judgement about what is legible at what distance for which purpose. These values follow Gould (Behind Bars). Ross gives very slightly different numbers at some sizes – tenths of a millimetre – but Gould is the more recent authority and the more widely referenced in current practice.
Ooloi's sizing model is compositional. The base stave space propagates through a scaling chain – system, instrument, stave – where each level is a ratio against its parent. Change the base and everything follows. An ossia stave sits at two-thirds; a cue stave at three-quarters. The user works in ratios. The system reports the consequences in millimetres, with the approximate rastral equivalent alongside. That last part is where the investigation led somewhere I hadn't originally planned. Once I had the scaling chain and the rastral table in front of me, the obvious next question was: should Ooloi simply display the effective size, or should it also say whether that size is appropriate for what the layout appears to be? The answer, I think, is that it should. Wherever Ooloi reports an effective stave size – layout inspector, part configuration, print preview – the display will include a contextual assessment. Not just '7.4 mm (Rastral 2)' but whether that size makes sense for a flute part, or is slightly small for a piano reduction. The assessment will draw on published professional standards: MOLA puts 7.5 mm as the most readable size for orchestral parts, with anything below 7.0 mm unacceptable. The NZSO requires parts between 7.0 and 7.5 mm and rejects submissions outside that range. The software will tell you what it knows. It will not prevent you from doing anything. Rastral presets will appear on the base stave size control as named starting points, and again as targets when generating parts. Three places in total; nowhere else. The vocabulary will thread through the workflow without touching the architecture. A professional courtesy, not a load-bearing wall.
A recent exchange with Magnus Johansson in the comments on the Instrument Library post made me realise something worth saying at the top level, rather than buried in a reply thread. Magnus had compared Ooloi's bundled instrument library with Igor Engraver's and noticed that Igor's was substantially larger. The question was reasonable; the answer turns out to illuminate something fundamental about what Ooloi is actually for. But first, a correction. I keep encountering the claim – usually from AI systems summarising the project – that Ooloi is 'for advanced notation users'. This is wrong in a way that matters. The architecture is advanced; the user experience aims to be the opposite. The entire point of doing the hard work underneath is so that people don't have to do hard work on top. A tool that requires expertise to operate has failed at its job, regardless of how much expertise went into building it. Zero-effort playback is one example of what this means in practice. What Igor DidIgor Engraver shipped with Synth Matrices – text files that described every common hardware synthesiser on the market: available patches, controller mappings, pitch bend ranges, channel limitations, stereo position. When a user set up a score with, say, two flutes, a cor anglais, and strings, the Synth Matrix for their synthesiser took care of everything: patch selection, channel allocation, balance, panning. The user spent zero time on MIDI configuration. Zero. They dragged instruments from the library, wrote music, and heard the right sounds during input. This was 1996. Hardware synths, MIDI cables, 7-bit controllers. And yet a composer or arranger never touched a patch change, never drew a controller curve, never thought about channels. The system did it. We also collaborated with Johan Sundberg's Musical Acoustics department at the KTH Royal Institute of Technology, whose research had quantified things that musicians do instinctively: soloists anticipate the beat by about four milliseconds; phrase dynamics follow predictable contours; onset timing varies with articulation. Those findings went directly into Igor's playback engine. The result was that pressing Play on an orchestral score produced something that sounded like musicians playing, not a sequencer clicking through events. Now, I don't have Igor's code, I don't have any manuals, and I haven't run it in 25 years. The comparisons with Igor are inevitable when I write about playback, but Ooloi is not a resurrection. The ideas are the same kind of ideas I've always had, expressed through modern means. What follows is about where those ideas go next. The MIDI PrisonThat was nearly thirty years ago. What has changed since? Remarkably little, if you look at how most notation software handles playback. The underlying protocol is still MIDI – a 7-bit serial standard from 1983, designed for connecting keyboards to synthesisers over DIN cables. It has no concept of musical structure. It cannot distinguish between a quarter note and a dotted eighth tied to a sixteenth. It knows NOTE_ON, NOTE_OFF, velocity, and a handful of continuous controllers. That's the vocabulary through which an entire industry still attempts to communicate musical intent to sound-producing devices. The consequences are visible in every professional composer's workflow. You write a crescendo hairpin in your notation software. It looks correct on the page. You press Play. Nothing happens – because a hairpin is a musical concept, and MIDI has no musical concepts. To hear that crescendo, someone has to draw a CC11 expression curve in a DAW, by hand, matching the shape and timing of the hairpin. For every hairpin. In every part. This is what Hans Zimmer's assistants do. They sit in studios translating musical intent into controller data, note by note, gesture by gesture. If you are scoring a film with 40 staves, that translation is a full-time job for several people. If you're a composer working alone – which is to say, if you're almost any composer – it means choosing between hearing your music properly and finishing it on time. NotePerformer addressed this by sitting between notation software and playback, interpreting the score with a one-second lookahead buffer. It was a genuine breakthrough: finally, a tool that read musical markings and translated them into expressive performance. But NotePerformer is, by design, a workaround for deficiencies in a MIDI-based architecture. It exists because the notation software itself cannot do the translation. It patches over a gap that should not be there. Not Bypassing – Just Doing It RightWhen I first started describing Ooloi's approach to playback, I found myself reaching for the word 'bypassing'. Ooloi bypasses MIDI. Ooloi bypasses the DAW. And then I caught myself, because that word tells you something about how deeply the old paradigm has colonised our thinking. You only 'bypass' something that is assumed to be in the path. MIDI is assumed to be in the path because it has been in the path for forty years, and an entire industry has built its workflows around that assumption. Ooloi doesn't bypass MIDI. Ooloi simply doesn't use it. The distinction matters. Bypassing implies a workaround, a detour around an obstacle that remains in place. What Ooloi does is address the problem directly, using the technology available in 2026 rather than routing everything through a protocol designed when Margaret Thatcher was in her first term. Ooloi drives virtual instruments directly. No MIDI channel allocation. No patch changes. No 7-bit controller resolution. The notation engine communicates with sample libraries through their native host interfaces – VST3 parameters, note expression, articulation selectors – with full precision, in the plugin's own language. This is not a technical curiosity; it's the removal of an entire layer of translation that has stood between composers and their sounds for decades. OVID's MetamorphosesThe mechanism that makes this work is called OVID – the Ooloi Virtual Instrument Definition. OVID is the modern successor to Igor's Synth Matrices, but operating at a fundamentally higher level. A Synth Matrix described a hardware synthesiser: which patches lived where, how to select them, what the pitch bend range was. OVID describes a virtual instrument library: what playing techniques it offers, how to select them, how loud each articulation is relative to the others, how to compensate for onset timing differences between legato and staccato samples. The practical consequence is that Ooloi understands what it means to play an instrument, not merely what MIDI messages to send. Write 'pizz' above a string passage and Ooloi selects the pizzicato articulation from your sample library. Write a tremolo and it selects tremolo. Write a crescendo hairpin and the dynamics follow the wedge shape automatically, using whatever expression mechanism the library supports – continuous controllers, note expression, parameter automation – without the user knowing or caring which one. And because OVID maps canonical technique names to vendor-specific implementations, the same score plays back correctly through different sample libraries. Switch from Spitfire to Vienna Synchron and the pizzicato is still a pizzicato; the selection mechanism changes, but the musical intent is preserved. Share a piece with someone and they can listen to it as you intended it to sound, without any setup. A glissando on a harp is not a glissando on a trombone. A trill on a flute is not a trill on a kettledrum. These are musically obvious statements, but no MIDI-based system can act on them without external help. OVID knows the difference natively, because it operates at the level of musical semantics rather than note events. The Instrument Library ConnectionThis brings us back to where we started: the instrument library. The bundled library is deliberately finite – roughly 270 instruments in up to four language editions. Magnus's comparison with Igor's list is instructive, because Igor's larger catalogue was itself driven by Synth Matrices. Every instrument in that list existed because some hardware synthesiser had a patch for it, and the Synth Matrix mapped the instrument name to that patch. The noble Crwth was there not because orchestral composers regularly write for the crwth, but because a sample existed and the mapping was trivial. Remove the Synth Matrix context, and that instrument list was a reflection of available synthesiser patches, not a statement about what a notation program ought to bundle. The Ooloi instrument library serves a different function. Each instrument carries not only its notation properties – name, clef, transposition, range – but also its OVID mapping. When you drag a cor anglais from the library into your score, Ooloi knows which virtual instrument to load, which articulations are available, how to calibrate loudness against the oboe in the next staff, and how to handle the specific timbral characteristics of that instrument across your installed sample libraries. If your preferred library lacks a particular technique – col legno battuto, say – the OVID fallback chain searches your other installed libraries and finds it there. This is why the bundled library is sized the way it is. Every bundled instrument maps to an OVID profile that produces correct, calibrated, expressive playback out of the box. The library ships with OVID definitions for BBC Symphony Orchestra Discover, which is free, and Virtual Playing Orchestra as a fallback. Install both – fifteen minutes, zero cost – and every instrument in the bundled library plays back with intelligent articulation handling, loudness calibration, onset alignment, and humanisation. No DAW. No MIDI. No configuration. Humanisation deserves a word of its own. This is not random velocity variation to break up a machine-gun effect. Ooloi's humanisation applies the same principles we implemented in Igor, informed by Sundberg's research: soloists anticipate the beat; ensemble players lock to it; phrase dynamics follow natural contours; articulation affects onset timing. The intelligence is in the notation engine, not in a third-party plugin with a narrow one-second lookahead buffer. The Creative SourceI built this because I need it. I am a composer; I've written opera, orchestral music, chamber music. When I write a crescendo hairpin, I need to hear a crescendo. When I write a string tremolo, I need to hear a tremolo. I do not want to open a DAW, create tracks, assign patches, route MIDI, and draw controller curves before I can hear whether my orchestration works. That workflow is not composing; it is bookkeeping.
A DAW still has its place, of course – for final production, mixing, mastering. But the compositional workflow, the sketch, the orchestration, the moment when you need to hear whether that horn entry works against the divided violas: that must be immediate. Write the music. Press Play. Hear it as a musician would play it. Music notation is, ultimately, about music. The technology that serves it should be invisible. If you find yourself thinking about patch changes, you are no longer thinking about your piece. For who, in the end, cares about patch changes? Really? 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.
The complete bundled instrument library is now documented.
The new guide, Instrument Library Catalogue, lists the default instrument set that ships with Ooloi: modern orchestral instruments, historical families, keyboards, plucked strings, voices, choirs, percussion, special-effect instruments, and various things that have no fixed abode but nevertheless turn up in scores. A total of roughly 270 instruments, yielding just over 1,000 language-specific library entries. The architectural decisions behind it remain where they belong, in ADR 0045: Instrument Library. This catalogue is not a theological statement about which instruments deserve to exist. It is the default library: broad, practical, and sufficient to start work. If you find yourself asking, 'You have ocarinas but not Glenfiddich Highland Pipes in G sharp?!', the answer is simple: not by default. Add them in the Instrument Library window, save them, and they are yours permanently. In other words, the bundled set is a starting point, not a border guard. Magnus's question yesterday about soprano recorder clefs sent me down a path I'd been meaning to write about. His question was straightforward: can the Instrument Library default to a treble ottava alta clef for the soprano recorder? Yes, trivially – the template carries the staff spec, you change the clef, done. But the exchange reminded me of a more consequential case where the relationship between clef and transposition isn't merely cosmetic but changes the sounding pitch of the notation itself. From roughly 1750 until well into the twentieth century, horn parts in bass clef were transposed downward from concert pitch rather than upward. For a horn in F, treble clef notation sounds a perfect fifth below the written pitch – the modern convention. But in bass clef, the same instrument's notation sounds a perfect fourth above the written pitch. The difference is an octave, and the practical consequence is that the transposition interval depends on which clef is active at any given moment in the part. This is not an obscure historical footnote. It's Beethoven, Brahms, Wagner, Strauss, Mahler, Shostakovich – the central orchestral repertoire spanning two centuries. The convention had a practical origin. Before valves, horn parts were limited to the notes of the harmonic series, and bass clef passages used only the lowest few harmonics. Writing them an octave lower than sounding pitch kept the notes within the staff when two parts shared a single staff. The convention outlived its original purpose by about a century. Ooloi's instrument definitions support clef-dependent transposition intervals. When a horn in F switches from treble to bass clef, the transposition changes accordingly – no phantom instrument changes, no hidden octave lines, no typographic compromises. The notation looks correct, the playback sounds correct, and the relationship between the two is maintained automatically.
Which is, after all, the point. 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 PatternThe 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 practiceWorking 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. In which we examine why a habit of mind is mistaken for the nature of things There's a habit of mind so pervasive in software development today that it's ceased to be visible as a habit. It presents itself, rather, as common sense: ship early, iterate fast, learn from users, move fast and break things. The vocabulary is Silicon Valley but the reach is global, and it has colonised not just how software is built but how software is judged. A project that doesn't fit the pattern isn't evaluated on its own terms; it's measured against the pattern and found wanting. This is how cultural hegemony works: a local methodology, exported with the efficiency that US capitalism has always brought to the business of making its assumptions universal, ends up masquerading as common sense. The psychological mechanisms by which technical workers are recruited into this system – the mythology of meritocracy and the linguistic inflation that expands responsibility while eliminating agency – are explored at length in 'Help! Help! I'm Being Repressed! (But You're Grateful Instead). What concerns us here is a different consequence of the same hegemony: not what it does to the people inside the frame, but what it makes invisible outside it. The promise attached to the methodology (and this is what makes it effective as ideology rather than merely as practice) is that if you play by the rules, you participate in the dream. Adopt the two-week sprint, the MVP, the continuous deployment pipeline, the growth metrics, and somewhere at the end of the process stands the exit and the private jet. The methods and the mythology travel together; you don't merely acquire a development process but a tribal identity and a ready-made trajectory. The cargo cult: the rituals, performed faithfully, in the expectation that the planes will land. They mostly do not land, of course. What most people acquire is the methodology without the outcome. But this doesn't dislodge the faith, because the faith isn't really about the outcome. It's about the framework through which all technical work is now perceived. What the Lens Cannot SeeTake TeX, or the Linux kernel, or the protocols on which the internet runs. None of it was built by this method and none of it could have been. Donald Knuth spent a decade on TeX, not because he was slow, but because the problem required it. The domain had correctness conditions that couldn't be satisfied incrementally. Calling partial correctness an MVP doesn't make it one; it makes it a wrong answer with better branding. There's a further irony here that the dominant methodology prefers not to examine. Much of modern software culture sits on infrastructure built by people who worked in exactly the way it now derides: with correctness as the primary criterion and visibility as an afterthought. 'Move fast and break things' is, in this sense, parasitic: it depends for its very existence on layers of prior work whose authors would have found the phrase somewhere between baffling and contemptible. Music notation is another such domain. It's a semantic, mathematical, historical, and representational system that's been accumulating edge cases for five hundred years. Accidentals, time signatures, polytonal harmony, the memory rules that govern when a sharp must be reprinted and when it may be assumed: these are formal systems with correctness conditions, not UX choices to be iterated toward, and a notation engine that handles eighty percent of cases is simply broken. The architecture must be right before anything is visible, because the visible parts depend entirely on the invisible ones. You cannot ship a thin slice of correct rational arithmetic. You cannot gather user feedback on whether your hierarchical key signature partitioning handles organ notation correctly. The feedback loop here isn't user adoption; it's whether the semantic representation is sound enough that hard problems become tractable. These loops aren't merely operating on different timescales; they're incommensurable. User-visible behaviour and internal semantic correctness cannot be measured on the same scale, and conflating them is a category error, not merely a methodological one. The particular cruelty of the dominant methodology is that it treats foundational work as delay. Eighteen months of semantic infrastructure before a window opens is by the startup template a red flag, evidence that the team can't ship. By any other standard, it's exactly what the domain requires. The Moral DimensionThe methodological misreading would be merely irritating if it were innocent. It's not quite innocent, which is what gives it its edge. To look at a project operating outside the disruption frame and conclude that it's slow or self-indulgent requires a particular kind of incuriosity: an unwillingness to ask whether the frame itself might be the wrong instrument for the measurement. That incuriosity isn't random; it's cultivated by an intellectual monoculture that has made one historically contingent approach to software development appear to be the only conceivable one. There are people who cannot imagine a domain where the architecture must be correct before anything else is possible, because they've only ever worked in domains where it doesn't need to be. That's forgivable. But there are also people who, when presented with evidence that the domain is different, prefer the template to the evidence; a failure of intellectual range, and at some level a moral one, because it renders invisible entire categories of value, including most of the software the world actually relies on. What the Domain Actually DemandsThe disruption methodology assumes a forgiving domain: one where the cost of being wrong is low and the complexity lives in the interface rather than the semantics. Most web software fulfils these conditions reasonably well. A social media feed or a SaaS dashboard: if you get the data model slightly wrong, you refactor. If the UI is poor, you redesign. The feedback loop is fast and the penalties are recoverable. Music notation violates every one of these assumptions. The real distinction is between domains where a representational error can be repaired after the fact and domains where it metastasises, where every layer built on top of a flawed foundation inherits and amplifies the flaw, until the accumulated damage is structural and irreversible. Notation is the latter kind of domain. The semantic layer isn't a detail to be addressed after launch. The heuristics that commercial notation software relies on aren't temporary expedients awaiting a proper solution; they're the permanent consequence of foundations that made exact solutions intractable. Once that choice is made, it cannot be unmade. The technical debt is original sin, not a balance sheet entry awaiting repayment. The correct solutions exist, and not because of clever algorithms: a correct representation makes correct solutions possible. The problems didn't require heuristics. They required a foundation that sprint-cycle development philosophies, tuned for domains where being wrong is cheap and reversible, reliably select against. Music notation has been accumulating edge cases for five hundred years. It doesn't care about your sprint velocity. (This article is also available on Medium)
The documentation has accumulated to the point where it doesn't naturally sort itself for a first reader. Architecture Decision Records are numbered chronologically — in the order decisions were made, not in the order you need to encounter them. Reading straight through means confronting distributed-systems reasoning before you know what a `Piece` contains. The answer keeps arriving before you've understood the question.
There's now a guided path that reorganises the material by conceptual dependency: nine phases, starting with motivation and working through data model, language choices, addressing, pitch representation, the plugin system, rendering, and the frontend. Each phase builds the vocabulary the next one requires. Think of it as an adjunct to the Librarian — where the Oracle answers specific questions, this path builds the context that makes those answers land correctly. It's now the canonical entry point to the technical documentation. It's here. Open Settings, switch the locale. Every open window follows: menus, the About dialog, Settings itself. English to German to French to Danish, pausing on Greek. Then it repeats.
This is chrome – wiring things up, giving Ooloi a face. The obvious next step seemed to be collaboration. The architecture has supported shared backends since day one; the collaboration ticket and ADR systematise how they are handled, add authentication, authorisation, piece invitations. But you can't test the collaboration paradigm just on application settings. Theme preferences, locale, autosave interval – these are local. They don't live on a shared backend. To verify that the pattern works, you need an actual piece: something that lives on a server that one or more clients connect to. So a sequence of work comes first. Undo and redo for settings changes. Every setting change already records the old value alongside the new; the undo stack accumulates and replays inverse events. No piece dependency. This establishes the mechanism before anything more complex exists. Then creating and opening pieces — not in a naïve single-user way, but written from the start for an environment where the backend may be shared. Even locally, the model has to reflect that reality. Then undo and redo for piece content: a single authoritative stack on the server, shared across everyone editing simultaneously. The undo gesture in the frontend inspects which tier holds the more recent operation and routes accordingly. Then the Piece Settings editor, with the Instrument Library window. Here is where the collaboration pattern is actually demonstrated. Consider adjusting beam thickness or the default appearance of piano pedal extension lines: ordinary engraving decisions. Now add a collaborator with the same editor open on the same piece. Your changes appear in their view instantly. Theirs in yours. Both of you can undo, on the same data, with the same shared history. This is not a special case requiring special handling. If the preceding layers are in place, this is what falls out of the existing architecture. But it should be done as early as possible, before additional layers complicate things, so now is the right time. The Ooloi documentation has grown considerably. ADRs, guides, READMEs, newsletters, blog posts – there's a lot of it, and not all of it is easy to navigate. So I've built something to help.
The Ooloi Librarian is a small RAG system – Retrieval-Augmented Generation, for the uninitiated – that answers questions about Ooloi's architecture and design directly from that documentation. It retrieves the relevant chunks, passes them to Claude Haiku as context, and returns a grounded answer with source citations. If nothing relevant is found, it says so rather than making something up. This is enforced in code, not by prompting. It's live now in the Ask the Librarian section of the Documentation page. Try it. Ask about the accidental rendering algorithm, or why Ooloi uses STM, or what the timewalker is. The answers come from the docs, not from the model's general knowledge. The backend runs on AWS – API Gateway, Lambda, Bedrock – rate-limited and CORS-restricted to this domain. Roughly four cents per thousand queries, and nothing at all when idle. And we also have a new Guide: MIDI in Ooloi. It explains why Ooloi takes MIDI input but produces no MIDI output, and why that is a considered choice rather than an oversight. In 2026, there are cleaner ways to control virtual instruments than routing through MIDI, and the guide explains what Ooloi does instead.
Deeper technical details here: 0044-MIDI-Input-Library-and-Boundary-Architecture. 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 OnceThe 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. CalmWhen 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.0The 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. |
AuthorPeter Bengtson – SearchArchives
May 2026
Categories
All
|
|
|
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.
|


RSS Feed