|
Creating a wind quintet with score and parts in Ooloi. Then adding doublings. No dialogs. Two years ago today, on 9 July 2024, the first commit to Ooloi went in. Time to take stock.
There's no music on screen. Two years in, and still not one printed note. No noteheads. No beams. No slurs. No page of music. All true, all planned, and all completely beside the point. Why? A notehead drawn in the first year would've been a comfort object, not evidence. So the visible surface stayed sparse on purpose while the real work went on underneath it. The absence of staves was never the interesting fact about this project. It was the cost of getting the interesting facts right, and this anniversary is the point at which they start becoming visible. This isn't an eccentric way to build notation software. It's how the serious ones are built, and the record is consistent. Sibelius took its two founders about seven years before the first release in 1993. MuseScore was carved out of a sequencer in 2002 and did not reach a stable 1.0 until 2011. Dorico, built by most of the old Sibelius team after they were let go, took close to four years and shipped its first version deliberately slow and incomplete, engine and engraving model first; the field judged the decision sound. Three programs, three decades, open source and commercial alike, and in every case the foundational years came before anything a user would recognise as finished. The counter-example proves the same point from the other side. Finale shipped fast in 1988, did more than anything before it, and became the industry standard for a generation. It was also, by wide agreement, punishing to use, and when its owners finally discontinued it in 2024 they said plainly that decades of accumulated code had made each new version harder to deliver and worth less. Feature piled on feature until the architecture could no longer move. That's the ceiling a notation program hits when the surface is built before the foundations are right. Two years isn't a long time to spend getting the foundations of a fifty-year program right, and I've never regarded it as a delay. Now, the bet on which Ooloi is built is functional programming. Music notation is a genuinely hard computer science problem wearing a friendly costume, and Clojure's properties looked to me like the right machinery for the parts of it that commercial programs have learned to manage rather than solve. Ooloi is my first Clojure project, though not my first Lisp by a long way, so the syntax was familiar ground; an old hand picks up a new dialect quickly. The deeper reorientation was immutability. Fully embracing immutable data structures, rather than merely tolerating them, changed how I thought about the whole domain, and almost everything architecturally distinctive about Ooloi fell out of that one shift. Undo, collaboration across machines, full use of multi-core parallelism, pervasive structural sharing: none of these is a feature bolted onto mutable objects. Each is a consequence of representing musical facts as immutable values. Learning to think that way was the real work of the first stretch. The harder part was psychological. A screenshot of notation would've been reassuring, and I could've produced something theatrical early on if theatre had been the goal. I grew up around theatre. I know a backdrop when I see one. I also know there's never a wall behind it. So the project spent a long time with most of its real work below the surface. The documentation was public, the ADRs were public, the blog was public, but the evidence still required reading. And the focus on the yet invisible might have given the impression that progress had been slow. So I drew up a list of what's been completed over the past two years. I expected it to be useful. I did not expect it to be quite so long. That surprised me, which is perhaps the point of doing the accounting at all. Daily work doesn't feel like accumulation. It feels like the next ticket, the next invariant, the next small refusal to leave a problem in a shape that'll charge interest later. Only when the pieces sit next to each other does the scale become visible:
During daily work, a subsystem closes and vanishes from attention. That's partly the method: finish something completely enough that it stops sharing the desk. The Instrument Library no longer asks to be designed. Localisation no longer asks to be invented, and the interface already speaks twenty-two locales. Undo no longer waits behind every future feature (with a small knife). Transport no longer converts itself through protobuf in-process for no reason. Window lifecycle no longer belongs to individual windows. Each closed subsystem removes one more thing from the cognitive field. The list makes visible what the surface hid. Ooloi hasn't spent two years waiting to become a notation program. It's spent two years removing the taxes a notation program would otherwise pay forever. This is also where the unusual working conditions matter. I've been ... unburdened by people. That sounds harsher than I mean it, though perhaps not by much. Not unburdened by readers, musicians, criticism or conversation, which have all mattered, but by the organisational forms that force software to explain itself before it has finished thinking. No investors, no release calendar, no committee, no inherited user base demanding that a workaround stay sacred because muscle memory has mistaken it for design. A team would have made some things faster and some things impossible, or at least premature. There's a stage in a project like this where the architecture has to be held whole in one mind, not because one mind is superior to several, but because dividing the thing too early changes the thing. Ooloi needed a period before that could happen. That period has had its own cost. Solitude removes obstruction, but it also removes alibis. Every accepted decision is mine. Every inconsistency in the documents is mine to catch. Every time Claude Code proposes a convenient shortcut, I have to know whether it's harmless, local, or the kind of thing that looks harmless now and grows teeth later. There's no one else to blame, which is excellent architecture and poor comfort. The AI has made the solitude more productive, though only because the architecture constrains it. Claude works best as an implementor inside settled boundaries. Give it one subsystem, clear invariants, failing tests, and a narrow target, and it moves with astonishing speed. Ask it to hold the whole system in its head at once, and it becomes what all of us become under those conditions: less intelligent than advertised. Every subsystem sealed below the musical floor is one fewer thing it has to juggle when the visible engraving work begins. That's another reason the long groundwork was worth doing. The cleared desk isn't a metaphor I use lightly. It's how the next phase becomes possible. The 3 June proof mattered because it compressed much of this into one modest action. Two laptops. One Ooloi hosting, one connected. A flute name changed on the guest, visible on the host. Undo on the host, reverting the guest's edit. The revert appearing on the guest. As cinema, it will not trouble Bergman. As architecture, it was the exact kind of evidence I had been waiting for. That little proof did not show engraving, and it did not pretend to. It showed authority, transport, shared state, invalidation, fetch, undo routing, and cross-machine propagation acting together without ceremony. Nearly two years of decisions appeared as a small correction to the name of a flute. Software milestones are sometimes like that: not fireworks, just a door opening without sticking because someone spent a very long time caring about the hinge. Now the work is moving towards the page. The custom picker is already visible and operable. It's not a native file chooser with new paint. It asks the backend what exists, receives opaque tokens, and never sees paths. Local storage and remote storage go through the same contract. The boring dialog is already the front edge of the piece model. Behind it, the piece window is now a live view of authoritative backend state, and this anniversary comes with a second small proof to show it. The video accompanying this post was recorded on two machines. On one, a wind quintet is assembled in a handful of gestures: flute, oboe, clarinet, horn, and bassoon dragged out of the Instrument Library into the Musicians panel of a shared piece window, each becoming a musician with its staves beneath it, and then dragged across into the Layouts panel to define the full score and the five individual parts. On the other machine, the same piece window answers. No delta is sent between them: a structural change names what is now stale, and the other screen refetches the canonical structure and redraws. It's the 3 June proof one storey higher. In June a flute changed its name and the plumbing held; here the same architecture carries piece structure rather than a library entry, edited collaboratively, with the invalidate–fetch–replace model the Instrument Library established generalising to the piece itself unchanged. And it's still, deliberately, not engraving. What crosses the wire and appears on the second screen is musical structure, not a rendered stave. There's no notehead in the video. The machinery that'll carry the music is running, and running across two machines, before the music arrives. Next come piece preferences and window persistence, the rest of the Piece Window arc. After that, MusicXML, because real scores must drive real notation work. Then Skija and the rendering pipeline. Then the noteheads: spacing, stems, flags, dots, accidentals, voices, collisions, beams, ties, slurs, hairpins, lyrics, pedalling, ottavas, tuplets, and the geometry of the page. The evidence will change again at that point. The collaboration you can already see in the video will still be there, but it'll be moving notation rather than structure. A slur will find space. A beam will sit, straddle, or hang. A note in a chord will change geometry around it. One screen will alter shared musical notation and another will answer with the same page. The program will make the argument in engraving, not only in structure. I'm looking forward to that more than I can sensibly say. Two years in, the thing that surprises me isn't that there's still no music on screen. I expected that. The noteheads were never beside the point as a goal, only as evidence; a program isn't proved by what it can draw first. The surprise is how much of the project is already behind the moment when music appears. The empty stave, seen from the surface, suggests absence. The inventory tells a different story: a great deal has already been decided, tested, closed, and made boring. Boring is underrated. Boring is what a foundation becomes when it's doing its job. So, coming out from the darkness that never really was, the next part will be visibly brighter, and with any luck it will involve fewer ... certificate files.
2 Comments
Two days from now it'll be two years since the first Ooloi commit. I've been working on the Piece Window, which is where a piece becomes visible and where you shape it: setting it up, creating scores and parts, and much more besides. It's a rich window, rich enough to do away with the modes and dialogs other programs need for this kind of work. And somewhere in the middle of building it, I noticed the thing had no documentation of its own. It was scattered across fourteen ADRs.
No more. Here's the fifty-fourth architectural decision record for Ooloi: ADR 0053: Piece Window and Piece Preferences. See you on Thursday. There should be a video by then, too. This is the door handle the previous blog post was talking about. There remain a few more bells and whistles to add to it, like favourites and a directory navigation dropdown, but this perfectly boring dialog handles a lot of complexity under the hood, so the user doesn't have to.
Oh, and the .ooloi files are empty – I haven't secretly imported huge scores without telling anyone. No, that happens after the Piece Window, up next, and after the first version of the MusicXML Import plugin, the first canonical one, is implemented. Then the engine will be bombarded with large amounts of musical data, to drive the implementation of real-world engraving. You go to open a piece. You reach for the menu, you pick 'Open', and a dialog appears showing you what's there. Nothing could be more ordinary. Every program you've ever used does some version of this, and you've stopped seeing it, the way you've stopped seeing door handles. Except this isn't the dialog your operating system would have given you. It looks close enough that most people will never notice the difference, and that's rather the point. The most boring dialog in the application is where everything is already different. Here's what's different. When you open a piece in Ooloi, even on your own computer, you're not looking at your folders the way Finder or Explorer would show them. You're looking at what Ooloi's engine tells you is there. The dialog asks the engine a single question, 'what's here?', and draws the answer. It never asks the operating system directly. On your own machine the distinction is so thin you'd be forgiven for missing it: the list looks like your disk because, for now, it more or less is your disk. But the habit is already broken. The application has stopped treating files-in-folders as the truth and started treating them as one possible answer to a question it asks somebody else. A Piece is More than a FileBegin with what a piece actually is, because the dialog only makes sense once that's clear. In most software a document is a file: a thing that lives at a location, that you save, that you can lose track of, that acquires a '(1)' after its name when the operating system panics about two things sharing a label. You hunt for it. You remember where you put it. You worry, mildly and constantly, about whether the version on the desktop is newer than the one in the folder you can't quite recall the name of. A piece in Ooloi is also a file, but that file's location isn't the important thing. The piece is a database with a permanent identity of its own, one that has nothing to do with where it happens to sit. You needn't think about saving it unless you want to; leave autosave on and the question never arises. You don't find it by remembering a filename and a folder. You find it by its name, by where it sits in your recent work, by searching for it. If that feels familiar, it should. It's how you normally work with files in a single-user application. Same Dialog, Different WorldsNow the part that matters most, and the reason any of this is worth a blog post rather than a footnote. Connect your Ooloi to another one, a school's server, a publisher's, or simply a colleague's machine across the room, and open that same dialog. The menu command is the same. The window looks the same. But what it shows you is no longer a polite version of a disk. It's an entirely abstract view of what's available to you, and only to you. This is the feeling of working in Google Docs, where you've long since stopped wondering which folder a document is 'in', brought to musical scores. The piece is simply there for you to access, and the question of its location has quietly ceased to matter. You don't see the remote machine's folders. You never see them. You see the pieces you're allowed to reach, and nothing else. The person who owns that Ooloi sees their own files when they open the dialog on their end; you, connected from outside, see your slice of what they've chosen to share. Same server, same dialog, two completely different worlds, because the world each of you sees is the one you're entitled to. This is worth dwelling on, because it's a privacy wall dressed as a convenience. Connecting to someone's Ooloi never lets you rummage around their computer. There's no path by which reaching their scores becomes reaching their disk, their documents, their photographs, the half-finished letter on their desktop. The dialog can't show you those things because it was never asking the operating system in the first place; it was asking Ooloi, and Ooloi only ever answers with what's yours to see. The boundary isn't a setting that can be left switched off by mistake. It's the shape of the thing. The Database is Really a GraphOnce other people are involved, the picture grows a dimension. What you're looking at, when you open that dialog against a shared Ooloi, is your corner of something larger: pieces on one side, people on the other, and between them the question of what each person may do with each piece. Read it. Print it. Edit it. Delete it. Different answers for different people, all hanging off the piece's permanent identity, which is the one thing that never changes no matter who's looking or from where. Pieces and people for the points, permissions for the lines between them: that's a graph, in the mathematician's plain sense, and your dialog is a view into one small region of it. A web, if you'd rather not think in mathematics, and your part of it is 'Shared with me', for scores. A conservatory running a server for two hundred students is the same idea at a larger scale; a publisher coordinating a dozen editors across a catalogue is the same idea again, larger still. None of it is a central cloud, some single machine in the sky holding everything. It's many Ooloi backends, each holding its own pieces, stitched together by the fact that every piece carries an identity that means the same thing everywhere. Your view across all of it is assembled from whatever each backend is willing to show you. Why it Isn't a GimmickIt would be easy to read all this as cleverness for its own sake, a notation program being contrary about file dialogs to seem distinctive. It isn't. It falls out of two decisions that were made long before anyone could collaborate at all, and that turn out to be the same decision wearing two faces.
The first: a piece's identity belongs to the piece, not to its location. Once that's true, location stops being something you manage and becomes something the system can keep entirely to itself. The whole world of pieces becomes a thing you query rather than a set of places you navigate. The second: the part of Ooloi you see and touch never reaches the disk itself. It always asks the engine, and the engine is the single authority on what exists and who may have it. That sounds like a technical nicety until you notice it's exactly what makes the privacy wall real. The screen can't leak the disk because the screen was never allowed near the disk. I'd like to claim I designed it this way on purpose. The truer version is that collaboration came along and showed me the architecture had already committed to all of it, whether I'd meant it to or not. The single-user case, you working alone on your own laptop, turned out to be the smallest possible version of the shared one: a group of one, asking the same questions, getting answers about the same database, through the same unremarkable dialog. Which is where we started. That dull little window you reach for without thinking, the one that looks like every Open dialog you've ever seen, is the whole worldview in miniature. It treats your work as something with an identity rather than an address; it shows each person only their own slice of a shared world; and it does both by the simple discipline of never looking at the disk itself, only ever asking what's there. The most boring dialog in the application. Look at it closely, and there's nothing boring about it at all. Three days ago I posted the road map: everything left before the real visible work begins, and the claim that it was all plumbing. Here's the receipt. Since then, six tickets have closed. The pattern across them is worth recording. Each was scoped narrowly, and each widened into something general before it closed. This is not scope creep; it's the opposite. The cost of doing the general thing sits close to the cost of the specific thing, so doing it once removes the work from everything downstream rather than leaving it to recur. Two of them shed precise new tickets as they widened, work that wasn't on the list a week ago and is now defined and waiting. I've seen this before, lower down. It's what the engine did during the validation phase, when problems the industry handles with heuristics collapsed into deterministic solutions because the foundations were right. Correct foundations make the general case cheap. What I'm watching now is the same effect one floor up, in the application layer rather than the engine. I didn't realise how much I'm looking forward to dealing with musical structures again, after this unsexy networking and certificate tedium. But the Piece Window is next, and that's true musical work. I'm looking forward to it tremendously. 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. 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)
|
AuthorPeter Bengtson – SearchArchives
July 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