ACIDCAT . FILE FORMAT REFERENCE

Vital Anatomy

bare JSON.vital
rev 2026.07
magic "{" 0x7B
encoding UTF-8 JSON
container single object
marker "synth_version"

A Vital preset has no binary header. The whole file is one bare UTF-8 JSON object: nine metadata keys at the top, then settings, the entire synth state, with the wavetable audio riding inside it as base64. Vital is open source (Matt Tytel), so every key can be checked against the synth's own load code; everything below is verified against a real 1.0.7 preset. acidcat dispatches on the 0x7B magic plus a fast search for the "synth_version" marker before paying for a full parse. Hover any field to light its exact bytes and read the decode; click a field with a + to open its table. Color marks kind (see the key).

file regions

Regions here are JSON keys, not byte offsets. The writer sorts keys alphabetically, which puts the huge settings object before synth_version: in the sample the marker sits at byte 96,285 of 96,309, so a dispatcher has to search the whole file, not just the head.

top-level metadatabyte 0 . nine keys + settings

Byte zero is the opening brace, and because keys sort alphabetically the first entry is always author. The opening bytes of the sample, Future_EGuitar.vital:

preset_name"Future EGuitar"
author"Flamedragonz"
commentsthe browser description; here "Control the dynamics of your guitar with Velocity..."
preset_stylebrowser category, e.g. Experiment . Bass . Keys . Lead
synth_versionwriting Vital version, "1.0.7" here; the dispatch marker
macro1 .. macro4the four macro knob labels; "MACRO 1" .. "MACRO 4" when the author never renamed them
settingsone object holding the entire engine; everything else on this page lives inside it
settings: the engine776 keys in the sample . mostly flat

The synth state is almost entirely flat: 772 of the 776 keys in the sample are plain numbers, alphabetical, one per parameter. Even switches are floats (chorus_on = 0.0). The four structured keys are lfos, modulations, sample, and wavetables.

scalar parametersfilter_1_cutoff = 54.68 . distortion_drive = -11.1 . env_1_attack = 0.166 ... hundreds of them, every knob in the UI
osc_1 .. osc_329 flat keys each: level, tune, wave_frame, unison, phase, spectral morph; the table each one plays lives under wavetables
wavetablesthree objects with name, author, and the wave-shaping groups/components; here "EG6StringR(M)" and two "6StringR(M)"
samplethe sample oscillator: name, length, sample_rate, base64 audio; loaded but off in this patch (sample_on = 0.0)
filter_1 / filter_2 / filter_fx16 keys each: model, style, cutoff, resonance, drive, keytrack, formant x/y; only filter_1 is on here
lfosarray of 8 shape objects: name, num_points, points (x,y pairs), powers, smooth; "Sin" and "Triangle" in the sample
env_1 .. env_6DAHDSR each: delay, attack, hold, decay, sustain, release plus per-stage power curves
random_1 .. random_4random LFO parameters: style, frequency, sync, stereo, keytrack
effects chainchorus, compressor, delay, distortion, eq, filter_fx, flanger, phaser, reverb, each with an _on plus effect_chain_order; this patch runs compressor + distortion + eq + filter_fx
modulationsthe modulation matrix: the region below
modulation matrix64 slots . the patch's wiring

The most human-meaningful deep content in the file. modulations is an array of 64 slot objects, each naming a source and a destination; the slot's depth lives back in the flat parameters as modulation_N_amount (with _bipolar, _power, _stereo, _bypass beside it). One real entry from the sample, slot 3, drawn byte for byte:

env_1 -> osc_1_wave_frameamount 1.0: the envelope scans the wavetable across the attack; same wiring on osc_2
env_2 -> osc_1_levelamount 0.293: the slot drawn above
env_3 -> osc_1_tunea pitch envelope, with random_1 -> osc_1_tune adding drift on the same parameter
lfo_1 -> distortion_drivean LFO working the effects chain, while lfo_2 -> lfo_1_tempo modulates the modulator
velocity -> modulation_9_amountmeta-modulation: velocity scales another slot's depth; this is the "control the dynamics with Velocity" the comments promise
empty slots18 of the 64 carry empty source/destination strings; 46 are wired
wavetable payloadbase64 inside settings

Each wavetable nests groups > components > keyframes, and every keyframe carries the raw audio of one single-cycle frame as base64. This is where the bytes go: the first wavetable alone is 22 KB of the sample's 96 KB.

wavetablename, author, version, full_normalize, remove_all_dc, groups[]
componenttype (e.g. "Wave Source"), interpolation, interpolation_style, keyframes[]
keyframeposition plus wave_data: base64 of one 2048-sample float frame, 10,924 chars for 8 KB of audio in the sample
sample audiothe sample oscillator stores its audio the same way, under samples / samples_stereo
reading it with acidcat

Plain JSON means the reader's job is dispatch and selection, not decoding.

inspect behavior--pretty . deep
dispatch0x7B magic plus a bytes search for "synth_version" before json.loads; arbitrary JSON that merely starts with a brace is rejected without a parse
inspect --prettypreset_name, author, comments, preset_style, synth_version, and the four macro labels; one region spanning the whole file
deep modeenumerates osc_1..3 with their wavetable names, the LFO/envelope/effect inventory, and walks the modulation matrix as source -> destination : amount lines
hardeninga forged deeply nested object raises RecursionError and is caught, matching the Serum walker; the threat model for a JSON preset is DoS, not code execution