@xiaoxianthis/jsflp
v0.1.0
Published
FL Studio project file (.flp) parser for Node.js & browsers. A high-fidelity TypeScript port of PyFLP.
Maintainers
Readme
jsflp
A high-fidelity FL Studio project file (.flp) parser and serialiser for Node.js & browsers.
jsflp is a 1:1 TypeScript port of the excellent Python library
PyFLP by demberto. Its public API,
property names (snake_case), behaviour, exceptions and round-trip
guarantees are intentionally identical, while the implementation is
zero-runtime-dependency TypeScript that runs in Node.js 18+ and any
modern browser.
Highlights
- High fidelity —
parsethensaveround-trips a real-world.flpbyte-for-byte. The includednull_checktest loadsFL 20.8.4.flpand assertsBuffer.equals(input, save(parse(input))). - Zero runtime dependencies — pure TypeScript. Browser-friendly. Tree-
shakable. ESM and CJS builds with bundled
.d.ts. - PyFLP-compatible API —
parse(bytes),save(project), models likeProject,Pattern,Note,Channel,Sampler,Mixer,Insert,Slot,Arrangement,Track,VSTPlugin, native plugin classes, etc. - Strongly typed — every event, struct field and enum is typed.
Installation
npm install jsflpUsage (Node.js)
import { parseFile, saveFile } from "jsflp/node";
const project = await parseFile("/path/to/song.flp");
console.log(project.title); // → "PyFLP Test FLP"
console.log(project.tempo); // → 69.42
console.log(project.version.toString()); // → "20.8.4.2576"
console.log(project.ppq); // → 96
// Iterate channels
for (const ch of project.channels) {
console.log(ch.iid, ch.display_name, ch.color);
}
// Iterate patterns
for (const p of project.patterns) {
console.log(p.iid, p.name, [...p.notes()].length, "notes");
}
// Iterate mixer inserts
for (const insert of project.mixer) {
console.log(insert.iid, insert.name, insert.volume);
}
// Save back to disk
project.title = "Edited";
await saveFile(project, "/path/to/output.flp");Usage (Browser / framework-agnostic)
import { parse, save } from "jsflp";
const buf = new Uint8Array(await file.arrayBuffer());
const project = parse(buf);
const out: Uint8Array = save(project);
const blob = new Blob([out], { type: "application/octet-stream" });API surface
The public API mirrors PyFLP's exactly. Every
public class, enum and function uses the same name as in PyFLP, with snake_case
properties preserved (rather than the JS-conventional camelCase):
| Domain | Top-level classes |
|---|---|
| Project | Project, parse, save, parseFile, saveFile |
| Channels | ChannelRack, Channel, Sampler, Instrument, Layer, Automation, AutomationPoint, DisplayGroup, sub-models like Arp, Delay, FX, Envelope, SamplerLFO, Polyphony, Tracking, Keyboard, Playback, TimeStretching, Content, Filter, Reverb, LevelAdjusts, Time |
| Patterns | Patterns, Pattern, Note, Controller |
| Mixer | Mixer, Insert, Slot, InsertEQ, InsertEQBand, InsertDock |
| Arrangements | Arrangements, Arrangement, Track, ChannelPLItem, PatternPLItem, TimeSignature |
| Time markers | TimeMarker, TimeMarkerType |
| Plugins | VSTPlugin, BooBass, FruitKick, FruityBalance, FruityBloodOverdrive, FruityCenter, FruityFastDist, FruityNotebook2, FruitySend, FruitySoftClipper, FruityStereoEnhancer, Plucked, Soundgoodizer, WrapperPage |
| Types | FLVersion, RGBA, MusicalTime |
| Exceptions | FLPError, HeaderCorrupted, DataCorrupted, EventIDOutOfRange, InvalidEventChunkSize, PropertyCannotBeSet, NoModelsFound, ModelNotFound, VersionNotDetected, ChannelNotFound |
Faithful to PyFLP
Care was taken to match PyFLP semantics exactly:
- The licensee jumble/decode algorithm
- The pattern/arrangement timemarker disambiguation logic
- The
Optionalfield semantics (rewind on parse failure, skip on build) - The
_extragreedy-bytes preservation (any unknown trailing bytes survive a round trip) - The maximum inserts/slots tables by FL version
- The
_VolWord/_VolByte/Levelscascade for channel volume/pan - Note-name ↔ MIDI-key conversion using sharp names only
64-bitintegers are exposed asbigintwhere they appear in the binary format (rare; mostly internal length prefixes).
Build & contribute
git clone <repo>
cd jsflp
npm install
npm test # run all 139 unit tests (incl. null_check round-trip)
npm run build # produce dist/ (ESM + CJS + d.ts)
npm run typecheckLicense
GPL-3.0. The original PyFLP project is also licensed under GPL-3.0.
