@k-l-lambda/lilypond-node
v2.24.8
Published
LilyPond music engraving as a Node.js native addon
Maintainers
Readme
@k-l-lambda/lilypond-node
LilyPond music engraving as a Node.js native addon. Convert LilyPond notation to SVG and MIDI directly in Node.js without spawning external processes.
Features
- Fast: Native addon with no process spawning overhead
- In-memory output: SVG and MIDI delivered via callbacks, no temporary files needed
- LilyPond 2.24.4: Based on the latest stable LilyPond release
- Guile 3.0: Modern Scheme runtime
- TypeScript support: Full type definitions included
Requirements
- Node.js: 22.0.0 or later
- Platform: Linux x64 (more platforms coming soon)
Installation
npm install @k-l-lambda/lilypond-nodeThe install script automatically downloads prebuilt binaries from GitLab CI (~4MB).
Usage
Basic Example
const lilypond = require('@k-l-lambda/lilypond-node');
const lyCode = `\\version "2.24.4"
\\header { title = "Hello World" }
{ c' d' e' f' g'2 g' | a' b' c''1 }
`;
lilypond.engrave(lyCode, {
onSVG: (filename, content) => {
console.log(`Generated ${filename} (${content.length} bytes)`);
// content is SVG string, save or process as needed
},
onMIDI: (filename, data) => {
console.log(`Generated ${filename} (${data.byteLength} bytes)`);
// data is ArrayBuffer containing MIDI bytes
},
log: (message) => {
console.log(message);
}
}).then((exitCode) => {
if (exitCode === 0) {
console.log('Success!');
} else {
console.log('Completed with warnings or errors:', exitCode);
}
});TypeScript Example
import { engrave, EngraveOptions } from '@k-l-lambda/lilypond-node';
import * as fs from 'fs';
const lyCode = `\\version "2.24.4"
{ c' e' g' c'' }
`;
const options: EngraveOptions = {
onSVG: (filename, content) => {
fs.writeFileSync(filename, content);
},
onMIDI: (filename, data) => {
fs.writeFileSync(filename, Buffer.from(data));
}
};
const exitCode = await engrave(lyCode, options);
console.log('Exit code:', exitCode);Using Include Paths
const lilypond = require('@k-l-lambda/lilypond-node');
const lyCode = `\\version "2.24.4"
\\include "my-library.ly"
{ \\myCustomFunction }
`;
lilypond.engrave(lyCode, {
includeFolders: ['/path/to/my/library', './local-includes'],
onSVG: (filename, content) => {
// handle SVG
}
});API Reference
engrave(lyCode, options?)
Engrave LilyPond source code to SVG and/or MIDI.
Parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
| lyCode | string | LilyPond source code |
| options | EngraveOptions | Optional configuration object |
Options:
| Option | Type | Description |
|--------|------|-------------|
| onSVG | (filename: string, content: string) => void | Callback for each SVG page generated |
| onMIDI | (filename: string, data: ArrayBuffer) => void | Callback for MIDI output |
| log | (message: string) => void | Callback for LilyPond log messages |
| includeFolders | string[] | Additional paths for \include commands |
Returns: Promise<number> - Exit code (0 = success, 1 = warnings, other = error)
getDataDir()
Returns the path to the LilyPond data directory (LILYPOND_DATADIR).
Returns: string
version
The LilyPond version bundled with this package.
Type: string (currently "2.24.4")
Exit Codes
| Code | Meaning | |------|---------| | 0 | Success | | 1 | Completed with warnings | | Other | Error occurred |
How It Works
This package wraps LilyPond as a Node.js native addon (N-API). When you install the package:
- The install script downloads prebuilt binaries from GitLab CI
- The package includes LilyPond's Scheme files and fonts
- SVG output uses LilyPond's in-memory rendering (no temp files)
- MIDI data is returned directly as an ArrayBuffer
Architecture
┌─────────────────────────────────────────────┐
│ Your Node.js Application │
├─────────────────────────────────────────────┤
│ @k-l-lambda/lilypond-node │
│ (JavaScript wrapper) │
├─────────────────────────────────────────────┤
│ lilypond.node │
│ (Native addon - NAN bindings) │
├─────────────────────────────────────────────┤
│ liblilypond.so │
│ (LilyPond core + Guile 3.0 runtime) │
└─────────────────────────────────────────────┘Building from Source
If prebuilt binaries aren't available for your platform, you can build from source:
Prerequisites
# Debian/Ubuntu
sudo apt install \
cmake g++ pkg-config python3 \
guile-3.0-dev libpango1.0-dev libfreetype-dev \
libfontconfig-dev libglib2.0-devBuild Steps
# Clone the full repository
git clone https://gitlab.com/k.l.lambda/lilypond.git
cd lilypond
# Configure LilyPond
./autogen.sh --noconfigure
./configure --disable-documentation
# Build the addon
cd node-addon
npm install
npm run buildPlatform Support
| Platform | Architecture | Status | |----------|--------------|--------| | Linux | x64 | ✅ Supported | | Linux | arm64 | ⏳ Planned | | macOS | x64/arm64 | ⏳ Planned | | Windows | x64 | ⏳ Planned |
Performance
Compared to spawning the lilypond CLI for each file:
| Scenario | CLI | This Package | |----------|-----|--------------| | Simple score | ~500ms | ~130ms | | Multiple scores | N × 500ms | N × 130ms (no startup overhead) | | Batch processing | Slow | Fast (persistent runtime) |
The native addon eliminates process startup time (~400ms) and keeps the Guile runtime warm between calls.
Troubleshooting
"LilyPond native addon not found"
The prebuilt binary wasn't downloaded. Try:
npm rebuild @k-l-lambda/lilypond-node"LilyPond data directory not found"
The Scheme files weren't set up. Try:
cd node_modules/@k-l-lambda/lilypond-node
node scripts/postinstall.js"Unsupported platform"
Currently only Linux x64 with Node.js 22+ is supported. For other platforms, you'll need to build from source.
License
GPL-3.0 - Same as LilyPond itself.
