croma
A Rust-first toolkit for ABC music notation — convert ABC to MusicXML and
back, format it, and edit it with real language-server support. Everything is
built on one library (croma-core); the CLI, formatter, reader, and language
server are thin layers over the same parse/model pipeline, not separate parsers.
croma targets ABC 2.1 as the stable spec (standard),
with ABC 2.2 treated as a draft compatibility mode. It is parser-strict:
malformed input is rejected with a diagnostic rather than silently guessed at;
loose source is repaired explicitly by croma fmt --auto-fix.
What it does
- ABC → MusicXML (
croma xml,croma-core): a library-first ABC 2.1 parser and MusicXML 4.0 writer. This is the foundation everything else builds on. - Formatter (
croma fmt,croma fmt --auto-fix,croma-fmt): a canonical ABC pretty-printer. Formatting is idempotent and lossless;--auto-fixadditionally sanitizes loose source (redundant/malformed barlines, whitespace) into spelling the strict parser reads cleanly. - MusicXML → ABC (
croma read,croma musicxml2abc): the reverse reader — inverts croma's own writer and reads foreign MusicXML dialects (abc2xml, MuseScore, Finale, Sibelius). - Lossless round-trip + agent help (
croma agent): theMusicXML → ABC → MusicXMLround-trip preserves facts ABC 2.1 cannot natively express through private[I:cr <code> …]/%%croma-*carriers (ignorable by other ABC tools; the older[I:croma-<name> …]spelling is still read until 2.0);croma agentexplains those notations to an AI agent / LLM, with the same topics available as acroma-coreAPI. Seedocs/carriers.mdanddocs/agent.md. - Language server (
croma-lsp): a stdio LSP — diagnostics, formatting, semantic tokens, document symbols, folding, hover, completion, and code actions — that is a thin adapter over the core (its output is byte-identical to the CLI's, by construction). - Editor support: a reusable
tree-sitter-abcgrammar (Zed, web/WASM, Markdown```abcinjection, Neovim/Helix) and a Zed extension wiring the grammar +croma-lsptogether.
Out of scope: PDF rendering and engraving layout.
Install
From crates.io:
cargo install croma-cli # the `croma` CLI
cargo install croma-lsp # the language server
cargo add croma-core # the library (ABC <-> MusicXML, zero-dependency)Prebuilt binaries for macOS / Linux / Windows are attached to each
GitHub Release — croma-* (CLI) and
croma-lsp-* (server), one per platform.
From source (pinned Rust 1.96.0, via rust-toolchain.toml):
git clone https://github.com/ro-ag/croma
cd croma
cargo build --release # target/release/croma (CLI) + croma-lspA reader-less, zero-dependency CLI is available with
cargo build -p croma-cli --no-default-features.
Usage
# ABC -> MusicXML
croma xml tune.abc > tune.musicxml
# Lint / diagnose (text or JSON)
croma check tune.abc
croma check --diagnostics=json tune.abc
# Format (stdout), or rewrite in place, or repair loose source
croma fmt tune.abc
croma fmt --write tune.abc
croma fmt --auto-fix tune.abc
# MusicXML -> ABC
croma read score.musicxml --format abc
croma musicxml2abc score.musicxml
# Explain croma's non-standard carrier notations to an AI agent / LLM
croma agent
croma agent xvoice-slurAs a library:
use croma_core::abc_to_musicxml;
let xml = abc_to_musicxml("X:1\nT:Scale\nM:4/4\nL:1/8\nK:C\nC D E F G A B c|\n")?;croma-core's default build is zero-dependency and crates.io-publishable.
The MusicXML reader's only dependency (roxmltree) is opt-in via the
musicxml-reader feature and ships on the CLI binary, never the library default.
API stability (1.x)
Within 1.x, the conversion entry points (abc_to_musicxml, export_musicxml,
export_musicxml_with_options, parse_document, lower_score,
write_musicxml, write_abc, read_musicxml) and the builder methods on the
option types keep compiling. The public data types — the score model, syntax
and parse trees, diagnostics, and the option structs' fields — are not
covered: a minor release may add struct fields and enum variants, as 1.1–1.3
did. Build options from Default plus the builder methods rather than struct
literals, and give matches on croma enums a wildcard arm. 2.0 will mark these
types #[non_exhaustive] so the compiler enforces it.
Workspace
crates/croma-core ABC 2.1 parser, model, and ABC<->MusicXML conversion (the library)
crates/croma-cli the `croma` command-line tool (thin wrapper)
crates/croma-fmt the formatter / auto-fixer, on the core model
crates/croma-lsp the stdio language server, a thin adapter over core + fmt
tree-sitter-abc/ reusable ABC grammar (outside the cargo workspace)
editors/zed/ Zed extension (outside the cargo workspace)Per-capability docs live in docs/: formatter,
MusicXML reader, LSP,
editors, and the benchmark baseline.
How it's proven
croma's correctness is validated against a real-world corpus of 10,000 ABC files (the Zenodo ABC dataset), not just hand-written unit tests (though there are about 1,100 of those too). Every shipped capability has a corpus-scale gate that must stay green:
| Capability | Gate | Result (10k corpus) |
|---|---|---|
| ABC → MusicXML writer | structural parity vs abc2xml (raw comparator) |
9,228 / 10,000 match; the other 772 are 707 adjudicated divergences and 65 files with no music |
| Formatter | idempotent and lossless re-formatting | 10,000 / 10,000 |
| MusicXML → ABC reader | self-loop XML re-emission | 9,933 / 9,935 |
| MusicXML → ABC reader | foreign-dialect parity vs music21 | 98.50% |
croma-lsp |
diagnostics / formatting / token fidelity vs core | 10,000 / 0 mismatches |
croma-lsp |
totality (no panics, no hangs on malformed input) | 0 panics / 10,000 |
tree-sitter-abc |
clean parse (no ERROR nodes) | 99.46% (9,946 / 10,000) |
Plus cargo test --workspace, cargo clippy --all-targets -- -D warnings, and
cargo fmt --check on every change. Each gate's residual is adjudicated — a
documented, spec-justified non-bug — never an unexplained failure. The figures
were last measured on 2026-10-07; the corpus, the adjudications and the
foreign-dialect (music21) measurement live in the private croma-test repository,
so they cannot yet be reproduced from this repository alone.
The corpus, the abc2xml comparator, the spec knowledge base, and the Python provers live in a companion repository, croma-test, so a developer working on croma never has to download the heavy proving apparatus. croma builds and tests standalone (the corpus-scale gates are environment-gated and skip cleanly when the corpus is absent); the full matrix runs from croma-test.
How croma compares to abc2xml
abc2xml (by Willem Vree) is the
long-standing reference ABC→MusicXML converter, and it is croma's correctness
baseline: croma is validated against it over the full 10k corpus. Where abc2xml
is spec-correct, croma matches it (9,228 of the 10,000 files match structurally);
croma diverges only where abc2xml departs from the ABC 2.1 spec, and every such
case (707 files) is adjudicated and documented. croma is an independent,
library-first implementation written from the ABC 2.1 spec (it follows
abc2xml's conventions in a few small tables, credited in
THIRD_PARTY_NOTICES.md), and it improves on the
reference in several ways:
| abc2xml | croma | |
|---|---|---|
| Direction | ABC → MusicXML (the reverse is a separate script, xml2abc) |
ABC ↔ MusicXML in one library |
| Form | a Python script (needs a Python runtime) | a Rust library + native binaries — zero-dependency, embeddable, crates.io-publishable, callable from any language |
| Speed | interpreted Python | compiled Rust — 7,007 ABC→MusicXML files/s and 50,391 parse files/s over the 10k corpus |
| Malformed input | permissive — silent best-effort heuristics | strict ABC 2.1 — structured diagnostics (codes + spans); recovers only when the intent is unambiguous, and always warns |
| Output artifacts | inserts spurious elements as heuristic side effects — e.g. empty leading/section measures, phantom measures | spec-faithful, minimal MusicXML — declines those artifacts; most croma↔abc2xml divergences are exactly such an artifact croma omits |
| Beyond conversion | converter only | also a formatter (idempotent + lossless), a language server (live editor diagnostics / formatting / completion), a reusable tree-sitter grammar, and a Zed extension |
| Validation | — | corpus-proven over 10k with a documented gate matrix + a reproducible benchmark baseline |
| Safety | — | memory-safe Rust (unsafe forbidden), pinned toolchain, clippy + fmt gates |
In short: abc2xml is a mature one-way converter script; croma is a fast, embeddable, bidirectional ABC toolkit that holds stricter to the spec and emits cleaner, lower-artifact MusicXML, with editor-grade diagnostics — designed to be linked into applications and proven at scale. (It also stands on abc2xml's shoulders: using it as the parity baseline is how croma earns its correctness claims.)
Benchmarks
Headline throughput/latency, measured 2026-10-07 at commit 98ed4bd (Apple M4
Max, Rust 1.96.0, --release). Full methodology, per-call micro-benchmarks, and
reproduction steps:
docs/benchmarks.md.
| Layer | Headline |
|---|---|
| Parser (corpus, in-process) | 50,391 files/s · 27.9 MB/s |
| Formatter (corpus, in-process) | 32,139 files/s · 17.8 MB/s |
| ABC → MusicXML writer (corpus) | 7,007 files/s · 3.9 MB/s |
| LSP diagnostics, real-size p99 | ≤ 5.7 ms (release ceiling 50 ms) |
| LSP semantic tokens, real-size p99 | ≤ 0.49 ms |
tree-sitter-abc (steady state) |
~9.0–9.2 MB/s |
The corpus median file is 14 lines (max 244); at those sizes every operation is in the low-millisecond range. The ABC→MusicXML export path is super-linear on synthetic 1000-line inputs (recorded, no real-use impact — see benchmarks §6).
Contributing
Contributions are welcome — see CONTRIBUTING.md for setup,
how to run the gates, and the conventions (strict-spec parser policy, branch and
commit rules, and the "don't regress a proven gate" discipline).
License
Licensed under the Apache License, Version 2.0 — see LICENSE and
NOTICE. Third-party files that ship in the repository (tree-sitter's
MIT-licensed runtime files) and credits are listed in
THIRD_PARTY_NOTICES.md.
You may use croma freely, including in commercial products. In return, any
redistribution or derivative work must retain the attribution in NOTICE
(per section 4 of the license) — i.e. commercial users must credit croma. The
software is provided as-is, without warranty, and the author carries no
liability for its use (sections 7–8).