Persistence
The .marbble project folder
Section titled “The .marbble project folder”Song.marbble/ project.json the Project (the active alternative), pretty-printed, with plug-in states (base64) project.autosave.json only while there are unsaved changes (see below) project.json.bak the previous save, only with Settings › General › Backups at 0 Audio Files/ recordings, conversions, joins, reverses, bounce-in-place results Freeze Files/ freeze renders ("<track> Freeze.wav") Alternatives/<name>/project.json the inactive project alternatives Backups/<alternative>/<time>.json earlier versions of each alternative (File › Revert to)ClipRef::pathis relative to the folder, except for files referenced in place (an imported WAV already at the project rate stays where it is, with an absolute path).- Save As to a new folder copies every clip inside the old folder (or with a relative
path) into the new
Audio Files/. - Plug-in state is
"<component base64>:<controller base64>"inPluginSlot::state. It is filled only when saving, autosaving, checkpointing withplugins, or rebuilding; the UI never sees it (for_ui). - Recordings go to
Audio Files/named<track>#NN.wav(or<project> - <track>#NN.wav), unless Settings › Recording names another folder, in which case they go to<folder>/<project name>/. - Opening a project whose sample rate differs from the device switches the device to the project’s rate, as Logic does.
Project alternatives
Section titled “Project alternatives”session/alternatives.rs. The active alternative is always the top-level project.json,
its name in Project::alternative (set to the project’s name at the first save), so
autosave, recovery, backups and older builds all treat it as the project and need no changes.
Inactive alternatives are Alternatives/<name>/project.json; they reference clips by the same
relative paths, so they share Audio Files/.
- New Alternative saves, copies
project.jsonintoAlternatives/<current>/, and renames the open project (on disk too) to the new name. - Switch reads the target first (a damaged one changes nothing), saves the active one,
copies
project.jsoninto its folder, writes the target asproject.json, removes the target’s folder and reopens. Each write is atomic; a crash between steps leaves at worst a duplicate (alternatives()lists a name once), never a lost alternative. - Rename of the active one rewrites the name in
project.jsonand the autosave at once, unsaved changes or not; of an inactive one, renames its folder. Backups move with the name. - Delete removes the folder and the backups; deleting the active one writes another into
project.jsonwithout saving. A project keeps at least one. - Names are folder names:
check_namerefuses empty names, a leading dot,/ \ : * ? " < > |and control characters. The UI mirrors it (alternativesModel.ts). - Save As writes only the active alternative into the new folder.
Backups
Section titled “Backups”session/backups.rs. Session::write_project is what save writes through: with
Prefs.general.backups > 0, the project.json being replaced (if readable and different) goes
to Backups/<its alternative>/<time>.json, where <time> is that file’s modification time in
UTC (2026-10-29T143205.123Z, sortable, valid on every file system; a clash adds a
millisecond), then all but the newest N are deleted, and any project.json.bak goes (it is
superseded). With 0, the single .bak is kept as before (recovery::write_project_file).
Opening falls back from project.json to the autosave, the .bak, then the newest backup of
the active alternative (latest_backup: backups of folders in Alternatives/ are skipped).
revert_to_backup saves, then writes the chosen version through write_project, so the state
before the revert becomes a backup; revert_to_saved discards the autosave and reopens.
The native File › Project Alternatives and File › Revert to submenus are rebuilt by the
set_project_menus command (menu.rs) from the list the frontend reads with project_files
and labels in local time (src/projectFiles.ts, run whenever the path, saved and dirty flags or
the alternative change). Their ids are alternative:<name> and revert:<file>.
Autosave and recovery
Section titled “Autosave and recovery”Session::autosave_jobis polled from the idle timer. When the interval in Settings › General has passed, the project is dirty and nothing is recording, it serializes the project (with plug-in states, so on the main thread) and returns a job;main.rswrites it on a new thread toproject.autosave.jsonvia a temporary file and rename. A hash skips writing an unchanged document.- Saving deletes the autosave (in the new and the old folder); so does Don’t Save.
recovery_info(dir)reports an autosave newer thanproject.json.recoverable_projectschecks the recent list plus every.marbblefolder in the projects folder and in~/Music/Marbble. Untitled projects autosave into their (not yet saved) folder in the projects folder, which is how they are found.open_recoveredloads the autosave and marks the project dirty, so saving writes it back.
Settings and other files
Section titled “Settings and other files”The config folder is $XDG_CONFIG_HOME/marbble, else %APPDATA%\marbble, else
~/.config/marbble (plugin_db::config_dir; macOS uses ~/.config/marbble too).
Marbble was called Fermata up to 0.1.0-alpha.5, and old_name.rs is the one place that knows it:
.fermata project folders still open and save in place, and old_name::migrate, first thing at
launch, moves the old config folder, ~/Music/Fermata Instruments and the webview’s storage
(named after the old identifier studio.fermata.daw) to their new names, never over anything
already there. src/oldName.ts then renames the fermata. localStorage keys. An existing
~/Music/Fermata stays the default projects folder until ~/Music/Marbble exists, since recent
projects point into it.
| File | Written by | Contents |
|---|---|---|
settings.json |
src-tauri/src/settings.rs |
Sections audio (AudioConfig), prefs (daw_engine::prefs::Prefs: general, recording, midi, editing, latency, click defaults), ui (UiPrefs: startup, confirm_unsaved, snap, nudge, keymap overrides, theme, ui_scale, wave_style, click_locates) and plugins (disabled ids, extra folders). Every field defaults, and a malformed section is replaced by defaults without losing the others. Written atomically. |
recent.json |
src-tauri/src/recent.rs |
Up to 20 canonical project paths, newest first (not written atomically). |
plugins.json |
plugin_db.rs |
The plug-in scan cache. |
plugin-scan.json |
plugin_db.rs |
The last scan’s report (bundles, plug-ins, failures with reasons), for Settings › Plug-ins. |
presets/<effect>/<name>.json |
presets.rs |
User presets of the built-in effects and the Channel EQ (see Presets). |
channel-strips/<name>.json |
strip_settings.rs |
Saved channel strip settings (see Channel strip settings). |
controller-assignments.json |
session/controllers.rs |
MIDI Learn’s controller assignments: controller (port without ALSA’s client:port numbers, channel, CC number or pitch bend), target (track id and parameter, plug-in instance id with its plug-in id, Channel EQ setting, Stereo Out or transport), mode and range. Global, like Logic’s; written atomically. |
templates/<name>.json |
session/templates.rs |
User project templates (File › Save as Template…): a Project without regions, clips, freeze state or alternatives, with plug-in states. new_from_template loads one as a new untitled project at the device’s rate, keeping its ids. |
Interface-only state lives in the webview’s localStorage:
| Key | Module | Contents |
|---|---|---|
marbble.theme |
themes.ts |
Theme id (a copy of ui.theme, for the first paint). |
marbble.uiScale |
uiScale.ts |
Interface scale, 0.9 to 2 (a copy of ui.ui_scale). |
marbble.waveStyle |
waveform.ts |
Waveform drawing style (a copy of ui.wave_style). |
marbble.paneSizes |
components/Splitter.tsx |
Library, Inspector, Browser, Mixer, Editor and header sizes. |
marbble.trackHeights:<project path> |
layout.ts |
{ base, heights, output } for one project (output: the Output track shown). |
marbble.bounce |
components/Bounce.tsx |
Tail, format, resolution, sample rate and normalize of the bounce and export dialogs. |
marbble.bounce-in-place |
components/Bounce.tsx |
Bounce in Place options. |
marbble.pianoRoll |
components/pianoRollEditor/prefs.ts |
Piano Roll view and Time Quantize settings. |
marbble.eventList |
components/EventList.tsx |
Event List filters, the type + adds, Catch and MIDI Out. |
marbble.stripSilence |
components/StripSilence.tsx |
The Strip Silence sheet’s last settings. |
marbble.showOverview |
components/Overview.tsx |
Whether the overview strip replaces the scroll bar. |
marbble.fxPanelSizes |
fxDockState.ts |
The last size of each effect type’s floating panel. |
marbble.detachedPanels |
detach.ts |
Which panels were detached at quit, and each kind’s window geometry. |
Appearance settings (theme, size, waveforms) are saved in settings.json by appearance.ts;
the localStorage copies only let the window open in the right theme and size before the
settings have loaded.