|
Today, preparing the drawing system, I returned to LilyPond’s essay on engraving. Within a few paragraphs I realised: we come from exactly the same place. The moment my spine tingled was when they described the shortened ledger line beside an accidental – that precise, almost invisible kindness that makes a score feel right before you know why. The line yields a little, the principle remains. That gesture is the mark of true engraving. Martin Spreadbury studied LilyPond when he built Dorico. I’m convinced Martin Keary did too, though we didn't talk about it when I had him on the line some time ago. LilyPond is the gold standard – the Holy Grail of digital engraving with a soul. The Masters We Serve Bärenreiter, Durand, Peters, Schott, Universal Edition. Publishers whose engravers spent ten years learning how to disappear behind their craft. The copper-plate aesthetic: bold, confident, generous. Staff lines weighty enough to guide the eye, noteheads round enough to feel sung, stems drawn with conviction. Modern digital engraving forgot this. It grew thin, bloodless — optimised for screens instead of for musicians. The Musician’s Eye As a performer, I react to good engraving before I’ve even played a note. My hands and eyes recognise the rhythm of care – the breathing room between notes, the balance of white space, the subtle confidence of proportion. A well-engraved page feels alive: it draws the body toward the instrument. The pulse is visible before sound begins. That is what the old masters knew, and what the best modern systems are all trying to recapture in their own ways. Good engraving isn’t decoration. Not by any means. It’s the first phrase of the music itself. The RecognitionThe lineage is clear, and the standard unchanging: those 1920s scores, those shortened ledger lines, that unspoken discipline that still knows how to sing.
We all worship at that shrine.
2 Comments
I've noticed people on various web forums asking when they'll see Ooloi render notation. Fair question.
Not yet. Foundation work comes first: the stuff that makes it possible to draw music efficiently. When the rendering implementation begins, the blog will shift from infrastructure to musical decisions: how things sit, straddle, and hang. Why this approach? This is open-source software built for decades of durability. It's not a commercial product racing as fast as possible to market. No competitors to 'eliminate', no users to 'capture'. Just correct foundations before visible output. Come back in a year or so: the conversation will be about music by then. What 'correct' means: Nested tuplets to arbitrary depth with exact arithmetic, for instance. No ticks, no fudging, no approximations. The kind of precision that takes time to build right. And tuplets are just one small example. Single-user first. Ooloi is designed for one person working on one score. Collaboration features are a byproduct of the architecture, not the main focus. The distributed design is about clean separation of concerns and deployment flexibility: collaboration just happens to work because the architecture supports it. Do I care if people understandably say 'I'll believe it when I see it'? No. The work continues. Research for Ooloi’s input system turned up something I hadn’t expected. Igor Engraver’s Flow Mode – the modal, stateful keyboard entry that defined its way of working – has never been recreated. Not by Dorico, not by Sibelius, not by Finale, nor by any of the open-source projects. Twenty-three years on, the idea has simply vanished.
Flow Mode was straightforward. You pressed “.” once and staccato stayed active; crescendo and diminuendo wedges and slurs extended naturally as you continued writing. The commands mapped directly to the symbols – intuitive, fast, and oddly satisfying. When Igor died of business failure in 2001, the method died with it. There is no academic record, no terminology, no sign that anyone even remembered it existed. I had fully expected other programs to have copied this feature; it gives a five- to ten-fold increase in music-entry speed. Web forums on notation are full of people asking for faster, more fluent keyboard entry, yet without the vocabulary to describe what they want. They are looking for something they have never seen. So this part of Ooloi isn’t innovation; it’s recovery. The system worked. It was lost for reasons that had nothing to do with design. The decision to re-implement it, and the details, are now recorded in ADR-0032: Ooloi Flow Mode. What remains is to implement it – and to find Magnus Johansson, who just might still have the user manual. 'Well then, alone!' – Elektra's cry when Chrysothemis refuses to help her. Not triumphant independence, but desperate necessity fused with unwavering resolve. The isolation isn't chosen; it's forced by the impossibility of finding anyone who shares her singular purpose. Orestes isn't likely to materialise. There was a functional programming conference in Stockholm recently. I'm sure it was excellent. I didn't attend. I should probably have been there – Ooloi is built in Clojure, after all, and finding collaborators would be useful – but I felt conflicted, and that conflict revealed something I'd been avoiding: the FP community cannot help me, and I don't need it anyway. Sect DynamicsI'm disappointed with the functional programming community. I was expecting higher-level thinking – freer thinking, commensurate with the intellectual freedom Clojure itself offers – but the atmosphere proved to be a shallow puddle of sectarianism. That probably has its reasons – being marginalised as a community is probably one of them – but the end result remains unchanged. The patterns are unmistakable. Knowledge as gatekeeping: the endless monad tutorial phenomenon, where every advocate believes they can explain monads better than everyone else, typically through increasingly baroque metaphors involving burritos, space suits, or elephants. This isn't pedagogy; it's ritual initiation. The complexity serves a social function – maintaining boundaries between insiders and outsiders. Purity as virtue signalling: debates about whether using `IO` makes you impure, whether exceptions violate functional principles, whether mutation in bounded contexts is acceptable. These discussions frame technical trade-offs as moral categories, as though architectural design were a moral discipline rather than an engineering one. The language reveals it – clean, pure, disciplined versus dirty, impure, undisciplined. This is religious vocabulary applied to software engineering. Terminology as tribal marker: deliberate retention of academic terminology when simpler terms exist. Endofunctor, catamorphism, anamorphism when 'map over containers', 'fold', 'unfold' would suffice. The obscurity is the point – it establishes hierarchy and demonstrates membership. The emphasis falls on mathematical elegance rather than problem-solving. The question isn't Does this help ship software but Is this theoretically sound. People who can recite monad laws but have never shipped a product receive more status than developers applying functional patterns to solve actual problems. Then there's the missionary behaviour: the conviction that imperative programmers need conversion. The framework isn't Here's another useful tool but You're doing it wrong until you see the light. This creates antagonism rather than adoption. Being marginalised as a community probably explains some of this – defensive posture manifesting as increased boundary enforcement, which creates insider/outsider distinctions, which enables status hierarchies based on doctrinal purity. But understanding the cause doesn't change the result, and it doesn't make the behaviour intellectually rigorous or practically useful. The Clojure Exception Clojure largely escaped this because Rich Hickey explicitly rejected purity culture. 'It's acceptable to use Java libraries'. 'Mutability in bounded contexts is fine'. 'Solve problems first'. The Clojure community focused on what you can build, not on arcane knowledge as status marker. This produced broader adoption without compromising functional principles. This is why I chose Clojure for Ooloi in the first place. But even within that pragmatic oasis, the broader FP community dynamics leak through. The conference I didn't attend would have featured both kinds of people – those interested in building things and those interested in doctrinal purity – and I couldn't predict which would predominate. The Intersection Problem Here's the substantive issue: finding Ooloi collaborators in FP communities is statistically improbable because very few people occupy my intersection point between various disciplines. Music notation requires an understanding of compositional structure, engraving conventions, and how musicians actually work. Functional architecture requires a sophisticated understanding of immutability, higher-order functions, transducers, STM transactions, and compositional patterns. Backend infrastructure requires a willingness to work on unglamorous problems like endpoint resolution and temporal traversal rather than visible features, and in Ooloi's case, an understanding of server technology and secure cloud operations. The population at that intersection is approximately one. FP communities might yield people who appreciate my transducer implementations or STM transaction handling. But they won't understand why endpoint resolution for slurs matters, how temporal traversal serves musical structure, or what makes intelligent engraving different from geometric placement. The domain expertise is orthogonal to FP community concentration. The inverse holds equally: musicians who understand notation deeply rarely have the architectural sophistication to work on Ooloi's core, and even fewer would find satisfaction in building infrastructure rather than using tools. I've worked outside the FP community all my life. Functional programming is a tool, not a (monadic) religion. (And why are monadic and nomadic so similar?) Why join the community now, when the benefits are unclear and the costs palpable? Consilium LatinumThe technical response is what I call the Latin strategy: making Ooloi's core a stable foundation for a plugin ecosystem. Build the architectural core once in Clojure, then let developers in other JVM languages contribute via plugins without needing to understand the underlying functional implementation. I've written about this approach in Penitenziagite!, so I won't rehearse it here. Elektra or Quixote? The psychological question is whether this makes me Elektra or Don Quixote. Elektra confronts a real murder, real injustice, a legitimate need for action that others refuse. The isolation comes from their cowardice or pragmatism, not from her misunderstanding of reality. The task is achievable and gets completed. The tragedy is the psychological cost, not the validity of the purpose. Quixote confronts imaginary problems with obsolete ideals, mistaking windmills for giants. The isolation comes from a fundamental disconnect with reality. The task is impossible because it's based on delusion. The comedy (later tragedy) is that the quest itself is meaningless. The distinction depends on whether the problem is real. Do musicians actually need what Ooloi provides? If existing notation software genuinely fails at problems Ooloi solves, then Elektra. If musicians are adequately served by current tools, if the architectural sophistication I'm building doesn't translate to problems they actually experience, then Quixote. But there's a third option beyond tragic obsession and delusional quest. I'm building something architecturally excellent because I can, because it interests me, because functional approaches to musical structure are intellectually satisfying. The architecture might be elegant, but it's not worth psychological dissolution. The Latin model suggests I've already chosen this third path. I'm building core infrastructure well, documenting it properly, then making it available via plugin architecture that assumes others might have different needs. That's craft separated from identity. Not Dancing to DeathElektra's tragedy is total consumption by purpose. She becomes nothing but the task, and when it completes, there's nothing left because she permitted no existence beyond vengeance. She dances herself to death.
I'm certainly not doing that. Ooloi is a project, not my entire existence. Sustainable completion means finishing the backend, documenting it clearly, releasing it, and then moving on. The work stands independently; I remain separate from it. I'll finish Ooloi's core architecture working alone, not because I prefer isolation, but because collaboration at this intersection point is impractical. The resolve comes from accepting reality rather than pretending community exists where it doesn't. The backend is complete. The transducer-based timewalker is fast, tight, and efficient. Endpoint resolution handles slurs and ties correctly. Nearly nineteen thousand tests pass. Vector Path Descriptors enable elegant client-server communication. Then comes plugin architecture, and seeing whether anyone finds Ooloi useful. If they do, excellent. If they don't, I built something architecturally sound and learned what I needed to learn. Either way, the work speaks for itself. And I continue existing beyond it. Nun denn, allein! Every notation program eventually reaches the same constraint: the interface and the layout engine compete for control of time. When they do, the system freezes.
ADR-0031 defines how Ooloi avoids that conflict. Local actions such as editing, scrolling, and selection remain inside the JavaFX event system, which responds instantly. Network events, like collaborative edits or layout changes, are handled separately, through a dedicated Event Router that never blocks the interface. For engravers, this means something simple but long overdue: editing and scrolling stay smooth, no matter how large or complex the score, and no matter who else is working on it. The document doesn’t describe a finished feature; it describes the foundation that makes such responsiveness possible. From this point on, Ooloi’s design rests on a single rule: the interface must never wait for the network, and the network must never interrupt the interface. Full text → ADR-0031: Frontend Event-Driven Architecture Last week's discussion on VI-Control turned into an unintentional seminar on what 'open source' actually means. Nando Florestan, a composer and developer learning Clojure, read through the draft licence with unusual care and pointed out something I hadn't thought through properly: the anti-rebranding clause made Ooloi proprietary in practice. Forks would have been impossible without my permission. The OSI definition is clear: that's not open source, regardless of intent. He was right. The clause came from experience, not malice. Igor Engraver derailed out of my control, and I didn't want that repeated. But open source is a matter of definition, not sentiment. Trademark protects the name 'Ooloi'. Anything beyond that belongs to the commons. The fix was simple: remove the modification. Pure MPL 2.0, no amendments. Register the trademark properly. Add a clear policy statement confirming that plugins and applications built using Ooloi's APIs aren't derivative works. Better to discover it before release than after. The conversation forced me to confront what open source actually means for this project: giving up control while retaining integrity. Why MPL 2.0 The Mozilla Public Licence sits exactly where Ooloi belongs: halfway between the ideological asceticism of GPL and the cheerful anarchy of MIT. File-level copyleft keeps the core collaborative while leaving everything built upon it entirely free. Reciprocity without coercion. If someone improves Ooloi's STM transaction coordinator or gRPC layer, those improvements remain in the commons. If someone builds a sophisticated playback system or commercial notation front-end atop Ooloi, they own it completely. That's how platform infrastructure should work. The 'for the avoidance of doubt' clarification states what's already true: plugins, extensions, and applications using Ooloi's APIs aren't derivative works. This matters because commercial developers won't participate if they need solicitors to interpret 'Larger Work' provisions. The clarification prevents that friction. The Alternatives FailGPL would poison the plugin ecosystem. The FSF's position on plugins-as-derivatives creates legal ambiguity that kills commercial participation. No professional algorithm vendors, no sophisticated commercial tools, no ecosystem. Apache/MIT/BSD permits enclosure. Someone could fork Ooloi's core into proprietary software, capturing improvements that should remain shared. For infrastructure intended as commons, permissive licences are actually less free. AGPL extends copyleft to network usage, which would criminalise legitimate commercial deployments: publishers running collaborative servers, institutions hosting multi-user environments, enterprises managing internal infrastructure. LGPL adds complex compliance requirements without benefits. MPL 2.0's file-level copyleft provides cleaner separation. The WordPress ParallelThe economics mirror WordPress's evolution. Core CMS functionality became commoditised infrastructure. Commercial value migrated to themes, plugins, hosting, services. Companies like Automattic built substantial businesses while the core improved collaboratively through thousands of contributors. Ooloi follows similar logic. What legacy notation systems monetise becomes architectural givens:
Commercial opportunities shift to where genuine value exists: sophisticated interfaces, professional algorithms, premium workflows, enterprise services. The core handles infrastructure. The plugins handle musical domain knowledge. The Ultimate IronyUnder MPL 2.0, a product such as Järvenpää Silence Notator 2.0 (purely hypothetical, right?) could theoretically build atop Ooloi while maintaining proprietary differentiation through proprietary plugins and interface. The core infrastructure they'd no longer need to maintain. The competitive advantages they'd demonstrate through superior musical intelligence. Whether this happens is irrelevant. The goal is proving that functional programming solves long-standing problems, enabling possibilities previously impractical: real-time collaboration without subscription lock-in, parallel processing without performance collapse, plugin extensibility without architectural fragility. The licence ensures improvements flow back to the commons while permitting commercial innovation. Practical ImplicationsFor developers evaluating Ooloi: Building plugins: Licence them however you want. Sell them, give them away, open-source them. No GPL contamination, no AGPL network copyleft. MPL 2.0 applies to Ooloi, not to your work. Modifying Ooloi's core: File-level copyleft applies. Modified files must remain MPL 2.0. You can still build proprietary applications using modified Ooloi. Just release your modifications to Ooloi's source files. Commercial deployment: Run SaaS services, embed in proprietary applications, charge what you like. MPL 2.0 requires nothing from you unless you modify core files. On Integrity Licensing isn't a legal appendix to the codebase; it's part of the architecture. A distributed system defines its boundaries through protocols. A licence does the same, only in law instead of syntax. Open source isn't marketing; it's a contract of trust. You keep the code open not because you must, but because integrity demands it. You protect the name not to hoard it, but to prevent confusion. Ooloi's licence now mirrors its architecture: clear boundaries, open interfaces, and a shared foundation for whatever might be built next. Clarity and transparency. When we last measured Ooloi’s storage engine, 50,000 musical objects occupied 14.5 KB – about 0.3 bytes per note. That already hinted at something unusual. The new benchmarks confirm it. A 1,000-measure orchestral score for 3333 / 4331 + piano + divisi strings was used as the test case – the kind of piece length found in the largest late-Romantic works: Wagner’s Götterdämmerung, Strauss’s Elektra, or Mahler’s Eighth Symphony. For non-musicians, 3333 / 4331 means three flutes, three oboes, three clarinets, three bassoons; four horns, three trumpets, three trombones, one tuba; plus piano and strings divided into multiple independent parts. In total: 29 staves, over 520 000 pitches, and thousands of simultaneous events – a scale that has historically defeated notation programs. So the orchestra is medium-sized romantic, but the length and amount of music is extreme: everyone is playing fast notes from start to finish. The benchmark is designed to test a heavy score. Bring on the worst case, so to speak. File Size and Persistence Saving this complete piece took 1.3 seconds; loading it, 3.3 seconds. The resulting file was 172 KB – smaller than a single page of PDF. The piece does not yet include graphical information (layout, coordinates, glyphs), which would roughly double the size or more, but at these levels it makes no practical difference. Even if it becomes ten times larger, it's still of no consequence. The numbers are that good. Streaming export (MIDI or MusicXML) completed in 1.25 seconds, using under 10 MB of memory. Even full materialisation of every internal element peaked at 244 MB, far below what such scores normally require. Traversal and ComputationBehind those figures lies the traversal engine that makes them possible. The full 520 000-pitch traversal completed in 0.58 seconds – roughly one million pitches per second. Cache repairs and dependency updates finished in 1–5 ms. Endpoint searches (for ties and slurs) are blazingly fast too, obviating the need for direct pointers. For comparison, a score of this magnitude would have strained the limits of any existing notation program. Finale or Sibelius might take tens of seconds – sometimes minutes – to perform an equivalent operation. Ooloi completes the same structural pass between heartbeats. What the Numbers Mean What makes this possible is not optimisation trickery but architecture: immutable musical data, transducer-based traversal, and disciplined cache coherence across every layer. The system streams; it does not churn.
For composers and engravers, these results suggest something once thought impossible – that even a full Wagnerian tutti might one day update instantly after an edit. Everything so far indicates that this should be achievable, not through hardware brute force but through architectural clarity. For developers, the message is the same: a purely functional, streaming model can handle orchestral-scale data in constant memory, with sub-millisecond locality. As far as I know, this is the first time a professional notation system has demonstrated real-time traversal of a Wagner-scale score with constant memory and sub-second exports. The invisible foundation – the hard part – is finished. All tests were run on a 2017 MacBook Pro with a 2.2 GHz six-core Intel i7 processor and 16 GB of RAM. A modern 2025 laptop or workstation would almost certainly halve every number reported here, and perhaps more. What feels instantaneous today will, in a few years, be beyond perception. Full benchmark results here. Still on track towards that goal of smooth scrolling through the Elektra recognition scene. Yes, another technical post. But this one explains why Ooloi doesn't demand you become a programmer to use it, or learn Latin to extend it for your specific needs. The architecture matters precisely because it removes barriers rather than creating them. In Umberto Eco's The Name of the Rose, a mad monk wanders through the monastery corridors shouting 'Penitenziagite!' – corrupted Latin mixed with vernacular, incomprehensible noise that might be prophecy or might be madness. Communication fails not from lack of content, but from linguistic confusion. The message is lost in translation. Software architectures shout 'Penitenziagite!' constantly, and we've grown so accustomed to the noise that we mistake it for communication. The Language Impedance ProblemWhen you write a plugin for a system, you shouldn't need to learn the implementation language. That seems obvious, yet most software makes exactly that demand. Functional programming libraries leak monads into Java APIs. Object-oriented frameworks force functional concepts into awkward method chains. Every language boundary becomes a barrier, every abstraction a translation exercise. The pattern is familiar:
This isn't malice. It's accidental linguistic imperialism – systems that never considered the difference between internal precision and external accessibility. The Monastic PatternMedieval monasteries preserved knowledge in Latin – a dead language, deliberately removed from common speech, chosen for precision and permanence. Yet they didn't demand everyone learn Latin to benefit from monastery medicine or improved agriculture. The knowledge stayed pure in the centre; the benefits propagated outward in the vernacular. Ooloi follows this pattern. The core is written in Clojure. This isn't negotiable, because the hard problems in music notation software require immutability, structural sharing, and proper concurrency. Functional programming isn't a preference; it's the only approach that doesn't collapse under its own compromises. But plugins can be written in Java, Kotlin, Scala, or any JVM language. Not as second-class extensions with limited capabilities, but as first-class citizens with full API access, equal performance, and no artificial limitations. The JVM interop means there's no penalty for crossing the boundary – your Java plugin operates with the same guarantees as code written in Clojure. This arrangement has three parts: I: The Scriptorium (Clojure core) – Where the hard problems are solved with uncompromising discipline. Immutable data structures provide structural sharing. Temporal coordination via the timewalker ensures correct musical semantics. STM enables proper concurrent editing. Zero-allocation hot paths ensure performance. This is where craft is mandatory, not aspirational. II: The Library (canonical plugins in Clojure) – Reference implementations showing how the architecture should be used. Teaching by example, maintaining standards, preserving patterns for others to study. III: The Gate (JVM plugin system) – The boundary that speaks idiomatically in every JVM language. Immutability guarantees propagate transparently. Plugin developers work naturally in their chosen language whilst benefiting from the rigorous core. Why This Structure WorksThe core cannot compromise. If mutability seeps in, if temporal coordination is abandoned for convenience, the whole thesis fails. The hard problems must be solved correctly, once, in the protected centre. But the perimeter cannot be closed. If only Clojure developers can extend Ooloi, adoption remains limited to those willing to learn functional programming. The architectural advantages – provable speedup on reflow operations, proper concurrent editing, elimination of state-corruption bugs – must be accessible without requiring conversion. This isn't architectural fussiness. It's the difference between a system that proves functional programming solves these problems and one that merely claims it whilst forcing everyone through the same narrow gate. Consider the alternative: most cross-language projects either compromise the core's purity to make external access easier, or maintain purity whilst making extension nearly impossible. Both approaches fail – the first produces unreliable systems, the second produces unused ones. First-Class CitizenshipWhen I say plugins are first-class citizens, I mean it precisely:
Your Java plugin implementing custom layout rules operates with the same capabilities as Clojure core code. The boundary is invisible – just clean interfaces and reliable contracts. No Penitenziagite Here Ooloi's architecture refuses the mad monk's cry. No demand that you learn Clojure to participate. No leaked functional programming concepts in the public API. No linguistic imperialism masquerading as technical necessity. The core speaks Clojure because that's the right tool for solving these problems correctly. The plugin system speaks your language because that's the right way to enable participation. Each side of the boundary uses the language appropriate to its purpose. This is architectural empathy: not compromise, but proper boundary design. The scriptorium can maintain Latin for precision whilst the gate speaks the vernacular. Work continues: the monastery's standards hold. The architecture neither shouts incomprehensibly nor demands conversion. There's no Inquisition burning engraving monks at the stake. The point isn't piety; it's architecture that stays intelligible. Just clarity, properly structured. – William of Baskerville Last week I wrote about flipping the Staff→Voice→Measure hierarchy to Staff→Measure→Voice. The structural reasons were sound, but I'd estimated a week for the implementation. Forty files to touch, thousands of tests to update, the whole spine of the system. With a 1:1 ratio of code to tests, that's rather a lot of surface area to cover. This usually means a lot of pain. The actual time: four hours. Claude Code's systematic approach made the difference. Seven discrete steps, continuous test validation, zero functionality regression. Tests failed as expected during refactoring, but the effort to fix them was absolutely minimal. This wasn't vibe coding; I controlled the process carefully, directing each step. Yet even with that careful oversight, the work required was remarkably little. Claude 4.5, being new and even more capable, may well have been key here. The result was better than expected as well: 30–99% less allocation through transducer-based lazy sequences, 211 fewer lines of code, clearer logic throughout. What surprises me isn't that AI-assisted development helped; it's the magnitude. A tenth of the expected time isn't incremental improvement. That's a different category of capability entirely. Now on to some small bits and bobs before the real work on the client starts: windows, drawing, printing, etc. I've been thinking about how to actually render music notation in Ooloi – not the data structures or the algorithms, but the visual side. Putting notes on a page. It turns out that trying to draw something has a way of exposing assumptions you didn't know you'd made. When you're formatting a measure, you need to see everything that's happening simultaneously in that slice of time. All the voices at once, so you can work out spacing, avoid collisions, align things vertically. It's how a conductor thinks about a score: bar 5 across all the instruments, not following each instrument's line from beginning to end. Here's the difficulty: in our current model, voices own measures. To get 'everything in measure 5', I have to find all the voices in a staff, look inside each one, find measure 5 in each voice, and collect them all together. I'm reconstructing what the printed page shows me directly: measure 5 contains these voices. The Original StructureThe hierarchy looked like this This seemed sensible at first. We were thinking about voices as independent melodic lines that happen to align at measure boundaries. We even structured it this way to handle ossia staves (those little alternative passages that appear for a few measures then disappear). But that's not how notation actually works. Measures are the organising unit. Voices are just 'simultaneous material in this measure'. The Revised StructureMeasures contain voices. Problem solved: 'give me everything in measure 5' is just a direct lookup. All the voices are right there. And ossia staves? Actually clearer in this model. A staff just enters at measure 17 and exits at measure 20. Clean and explicit. The ImplementationThe conceptual change is straightforward: flip the hierarchy so measures contain voices instead of voices containing measures. The implementation touches about 40 files because this hierarchy is everywhere – it's the spine of the system. Record definitions, accessor methods, specs, generators, thousands of tests, documentation. Everything builds on this assumption. But here's the thing: it's still intellectually manageable despite being architecturally pervasive. We're not adding complexity or clever tricks. We're just making the model honest. Every place that changes is a place where the code now says something true instead of something awkward. The timewalker (the thing that walks through a piece in temporal order) becomes significantly more efficient too. Instead of recursing through voices to find measures, it just walks the measures directly. We're expecting 15–35% fewer allocations – fewer temporary objects being created and discarded, which means it runs faster with less memory overhead. Discovery, Not PlanningHere's what's interesting: I didn't know this change was necessary until I started thinking about rendering. The original model wasn't obviously wrong. It worked fine for everything we'd built so far. Only when I sat down to work out how to actually draw the notation did the mismatch become clear. This is the value of working without artificial deadlines. If I'd been rushing to ship something, pressured to show 'progress' to investors or whoever, I'd have built the formatting layer on top of the awkward structure. It would have worked, sort of. With enough wrapper functions and helper methods, you can make anything work. But that's exactly how technical debt accumulates. You build on a foundation that's slightly crooked because you don't have time to straighten it. Then you build on top of that. Eventually the whole thing is held together with duct tape and hope rather than sound engineering. Instead, because I'm approaching this from a computer science angle rather than chasing some market opportunity, I can pause when something feels wrong and ask: is the model lying to me? Should I fix this now, while it's still straightforward, before I've built a cathedral on quicksand? The answer was yes. So we fix it. The architecture stays clean. No debt accumulates. When we get to rendering, the code will be straightforward because the structure matches what we're trying to do. Transparency as MethodA commercial project wouldn't announce an architectural change like this. It would be seen as weakness – proof that the team didn't plan properly, didn't understand the domain, had to change course because they got it wrong. But this isn't a commercial project. It's research. It's domain adaptation. In research, discovering that your model needs adjustment isn't failure – it's progress. You learn something about the domain you didn't know before. You refine your understanding. You make the system better. So I'm documenting it openly. Not because I have to, but because it's interesting. Because someone else working on music software might hit the same realisation. Because transparency about process (including the adjustments and course corrections) is more valuable than maintaining some illusion of perfect foresight. In commercial software, there's pressure to appear confident and consistent: it's part of the corporate theatre. In research software, there's freedom to be honest about what you're learning as you go. Why This MattersWhen your data structure fights the domain, everything becomes harder. Performance tricks, validation logic, traversal algorithms – they all work around the mismatch instead of with the model.
When the structure matches reality, everything becomes easier. The formatting code that sparked this whole thing? It'll be trivial now. Just iterate measures, render their voices. Done. This is one of those changes that seems obvious in hindsight but not when you're in the middle of building. You pick a structure that appears reasonable, you build on it, and only later when you try to use it do you realise the foundation is slightly crooked. The question is whether you have the time and space to fix it, and whether you have the honesty to admit it needs fixing. I'm fortunate. I have the time. This project doesn't require me to pretend otherwise. And if the page keeps telling me the model is lying, I'll keep listening. |
AuthorPeter Bengtson – SearchArchives
January 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