ACIDCAT . FILE FORMAT REFERENCE

Serum Preset Anatomy

Xfer Serum 1 / 2XferJson . JSON + blob
rev 2026.07
magic "XferJson"
endian little
container JSON + binary
lineage Serum 2014

A .SerumPreset is a text-and-binary hybrid: an 8-byte XferJson magic, a tiny binary length preamble, a UTF-8 JSON metadata block (name, author, tags, product, version), then one opaque binary blob carrying the wavetable and modulation state. Because the head is JSON, the searchable metadata is plain text — readable verbatim, no binary decode — while the blob stays a black box. Serum 2 adds a twist: it zstd-compresses that blob. The tabs open each region, drawn byte by byte from a real Serum 2 preset. Hover a field to light its bytes, click a field with a + for its table. Color marks kind (see the key).

Most synth presets are all binary — you need a walker to read a single tag. Serum took the opposite bet: it leads with plain JSON. A .SerumPreset is three regions in a row: an 8-byte XferJson magic and a short binary length preamble, a UTF-8 JSON object holding every bit of searchable metadata, and then the binary blob that is the actual patch (the wavetables, the modulation matrix, embedded samples). The metadata is legible without decoding anything; only the blob is opaque. The preset below is a real Serum 2 patch.

a real preset, laid out
XferJson 8-byte magic + 9-byte preamble — u32 preamble = JSON length ├─ JSON 335 bytes { fileType, presetName, tags, product... } — UTF-8 text └─ blob 67,762 bytes zstd frame (28 B5 2F FD) — wavetable + mod state product "Serum2" v2.0.18 tags: Wavetable, Embedded-Data, Mono
Serum, dated
2014Xfer Serum ships — a wavetable synth that became the de-facto standard for modern electronic sound design. Serum 1 ships VST2 .fxp presets (the CcnK container); the native .SerumPreset with its XferJson magic arrives with Serum 2.
JSONthe metadata is text. presetName, author, description, product, version and a tag list live in a JSON object right after the magic — readable in any hex editor, indexable without a decoder.
2024Serum 2 — reports product: "Serum2". It zstd-compresses the binary blob and can embed sample data (the Embedded-Data tag), so a preset carries its own audio.
nowthe JSON reads, the blob stays opaque. presetName / author / tags / product lift straight out as text; the wavetable/mod blob is a region with no public format to decode.
find the JSON by the brace, not the preamble. Locate the metadata by scanning for the first {, not by trusting the binary preamble — that is robust to preamble changes between Serum versions. The u32 at offset 9 (just past the flag byte at offset 8) does encode the JSON byte length (335 here), a useful cross-check, but the brace scan is what drives the parse.
the blob is Zstandard. In this Serum 2 preset the blob's bytes 8–11 are 28 B5 2F FD — the zstd magic (little-endian 0xFD2FB528). Serum 2 compresses the wavetable and modulation state; Serum 1 stored it raw. Two u32s precede the frame: the uncompressed size (108,892 bytes here) at the blob's start, then a small count (2) immediately before the magic. The decompressed interior has no documented format either.
JSON is self-describing, and that is the point. Because the head is text, Serum's tags, author and product need no per-format walker — the same presetName/tags a human reads are what an indexer stores. Contrast Bitwig or Native Instruments, whose metadata is buried in binary trees that each need their own decoder.
a forged preset can blow the stack. A recursive-descent JSON parser descends once per nesting level, so a hand-crafted preset with thousands of nested objects exhausts it. A reader must bound the nesting depth or catch the failure rather than crash on it.
magic"XferJson"size8 + 9 bytespreambleu32 = JSON lengththenthe JSON brace

The file opens with the 8-byte XferJson magic — the signature that identifies any Serum preset or .fxp. Immediately after sits a 9-byte binary preamble: a flag byte, a u32 that equals the JSON block's byte length, and a reserved word. Then the JSON begins at its opening {. The map is the full 17-byte head of a real Serum 2 preset.

magic + preamble (bytes 0x00–0x10)

Hover json_length — 0x0000014F = 335, exactly the JSON block's size.

route on the magic, parse by the brace. "XferJson" is the same first eight bytes for Serum presets, wavetables and FXP wrappers; use it only to identify the file, then find the metadata by scanning for the first {. The preamble's json_length is a cross-check, not the source of truth — so a version that resizes the preamble still parses.
little-endian, like the plugin's host. The preamble's length word is a little-endian u32 — Serum is an x86/ARM plugin, and the binary framing follows the host's byte order. Only the JSON in the middle is endianness-free (it is text).
encodingUTF-8 JSONsize335 bytes herekeys11indexedas plain text

The metadata block is a single JSON object — ordinary UTF-8 text you can read in any hex viewer. It carries the preset's identity and every searchable field: fileType, presetName, presetAuthor, presetDescription, product, productVersion, a tags array, vendor, a content hash, and a struct version. These lift straight into a library index — no decoder, because there is nothing to decode.

the JSON, opening bytes

The first 24 bytes of the block — plain ASCII. Hover any byte: it is just text.

decoded keys (this preset)
fileTypeSerumPreset
presetNameBASS - Demolish
presetAuthor(preset author)
presetDescriptionUse Modwheel for Delay
product / productVersionSerum2 · 2.0.18
tagsWavetable, Embedded-Data, Mono
vendorXfer Records
version7.0 (struct schema)
product tells the generation. product: "Serum2" with productVersion: "2.0.18" marks a Serum 2 preset; a Serum 1 preset reports "Serum". The version field (7.0 here) is the JSON schema version, distinct from the plugin version — two different numbers to keep apart.
tags are the free win. The tags array (Wavetable, Embedded-Data, Mono) is exactly what a library browser wants, sitting in plain text. Embedded-Data means the preset carries its own sample content inside the blob — it is self-contained, not referencing an external wavetable.
contentwavetable + mod stateSerum 2zstd-compressedmagic28 B5 2F FDstatusregion, not decoded

Everything the JSON does not say lives here: the wavetables, the modulation matrix, oscillator and effect state, and (when tagged Embedded-Data) the sample content itself. There is no public format for the interior — it reads as one opaque byte region. What is legible is the framing: this Serum 2 preset opens the blob with a small header and then a Zstandard frame. The map is the blob's first 16 bytes.

blob header (first 16 bytes)

Hover zstd_magic — the Zstandard signature that marks the compressed frame.

Zstandard, since Serum 2. Bytes 8–11 are 28 B5 2F FD, the little-endian zstd magic 0xFD2FB528. The uncompressed size (0x0001A95C = 108,892) sits at the blob's start, with a small count (2) between it and the magic; the byte after the magic is the zstd frame-header descriptor. Serum 1 wrote the blob uncompressed, so its byte-8 signature is absent — a quick way to tell the two generations apart beyond the product string.
opaque by design. The blob's offset and length are knowable, but the frame contents and the wavetable are not — there is no documented interior and no reason to guess. Carving the region yields the literal blob, replayable back into Serum, byte-for-byte.