{
    "version": 1,
    "app": {
        "name": "Ferenc",
        "subtitle": "Waveform instrument",
        "purpose": "Play personal single-cycle waves and samples as a MIDI-controlled instrument.",
        "delivery": "A locally running instrument that plays the MG library, shapes notes, sequences them, and saves projects.",
        "reference": "Tactile desktop sampler panel with a waveform display, eight encoders, transport, and sixteen step pads."
    },
    "platform": {
        "server": "PHP 8.2 or newer with PDO SQLite, mbstring, and fileinfo",
        "preferred_router": "Slim 4 with slim/psr7",
        "local_router": "Dependency-free PHP adapter with the same route contracts",
        "client": "Server-rendered HTML and native JavaScript modules, with no frontend build step",
        "audio": "Web Audio plus an AudioWorklet for granular rendering",
        "midi": "Web MIDI; require a supporting browser and localhost or HTTPS",
        "sample_privacy": "Read the configured local bank; browser imports remain in IndexedDB. Never commit or upload the user's sound bank."
    },
    "public_deployment": {
        "base_path": "/fun-and-games/instruments",
        "delivery": "Three static playable browser instruments on the blog, with a shared original six-sample bank.",
        "samples": {
            "manifest": "demo-samples/manifest.json",
            "count": 6,
            "library_path": "/samples/library.json",
            "file_path": "/samples/{id}.wav",
            "provenance": "Deterministic procedural synthesis only. Never include the configured personal or Oxford Overdrive bank."
        },
        "projects": {
            "v1_adapter": "browser",
            "namespace": "ferenc.public.v1.projects.v1",
            "contract": "Same listProjects/saveProject/loadProject interface as the HTTP adapter. Validate names and complete instrument states; preserve independent project IDs and return detached state copies.",
            "failures": "Invalid state, missing ID, corrupt storage and quota failures reject visibly; never silently discard saved work."
        },
        "privacy": "User imports and projects stay in version-specific browser storage. No server writes, SQLite files, environment files, or private bank files are exported.",
        "v2_demo_slots": [
            16,
            17,
            18,
            19,
            20,
            21
        ],
        "acceptance": [
            "All three entries, version links, native module imports and worklets resolve under the nested base path.",
            "Exactly six original WAVs and provenance are exported; the bank is selectable in every instrument.",
            "V1 saves and restores validated projects without HTTP writes; V2 and Fischwerke retain their separate browser stores.",
            "Actual browser playback, Stop/Panic, sample loading, persistence and console errors are checked before publishing."
        ]
    },
    "versions": {
        "default": "v1",
        "switcher": {
            "navigation": "Stop audio/MIDI and await V2's latest autosave before full-page navigation. A failed save keeps the current page open with a recovery message. Unpowered V2 never overwrites existing saved data.",
            "placement": "Keyboard-accessible selector; in document flow on V1 to avoid covering controls, fixed on V2 and Fischwerke.",
            "shortcut": "Alt+V focuses the selector; Tab and Enter work normally while it has focus."
        },
        "v1": {
            "label": "V1 \u00b7 Astrax",
            "path": "/fun-and-games/instruments/v1",
            "entry": "public/index.php",
            "assets": {
                "app": "public/app",
                "assets": "public/assets"
            },
            "persistence": {
                "samples": "ferenc-sounds",
                "projects": "var/ferenc.sqlite"
            },
            "api": "Prefix the existing API contracts with /v1; retain unprefixed routes for compatibility.",
            "specification": "The app, UI, parameters, actions, flows, APIs, and contracts below describe V1."
        },
        "v2": {
            "label": "V2 \u00b7 Claude",
            "path": "/fun-and-games/instruments/v2",
            "entry": "versions/claude/index.html",
            "assets": {
                "js": "versions/claude/js",
                "css": "versions/claude/css"
            },
            "source": {
                "pull_request": "https://github.com/maciejjankowski/ferenc/pull/1",
                "ref": "claude/sweet-dijkstra-bc1x8g",
                "commit": "b9d5bb0e1cbc5d07484a7c288f4336879b822c15"
            },
            "persistence": {
                "database": "ferenc-v2",
                "midi_settings": "ferenc.v2.midi",
                "keyboard_mode": "ferenc.v2.keys"
            },
            "defaults": {
                "name": "UNTITLED",
                "bpm": 120,
                "master": 90,
                "pattern_steps": 16,
                "swing": 50,
                "keyboard": {
                    "oct": 0,
                    "root": 0,
                    "scale": 0
                }
            },
            "api": "No project server API; use version-specific IndexedDB and JSON project export/import.",
            "layers": {
                "intent": "js/params.js and js/project.js",
                "wiring": "js/app.js and js/main.js",
                "adapters": "js/audio/, js/midi.js, js/storage.js, js/ui/"
            },
            "actions": {
                "powerOn": "Resume audio on user gesture, initialize the worklet, restore local data, or load factory sounds.",
                "shapeSound": "Apply the parameter catalog and pages below; support eight encoders and parameter locks.",
                "sequence": "Play eight tracks with independent lengths, swing, microtiming, probability, conditions, queued patterns, and song chains.",
                "useMidi": "Choose MIDI routing and clock settings; receive notes, CC, bend, and program changes; send per-track notes.",
                "importSounds": "Import files or folders into the 128-slot pool and persist them in this version's browser database.",
                "saveProject": "Autosave to this version's browser database; allow a JSON export/import with embedded samples."
            },
            "flows": {
                "play": [
                    "open_v2",
                    "power_on",
                    "select_track_and_sound",
                    "choose_machine",
                    "play_keys_or_sequence",
                    "stop"
                ],
                "edit": [
                    "choose_page_1_to_6",
                    "choose_encoder_qwer_asdf",
                    "adjust_mouse_or_wheel",
                    "edit_pattern_or_parameter_lock"
                ],
                "restore": [
                    "autosave_or_export",
                    "reload_or_import",
                    "resolve_samples",
                    "restore_tracks_patterns_and_settings"
                ]
            },
            "limits": {
                "tracks": 8,
                "steps": 64,
                "banks": 4,
                "patterns_per_bank": 16,
                "slots": 128,
                "ticks_per_step": 24
            },
            "ui": {
                "knob_keys": [
                    "q",
                    "w",
                    "e",
                    "r",
                    "a",
                    "s",
                    "d",
                    "f"
                ],
                "page_names": [
                    "TRIG",
                    "SRC",
                    "FLTR",
                    "AMP",
                    "FX",
                    "MOD"
                ],
                "pages": {
                    "TRIG": [
                        [
                            "note",
                            "vel",
                            "len",
                            "prob",
                            "cond",
                            "micro",
                            "port",
                            "poly"
                        ]
                    ],
                    "SRC": {
                        "WAVE": [
                            [
                                "tune",
                                "fine",
                                "smp",
                                "wpos",
                                "uni",
                                "dtun",
                                "sprd",
                                "lev"
                            ]
                        ],
                        "SMPL": [
                            [
                                "tune",
                                "fine",
                                "smp",
                                "strt",
                                "end",
                                "mode",
                                "lpos",
                                "lev"
                            ]
                        ],
                        "GRAN": [
                            [
                                "tune",
                                "fine",
                                "smp",
                                "gpos",
                                "gsiz",
                                "gden",
                                "gspr",
                                "gscn"
                            ],
                            [
                                "gpjt",
                                "gjmd",
                                "grev",
                                "gwin",
                                "gpsp",
                                "gemt",
                                null,
                                "lev"
                            ]
                        ]
                    },
                    "FLTR": [
                        [
                            "fatk",
                            "fdec",
                            "fsus",
                            "frel",
                            "freq",
                            "reso",
                            "ftyp",
                            "fenv"
                        ]
                    ],
                    "AMP": [
                        [
                            "atk",
                            "dec",
                            "sus",
                            "rel",
                            "amod",
                            "vels",
                            "pan",
                            "vol"
                        ]
                    ],
                    "FX": [
                        [
                            "br",
                            "srr",
                            "driv",
                            null,
                            "dly",
                            "rev",
                            null,
                            null
                        ]
                    ],
                    "MOD": [
                        [
                            "l1spd",
                            "l1syn",
                            "l1dst",
                            "l1wav",
                            "l1sph",
                            "l1mod",
                            "l1fad",
                            "l1dep"
                        ],
                        [
                            "l2spd",
                            "l2syn",
                            "l2dst",
                            "l2wav",
                            "l2sph",
                            "l2mod",
                            "l2fad",
                            "l2dep"
                        ]
                    ]
                },
                "effects_pages": [
                    [
                        "dtim",
                        "dx",
                        "dwid",
                        "dfb",
                        "dhp",
                        "dlp",
                        "drev",
                        "dvol"
                    ],
                    [
                        "rpre",
                        "rdec",
                        "rdmp",
                        null,
                        "rhp",
                        "rlp",
                        null,
                        "rvol"
                    ]
                ]
            },
            "midi": {
                "controls": {
                    "7": "vol",
                    "10": "pan",
                    "71": "reso",
                    "72": "rel",
                    "73": "atk",
                    "74": "freq",
                    "75": "dec",
                    "76": "gpos",
                    "77": "gsiz",
                    "78": "gden",
                    "79": "gspr",
                    "80": "wpos",
                    "81": "strt",
                    "82": "driv",
                    "83": "gscn",
                    "91": "rev",
                    "92": "dly"
                }
            },
            "parameters": {
                "mach": {
                    "label": "MACH",
                    "min": 0,
                    "max": 2,
                    "default": 0,
                    "integer": true,
                    "lock": false,
                    "options": [
                        "WAVE",
                        "SMPL",
                        "GRAN"
                    ]
                },
                "note": {
                    "label": "NOTE",
                    "min": 0,
                    "max": 127,
                    "default": 60,
                    "integer": true,
                    "lock": true,
                    "options": null
                },
                "vel": {
                    "label": "VEL",
                    "min": 1,
                    "max": 127,
                    "default": 100,
                    "integer": true,
                    "lock": true,
                    "options": null
                },
                "len": {
                    "label": "LEN",
                    "min": 0,
                    "max": 18,
                    "default": 5,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "1/128",
                        "1/64",
                        "3/128",
                        "1/32",
                        "3/64",
                        "1/16",
                        "3/32",
                        "1/8",
                        "3/16",
                        "1/4",
                        "3/8",
                        "1/2",
                        "3/4",
                        "1/1",
                        "3/2",
                        "2/1",
                        "3/1",
                        "4/1",
                        "INF"
                    ]
                },
                "prob": {
                    "label": "PROB",
                    "min": 0,
                    "max": 100,
                    "default": 100,
                    "integer": true,
                    "lock": true,
                    "options": null
                },
                "cond": {
                    "label": "COND",
                    "min": 0,
                    "max": 15,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "---",
                        "FILL",
                        "!FILL",
                        "1ST",
                        "!1ST",
                        "1:2",
                        "2:2",
                        "1:3",
                        "2:3",
                        "3:3",
                        "1:4",
                        "2:4",
                        "3:4",
                        "4:4",
                        "1:8",
                        "8:8"
                    ]
                },
                "micro": {
                    "label": "MCRO",
                    "min": -23,
                    "max": 23,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": null
                },
                "port": {
                    "label": "PORT",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "poly": {
                    "label": "VOIC",
                    "min": 1,
                    "max": 8,
                    "default": 8,
                    "integer": true,
                    "lock": false,
                    "options": null
                },
                "tune": {
                    "label": "TUNE",
                    "min": -24,
                    "max": 24,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": null
                },
                "fine": {
                    "label": "FINE",
                    "min": -64,
                    "max": 63,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "smp": {
                    "label": "SMP",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": null
                },
                "lev": {
                    "label": "LEV",
                    "min": 0,
                    "max": 127,
                    "default": 100,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "wpos": {
                    "label": "WPOS",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "uni": {
                    "label": "UNI",
                    "min": 1,
                    "max": 7,
                    "default": 1,
                    "integer": true,
                    "lock": true,
                    "options": null
                },
                "dtun": {
                    "label": "DTUN",
                    "min": 0,
                    "max": 127,
                    "default": 24,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "sprd": {
                    "label": "SPRD",
                    "min": 0,
                    "max": 127,
                    "default": 64,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "strt": {
                    "label": "STRT",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "end": {
                    "label": "END",
                    "min": 0,
                    "max": 127,
                    "default": 127,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "mode": {
                    "label": "MODE",
                    "min": 0,
                    "max": 4,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "FWD",
                        "REV",
                        "LOOP",
                        "RLOP",
                        "PING"
                    ]
                },
                "lpos": {
                    "label": "LPOS",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "gpos": {
                    "label": "POS",
                    "min": 0,
                    "max": 127,
                    "default": 32,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "gsiz": {
                    "label": "SIZE",
                    "min": 0,
                    "max": 127,
                    "default": 70,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "gden": {
                    "label": "DENS",
                    "min": 0,
                    "max": 127,
                    "default": 70,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "gspr": {
                    "label": "SPRY",
                    "min": 0,
                    "max": 127,
                    "default": 10,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "gscn": {
                    "label": "SCAN",
                    "min": -64,
                    "max": 63,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "gpjt": {
                    "label": "PJIT",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "gjmd": {
                    "label": "JMOD",
                    "min": 0,
                    "max": 2,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "FREE",
                        "OCT",
                        "OCT5"
                    ]
                },
                "grev": {
                    "label": "RVRS",
                    "min": 0,
                    "max": 100,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": null
                },
                "gwin": {
                    "label": "WIN",
                    "min": 0,
                    "max": 5,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "HANN",
                        "TRI",
                        "TRAP",
                        "PERC",
                        "RPRC",
                        "RECT"
                    ]
                },
                "gpsp": {
                    "label": "PSPR",
                    "min": 0,
                    "max": 127,
                    "default": 40,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "gemt": {
                    "label": "EMIT",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "ASYN",
                        "SYNC"
                    ]
                },
                "fatk": {
                    "label": "ATK",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "fdec": {
                    "label": "DEC",
                    "min": 0,
                    "max": 127,
                    "default": 64,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "fsus": {
                    "label": "SUS",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "frel": {
                    "label": "REL",
                    "min": 0,
                    "max": 127,
                    "default": 64,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "freq": {
                    "label": "FREQ",
                    "min": 0,
                    "max": 127,
                    "default": 127,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "reso": {
                    "label": "RESO",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "ftyp": {
                    "label": "TYPE",
                    "min": 0,
                    "max": 4,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "LP2",
                        "LP4",
                        "BP",
                        "HP",
                        "NTCH"
                    ]
                },
                "fenv": {
                    "label": "ENV",
                    "min": -64,
                    "max": 63,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "atk": {
                    "label": "ATK",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "dec": {
                    "label": "DEC",
                    "min": 0,
                    "max": 127,
                    "default": 64,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "sus": {
                    "label": "SUS",
                    "min": 0,
                    "max": 127,
                    "default": 127,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "rel": {
                    "label": "REL",
                    "min": 0,
                    "max": 127,
                    "default": 40,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "amod": {
                    "label": "MODE",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "ADSR",
                        "AHD"
                    ]
                },
                "vels": {
                    "label": "VELS",
                    "min": 0,
                    "max": 127,
                    "default": 64,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "pan": {
                    "label": "PAN",
                    "min": -64,
                    "max": 63,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "vol": {
                    "label": "VOL",
                    "min": 0,
                    "max": 127,
                    "default": 100,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "br": {
                    "label": "BR",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "srr": {
                    "label": "SRR",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "driv": {
                    "label": "DRIV",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "dly": {
                    "label": "DEL",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "rev": {
                    "label": "REV",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "l1spd": {
                    "label": "SPD",
                    "min": 0,
                    "max": 127,
                    "default": 48,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "l1syn": {
                    "label": "SYNC",
                    "min": 0,
                    "max": 9,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "OFF",
                        "1/32",
                        "1/16",
                        "1/8",
                        "1/4",
                        "1/2",
                        "1BAR",
                        "2BAR",
                        "4BAR",
                        "8BAR"
                    ]
                },
                "l1dst": {
                    "label": "DEST",
                    "min": 0,
                    "max": 12,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "---",
                        "PTCH",
                        "FREQ",
                        "RESO",
                        "AMP",
                        "PAN",
                        "WPOS",
                        "STRT",
                        "GPOS",
                        "SIZE",
                        "DENS",
                        "SPRY",
                        "DRIV"
                    ]
                },
                "l1wav": {
                    "label": "WAVE",
                    "min": 0,
                    "max": 7,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "TRI",
                        "SINE",
                        "SQR",
                        "SAW",
                        "RAMP",
                        "EXP",
                        "RND",
                        "SMTH"
                    ]
                },
                "l1sph": {
                    "label": "SPH",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "l1mod": {
                    "label": "MODE",
                    "min": 0,
                    "max": 2,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "FREE",
                        "TRIG",
                        "ONE"
                    ]
                },
                "l1fad": {
                    "label": "FADE",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "l1dep": {
                    "label": "DEP",
                    "min": -64,
                    "max": 63,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "l2spd": {
                    "label": "SPD",
                    "min": 0,
                    "max": 127,
                    "default": 48,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "l2syn": {
                    "label": "SYNC",
                    "min": 0,
                    "max": 9,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "OFF",
                        "1/32",
                        "1/16",
                        "1/8",
                        "1/4",
                        "1/2",
                        "1BAR",
                        "2BAR",
                        "4BAR",
                        "8BAR"
                    ]
                },
                "l2dst": {
                    "label": "DEST",
                    "min": 0,
                    "max": 12,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "---",
                        "PTCH",
                        "FREQ",
                        "RESO",
                        "AMP",
                        "PAN",
                        "WPOS",
                        "STRT",
                        "GPOS",
                        "SIZE",
                        "DENS",
                        "SPRY",
                        "DRIV"
                    ]
                },
                "l2wav": {
                    "label": "WAVE",
                    "min": 0,
                    "max": 7,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "TRI",
                        "SINE",
                        "SQR",
                        "SAW",
                        "RAMP",
                        "EXP",
                        "RND",
                        "SMTH"
                    ]
                },
                "l2sph": {
                    "label": "SPH",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "l2mod": {
                    "label": "MODE",
                    "min": 0,
                    "max": 2,
                    "default": 0,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "FREE",
                        "TRIG",
                        "ONE"
                    ]
                },
                "l2fad": {
                    "label": "FADE",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "l2dep": {
                    "label": "DEP",
                    "min": -64,
                    "max": 63,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                }
            },
            "effects": {
                "dtim": {
                    "label": "TIME",
                    "min": 0,
                    "max": 16,
                    "default": 10,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "1/64",
                        "1/32T",
                        "1/32",
                        "1/16T",
                        "1/32D",
                        "1/16",
                        "1/8T",
                        "1/16D",
                        "1/8",
                        "1/4T",
                        "1/8D",
                        "1/4",
                        "1/2T",
                        "1/4D",
                        "1/2",
                        "1/2D",
                        "1/1"
                    ]
                },
                "dx": {
                    "label": "X",
                    "min": 0,
                    "max": 1,
                    "default": 1,
                    "integer": true,
                    "lock": true,
                    "options": [
                        "OFF",
                        "ON"
                    ]
                },
                "dwid": {
                    "label": "WID",
                    "min": 0,
                    "max": 127,
                    "default": 127,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "dfb": {
                    "label": "FDBK",
                    "min": 0,
                    "max": 127,
                    "default": 56,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "dhp": {
                    "label": "HPF",
                    "min": 0,
                    "max": 127,
                    "default": 24,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "dlp": {
                    "label": "LPF",
                    "min": 0,
                    "max": 127,
                    "default": 96,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "drev": {
                    "label": "REV",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "dvol": {
                    "label": "VOL",
                    "min": 0,
                    "max": 127,
                    "default": 100,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "rpre": {
                    "label": "PRE",
                    "min": 0,
                    "max": 127,
                    "default": 8,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "rdec": {
                    "label": "DEC",
                    "min": 0,
                    "max": 127,
                    "default": 70,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "rdmp": {
                    "label": "DAMP",
                    "min": 0,
                    "max": 127,
                    "default": 64,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "rhp": {
                    "label": "HPF",
                    "min": 0,
                    "max": 127,
                    "default": 16,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "rlp": {
                    "label": "LPF",
                    "min": 0,
                    "max": 127,
                    "default": 100,
                    "integer": false,
                    "lock": true,
                    "options": null
                },
                "rvol": {
                    "label": "VOL",
                    "min": 0,
                    "max": 127,
                    "default": 100,
                    "integer": false,
                    "lock": true,
                    "options": null
                }
            },
            "acceptance": [
                "Both entry routes resolve their own styles, modules, generated catalog, worker, and AudioWorklet.",
                "The switcher identifies the current version and navigates to the other version.",
                "Sample/project/MIDI settings use this version's storage namespace.",
                "The upstream engine and sequencer tests pass with the generated SSOT catalog."
            ],
            "moduleHash": "a5761cfc03754a26634b66434728d2dff5bfbbb020090344b515ad9ef1b123b5",
            "demo_bank": {
                "url": "/fun-and-games/instruments/samples/library.json",
                "slots": [
                    16,
                    17,
                    18,
                    19,
                    20,
                    21
                ]
            }
        }
    },
    "layers": {
        "intent": "Readable user actions and musical rules; no browser, database, or protocol mechanics.",
        "wiring": "Coordinate state, scheduling, and injected adapters through the contracts below.",
        "adapters": "DOM, Web Audio, AudioWorklet, MIDI, IndexedDB, HTTP, filesystem, SQLite, and routing."
    },
    "ui": {
        "groups": {
            "source": {
                "label": "Source",
                "shortcut": "1",
                "controls": [
                    "tune",
                    "rootNote",
                    "position",
                    "reverse",
                    "cutoff",
                    "resonance",
                    "pan",
                    "volume"
                ]
            },
            "envelope": {
                "label": "Envelope",
                "shortcut": "2",
                "controls": [
                    "attack",
                    "decay",
                    "sustain",
                    "release",
                    "cutoff",
                    "resonance",
                    "pan",
                    "volume"
                ]
            },
            "tone": {
                "label": "Tone",
                "shortcut": "3",
                "controls": [
                    "cutoff",
                    "resonance",
                    "tune",
                    "rootNote",
                    "attack",
                    "decay",
                    "pan",
                    "volume"
                ]
            },
            "granular": {
                "label": "Granular",
                "shortcut": "4",
                "controls": [
                    "grainSize",
                    "density",
                    "position",
                    "spray",
                    "scan",
                    "spread",
                    "tune",
                    "volume"
                ]
            },
            "sequence": {
                "label": "Sequence",
                "shortcut": "5",
                "controls": [
                    "tempo",
                    "swing",
                    "stepNote",
                    "stepVelocity",
                    "stepGate",
                    "stepProbability",
                    "octave",
                    "volume"
                ]
            },
            "mix": {
                "label": "Mix",
                "shortcut": "6",
                "controls": [
                    "volume",
                    "pan",
                    "spread",
                    "cutoff",
                    "resonance",
                    "attack",
                    "sustain",
                    "release"
                ]
            }
        },
        "sections": {
            "library": "Searchable built-in/local bank with file import, folder import, and drag/drop.",
            "screen": "Selected sound, waveform, playback mode, tempo, and audio status.",
            "encoders": "Six group buttons (1-6) and eight encoders selected with QWER/ASDF; hold the selector key while moving the mouse vertically to change its value. Releasing the selector or its held group key disarms adjustment immediately. Shift makes finer changes; Escape exits adjustment and silences notes. Pointer drag and native range keyboard adjustment also work.",
            "transport": "Enable audio, play/stop, panic, project save/load, tempo, swing, and clear pattern.",
            "steps": "Collapsible sixteen-step editor with Steps/Piano roll view switch; transport remains outside the fold. Both views edit the same monophonic pattern.",
            "keyboard": "Two octaves of pointer-playable keys. Computer note typing has an explicit Play keys mode, disabled by default to prevent conflicts with QWER/ASDF encoder selection.",
            "midi": "Connect button, input/output selection, output channel, and optional outgoing clock."
        },
        "knob_keys": [
            "q",
            "w",
            "e",
            "r",
            "a",
            "s",
            "d",
            "f"
        ],
        "sound_browser": {
            "navigation": "ArrowUp/ArrowDown in the search field or sound list select the previous/next filtered sound, clamp at either end, focus the row, and keep it visible. Consume these keys locally.",
            "playback": "Decode the latest selection before committing; preserve running transport, playhead, held-note ownership, sustain, and queued MIDI. Existing voices retain their original source; new notes use the selected source. Failed or superseded loads do not replace it."
        },
        "granular_display": {
            "observation_interval_ms": 100,
            "contents": "Real AudioWorklet voice heads, active grain positions and source windows over the waveform, with grain count, size, density, scan, spray, and stereo spread. Label continuing voices from a previous sound; do not draw their windows against the new waveform.",
            "idle": "Show the configured position, source-window length, spray region, and numeric settings with zero active grains; distinguish the preview from playing grains.",
            "invariant": "Read-only snapshots requested outside the render callback; visualization never drives or changes audio."
        },
        "sequence_editor": {
            "default_view": "steps",
            "views": [
                "steps",
                "piano"
            ],
            "collapse": "Hide the pattern and inspector; keep Play, Stop, tempo, swing, and the expand button reachable. Folding and switching views do not change notes, locks, scheduling, or playhead.",
            "long_press_ms": 450,
            "movement_cancel_px": 8,
            "step_gestures": "Tap toggles activation and selects a step; hold opens the inspector without toggling. Cancel on pointer cancellation or movement. Shift+Enter or the Edit step button opens the same inspector.",
            "piano_roll": "Sixteen columns, one note per step. Click a cell to set pitch and enable the step; click an existing note to select it. Drag a note vertically to change pitch; Up/Down changes pitch, Left/Right selects a step, Enter opens the inspector, and Delete/Backspace disables a note. Note duration displays gate; gate, velocity, and chance are editable in the shared inspector. Pitch range follows a selectable octave; scrolling is confined to the roll and its time ruler remains visible.",
            "locks": "Add/edit/remove any unscoped sound parameter in the inspector. Show its label, units, value, and a lock-count indicator on both views. Removing a lock restores patch inheritance for future notes."
        },
        "design": {
            "palette": {
                "background": "#141619",
                "panel": "#292b2d",
                "keys": "#35383b",
                "screen": "#b6d9d6",
                "amber": "#ffba47",
                "red": "#ff6d74",
                "mint": "#80ddbd"
            },
            "typography": "System sans for controls; monospace for musical values and display.",
            "mobile": "Stack library and panel, preserve all controls, and wrap steps into two rows of eight. Allow internal horizontal scrolling in the two-octave playable keyboard and piano roll to retain 24px minimum pointer targets.",
            "accessibility": "Labels, visible focus, pressed states, native range keyboard input, and reduced-motion support."
        },
        "modes": {
            "cycle": "Single cycle",
            "sample": "Sample",
            "granular": "Granular"
        }
    },
    "parameters": {
        "tune": {
            "label": "Tune",
            "min": -24,
            "max": 24,
            "step": 1,
            "default": 0,
            "unit": "st"
        },
        "rootNote": {
            "label": "Root note",
            "min": 24,
            "max": 96,
            "step": 1,
            "default": 60,
            "unit": "note"
        },
        "position": {
            "label": "Position",
            "min": 0,
            "max": 0.99,
            "step": 0.01,
            "default": 0,
            "unit": "percent"
        },
        "reverse": {
            "label": "Reverse",
            "min": 0,
            "max": 1,
            "step": 1,
            "default": 0,
            "unit": "toggle"
        },
        "cutoff": {
            "label": "Cutoff",
            "min": 40,
            "max": 18000,
            "step": 1,
            "default": 12000,
            "unit": "Hz",
            "scale": "log"
        },
        "resonance": {
            "label": "Resonance",
            "min": 0.1,
            "max": 12,
            "step": 0.1,
            "default": 0.7,
            "unit": "Q"
        },
        "pan": {
            "label": "Pan",
            "min": -1,
            "max": 1,
            "step": 0.01,
            "default": 0,
            "unit": "pan"
        },
        "volume": {
            "label": "Level",
            "min": 0,
            "max": 1,
            "step": 0.01,
            "default": 0.65,
            "unit": "percent"
        },
        "attack": {
            "label": "Attack",
            "min": 0.002,
            "max": 4,
            "step": 0.001,
            "default": 0.012,
            "unit": "seconds",
            "scale": "log"
        },
        "decay": {
            "label": "Decay",
            "min": 0.005,
            "max": 4,
            "step": 0.001,
            "default": 0.18,
            "unit": "seconds",
            "scale": "log"
        },
        "sustain": {
            "label": "Sustain",
            "min": 0,
            "max": 1,
            "step": 0.01,
            "default": 0.7,
            "unit": "percent"
        },
        "release": {
            "label": "Release",
            "min": 0.005,
            "max": 8,
            "step": 0.001,
            "default": 0.32,
            "unit": "seconds",
            "scale": "log"
        },
        "grainSize": {
            "label": "Grain size",
            "min": 0.005,
            "max": 0.5,
            "step": 0.001,
            "default": 0.08,
            "unit": "seconds",
            "scale": "log"
        },
        "density": {
            "label": "Density",
            "min": 2,
            "max": 80,
            "step": 1,
            "default": 20,
            "unit": "grains/s"
        },
        "spray": {
            "label": "Spray",
            "min": 0,
            "max": 1,
            "step": 0.01,
            "default": 0.12,
            "unit": "percent"
        },
        "scan": {
            "label": "Scan",
            "min": -1,
            "max": 1,
            "step": 0.01,
            "default": 0,
            "unit": "scan"
        },
        "spread": {
            "label": "Spread",
            "min": 0,
            "max": 1,
            "step": 0.01,
            "default": 0.35,
            "unit": "percent"
        },
        "tempo": {
            "label": "Tempo",
            "min": 30,
            "max": 240,
            "step": 1,
            "default": 110,
            "unit": "BPM",
            "scope": "sequence"
        },
        "swing": {
            "label": "Swing",
            "min": 0,
            "max": 0.6,
            "step": 0.01,
            "default": 0,
            "unit": "percent",
            "scope": "sequence"
        },
        "stepNote": {
            "label": "Step note",
            "min": 0,
            "max": 127,
            "step": 1,
            "default": 48,
            "unit": "note",
            "scope": "step"
        },
        "stepVelocity": {
            "label": "Velocity",
            "min": 1,
            "max": 127,
            "step": 1,
            "default": 96,
            "unit": "velocity",
            "scope": "step"
        },
        "stepGate": {
            "label": "Gate",
            "min": 0.05,
            "max": 1,
            "step": 0.01,
            "default": 0.5,
            "unit": "percent",
            "scope": "step"
        },
        "stepProbability": {
            "label": "Probability",
            "min": 0,
            "max": 1,
            "step": 0.01,
            "default": 1,
            "unit": "percent",
            "scope": "step"
        },
        "octave": {
            "label": "Octave",
            "min": 0,
            "max": 7,
            "step": 1,
            "default": 3,
            "unit": "octave",
            "scope": "keyboard"
        }
    },
    "defaults": {
        "mode": "cycle",
        "bpm": 110,
        "swing": 0,
        "octave": 3,
        "steps": 16,
        "active_steps": [
            0,
            2,
            4,
            6,
            8,
            10,
            12,
            14
        ],
        "notes": [
            48,
            48,
            55,
            48,
            58,
            48,
            55,
            48,
            60,
            48,
            55,
            48,
            58,
            48,
            55,
            48
        ]
    },
    "limits": {
        "voices": 8,
        "import_bytes": 67108864,
        "decoded_frames": 14400000,
        "preset_bytes": 65536,
        "tempo": [
            30,
            240
        ],
        "swing": [
            0,
            0.6
        ],
        "file_extensions": [
            "wav",
            "aif",
            "aiff",
            "flac",
            "mp3",
            "ogg",
            "m4a"
        ]
    },
    "midi": {
        "output_channel": 1,
        "clock_output_default": false,
        "clock_ticks_per_quarter": 24,
        "input_messages": [
            "note_on",
            "note_off",
            "velocity_zero_note_off",
            "sustain",
            "pitch_bend",
            "control_change",
            "all_notes_off"
        ],
        "echo_input": false,
        "controls": {
            "7": "volume",
            "71": "resonance",
            "72": "release",
            "73": "attack",
            "74": "cutoff"
        },
        "computer_keys": [
            "a",
            "w",
            "s",
            "e",
            "d",
            "f",
            "t",
            "g",
            "y",
            "h",
            "u",
            "j",
            "k",
            "o",
            "l",
            "p",
            ";"
        ]
    },
    "actions": {
        "enableAudio": "Resume the audio context only after a user gesture; show a useful error if unavailable.",
        "chooseSound": "Decode before replacing the sound; infer cycle mode for files shorter than 50ms while preserving granular mode. Preserve playback and existing voices throughout browsing; newest requested selection wins.",
        "importSounds": "Validate supported file size/type, retain successfully decoded sounds in IndexedDB, and explain rejected files.",
        "playNote": "Start a pitched voice with velocity and ADSR; support keyboard, pointer, MIDI, and sequencer sources.",
        "releaseNote": "Release only the matching voice; honor sustain pedal and continue grain generation through the ADSR release.",
        "shapeSound": "Clamp values to SSOT bounds, update display/state, and apply live engine parameters.",
        "selectGroup": "Number keys 1-6 or group buttons choose the visible set of eight encoders.",
        "selectControl": "Holding QWER/ASDF selects a knob and arms vertical mouse adjustment only while the selector is held; keyup of that selector or a concurrently held group key, blur, or visibility loss disarms it. Ignore shortcuts in editable fields. Play keys mode routes note keys to the synth instead; key release still clears any previously armed adjustment.",
        "startSequence": "Schedule sixteenth notes on the audio clock with swing, gate, velocity, and probability; Stop/Panic cancels any start waiting for audio permission.",
        "stopSequence": "Cancel future events and silence local/output notes; return playhead to the beginning.",
        "clearPattern": "Disable all steps without altering their note settings.",
        "editStep": "Select a step and expose note, velocity, gate, chance, and parameter locks without toggling it.",
        "lockStepParameter": "Clamp and quantize a sound parameter against its SSOT rule; store it only on the selected step. Snapshot locks when scheduling a note and apply them to that voice only, in every engine mode.",
        "viewPattern": "Collapse/expand the editor or switch Steps/Piano roll without changing musical state or stopping playback.",
        "connectMidi": "Ask browser permission on user gesture, expose available ports, and handle denied/unavailable/disconnected devices.",
        "saveProject": "Validate and save sample reference, mode, parameters, tempo, swing, octave, and sixteen steps in SQLite.",
        "loadProject": "Resolve the sound first, validate the state, then restore atomically; explain unavailable sounds.",
        "panic": "Stop transport, clear queued events and held keys, and send MIDI all-notes-off.",
        "switchVersion": "Stop scheduling, cancel queued MIDI output, silence voices, suspend audio, and finish V2 autosave before navigating. Also silence audio/MIDI on other page exits."
    },
    "flows": {
        "play_library": [
            "open_app",
            "load_first_sound",
            "enable_audio",
            "play_note",
            "release_note"
        ],
        "play_import": [
            "choose_files_or_folder_or_drop",
            "validate_and_decode",
            "store_locally",
            "choose_sound",
            "play_note"
        ],
        "make_pattern": [
            "toggle_steps",
            "edit_selected_step",
            "set_tempo_and_swing",
            "start_sequence",
            "stop_sequence"
        ],
        "browse_while_playing": [
            "start_sequence_or_hold_note",
            "focus_sound_browser",
            "choose_or_arrow_to_sound",
            "decode_candidate",
            "commit_for_new_notes",
            "continue_playback"
        ],
        "shape_one_step": [
            "hold_step_or_use_edit_button",
            "edit_note_gate_velocity_chance",
            "add_parameter_lock",
            "hear_locked_voice",
            "remove_lock_to_inherit_patch"
        ],
        "edit_piano_roll": [
            "switch_to_piano",
            "choose_pitch_range",
            "place_or_select_note",
            "drag_pitch",
            "edit_shared_inspector",
            "switch_back_to_steps"
        ],
        "edit_controls": [
            "select_group",
            "hold_control_selector",
            "move_mouse_vertically",
            "hold_shift_for_fine_adjustment",
            "release_selector_to_exit"
        ],
        "use_midi": [
            "connect_midi",
            "choose_ports_and_channel",
            "play_input_notes",
            "send_keyboard_or_sequence_notes"
        ],
        "restore_project": [
            "save_project",
            "reload_app",
            "choose_saved_project",
            "resolve_sound",
            "restore_state"
        ]
    },
    "apis": {
        "library": {
            "method": "GET",
            "path": "/fun-and-games/instruments/samples/library.json",
            "response": "array of {id, name, bank, url, bytes}"
        },
        "sample": {
            "method": "GET",
            "path": "/fun-and-games/instruments/v1/api/samples/{id}",
            "response": "audio_bytes",
            "failures": [
                404
            ]
        },
        "projects": {
            "method": "GET",
            "path": "/fun-and-games/instruments/v1/api/projects",
            "response": "array of {id, name, updatedAt}"
        },
        "save_project": {
            "method": "POST",
            "path": "/fun-and-games/instruments/v1/api/projects",
            "input": "{name, state}",
            "response": "{id, name, state, updatedAt}",
            "failures": [
                400,
                413,
                422
            ]
        },
        "project": {
            "method": "GET",
            "path": "/fun-and-games/instruments/v1/api/projects/{id}",
            "response": "{id, name, state, updatedAt}",
            "failures": [
                404
            ]
        },
        "health": {
            "method": "GET",
            "path": "/fun-and-games/instruments/v1/api/health",
            "response": "{ok, router, specVersion}"
        }
    },
    "contracts": {
        "AudioEngine": {
            "methods": [
                "enable()",
                "suspend()",
                "loadSound(decodedSound)",
                "setParameters(parameters)",
                "setMode(mode)",
                "playNote(note, velocity, time, duration, locks)",
                "releaseNote(voiceId, time)",
                "setPitchBend(semitones)",
                "panic()",
                "now()",
                "readMeter()",
                "readGrains()"
            ],
            "returns": {
                "playNote": "opaque voiceId",
                "now": "audio seconds",
                "readMeter": "RMS amplitude",
                "readGrains": "latest read-only {time, voices:[{id, soundId, parameters, head, grains:[{position, start, span, phase, reverse}]}]} snapshot; empty before worklet is ready; parameters are the effective patch plus captured locks"
            },
            "invariants": [
                "Eight voices maximum",
                "release matches voice identity across source and mode changes",
                "sound replacement preserves existing voice sources",
                "sequence timing uses audio time",
                "the whole file represents one cycle at the requested note frequency",
                "grain windows taper to zero",
                "granular release remains audible through the selected envelope",
                "panic cancels future notes",
                "locks override only their own voice and never mutate patch parameters"
            ]
        },
        "SoundDecoder": {
            "methods": [
                "decodeSound(bytes)",
                "makeDemo(samples, rate)"
            ],
            "returns": {
                "decodeSound": "decoded AudioBuffer",
                "makeDemo": "decoded AudioBuffer"
            },
            "invariants": [
                "Reject undecodable audio and oversized decoded buffers",
                "decoding does not replace the current playable sound"
            ]
        },
        "SoundLibrary": {
            "methods": [
                "listSounds()",
                "readSound(id)",
                "importSound(file)"
            ],
            "dependencies": [
                "SoundDecoder"
            ],
            "invariants": [
                "Stable identifiers",
                "local imports persist across reloads",
                "decoder errors do not replace playable sounds"
            ]
        },
        "ProjectStore": {
            "methods": [
                "listProjects()",
                "saveProject(name, state)",
                "loadProject(id)"
            ],
            "invariants": [
                "Validate against SSOT before writes",
                "exact sixteen-step patterns",
                "no raw sample bytes in project JSON",
                "each step has an optional locks map of unscoped parameter IDs to finite in-range values",
                "missing locks in older projects mean empty maps",
                "reject unknown or scoped lock IDs and malformed lock maps",
                "locks survive SQLite and browser project save/load"
            ]
        },
        "MidiPort": {
            "methods": [
                "connect()",
                "ports()",
                "selectInput(id)",
                "selectOutput(id)",
                "setChannel(channel)",
                "setClockEnabled(enabled)",
                "sendNote(note, velocity, time, duration)",
                "releaseNote(voiceId, time)",
                "start(time)",
                "sendClock(time)",
                "panic()"
            ],
            "callbacks": {
                "message": "parsed input message with source and channel",
                "ports": "inputs/outputs and selected IDs",
                "panic": "release local voices after input change or disconnection",
                "error": "recoverable MIDI output error"
            },
            "returns": {
                "connect": "port list",
                "ports": "port list",
                "sendNote": "opaque voiceId or null when output is off"
            },
            "invariants": [
                "No automatic echo of incoming notes",
                "channels 1 through 16",
                "timed and held notes share voice ownership",
                "same-pitch output remains held until all overlapping voices release",
                "each emitted note-on has a matching final note-off",
                "clear queued output on stop",
                "disconnection silences held notes",
                "output failures do not strand local voices or prevent local transport"
            ]
        }
    },
    "acceptance": [
        "Root opens V1; /v1 and /v2 select independently usable apps and both show the version switcher.",
        "Version-prefixed assets and V1 APIs resolve correctly; traversal and private source/config paths remain inaccessible.",
        "The configured MG library appears and a selected real WAV plays at the requested MIDI pitch.",
        "Holding/releasing a key produces the selected ADSR, with independent overlapping notes.",
        "Pattern edits, swing, gate, probability, and BPM affect audible sequencing.",
        "Clicking or arrow-browsing filtered sounds preserves transport phase and held notes; failed and racing selections keep the last successful latest source.",
        "Granular display shows actual heads and grains with window lengths and settings; idle preview and old-source voices are identified honestly.",
        "Folding the editor and switching between Steps and Piano roll preserve the same notes, gate, velocity, chance, locks, and running playhead.",
        "Holding a step for 450ms opens its inspector without toggling; canceled gestures do nothing; keyboard users can open it with Shift+Enter or Edit step.",
        "Per-step locks affect only scheduled voices in cycle, sample, and granular modes, preserve the base patch, and survive project save/load; invalid locks are rejected in browser and PHP.",
        "Stop and panic silence future/local/external notes.",
        "Granular mode produces windowed grains with working size, density, position, spray, scan, and spread controls.",
        "Imported audio and saved project state survive reload; missing or invalid files produce recoverable errors.",
        "MIDI input controls voices and CCs; output sends correctly timed notes and optional 24-PPQN clock.",
        "The desktop panel and mobile controls work with keyboard, pointer, and focus navigation.",
        "Keys 1-6 select screens; held QWER/ASDF keys arm mouse adjustment, selector or held-group key release immediately stops it, and Play keys mode prevents conflicting note/parameter shortcuts."
    ],
    "roadmap": [
        "External MIDI clock following, sample slicing, and multiple independent tracks.",
        "Audio recording/resampling, configurable grain pitch jitter, and longer sample modulation.",
        "Evaluate microgranny and Roland P-6 workflows with the user's hardware; do not claim device equivalence."
    ],
    "review_proposals": {
        "performance_editing_2026_10_02": {
            "status": "awaiting_user_review",
            "implementation": "held",
            "origin": "Verified Astrax V1 performance-editing interactions; adapt to each instrument rather than copy its interface or project format.",
            "authority": "These are proposed contracts only. They do not extend active actions or acceptance criteria until the user approves; no V2 or Fischwerke implementation is authorized by this record.",
            "targets": {
                "v2": {
                    "owner": "versions.v2",
                    "fit": "Good fit for the existing groovebox; refine its sample menu, granular display, and p-lock workflow, then add a second pattern view.",
                    "evidence": [
                        "versions/claude/js/ui/menus.js already has sample navigation and assign/preview actions.",
                        "versions/claude/js/app.js already has holdKnob keyup ownership and held/latched step locks.",
                        "versions/claude/js/ui/viz.js already displays real grain points and a read-only note lane.",
                        "versions/claude/js/project.js and sequencer.js own eight tracks and up to sixty-four steps."
                    ],
                    "sound_browser": {
                        "disposition": "refine_existing",
                        "ui": "Focus-scoped Up/Down moves through populated filtered slots, keeps the cursor visible, and assigns the selection to the selected track for subsequently scheduled notes. Provide explicit audition; browsing a running pattern must not trigger extra preview notes.",
                        "flow": "Prepare selected slot, commit only the latest request, preserve transport phase, queued patterns, MIDI clock/output, and held-note ownership. Existing voices keep their immutable source; failed preparation keeps the current source.",
                        "contract": "Preserve slot IDs and the V2 pool; retain any replaced slot data until its last voice finishes. No V1 API or storage dependency."
                    },
                    "grain_display": {
                        "disposition": "extend_existing",
                        "ui": "Add actual source-window widths, direction, head/count activity, scan and spray regions to the existing SRC/GRAN screen. Preserve its compact groovebox design and real points; distinguish idle preview and prior-slot voices.",
                        "contract": "Observe voice/slot identity, source-frame position and span, phase, amplitude, direction, and stereo pan outside the audio render callback. Values reflect captured locks and effective modulation; visualization cannot affect audio."
                    },
                    "sequence_editor": {
                        "disposition": "add_view_and_fold",
                        "ui": "Collapse the pattern editor while leaving transport, track selection, and pattern/page controls available. Toggle pads and an editable piano roll for the selected track/current pattern; show the full track length with explicit page/scroll position.",
                        "contract": "Both views edit the existing sparse steps and locks. Preserve up to sixty-four steps, track lengths, swing, microtiming, probability, conditions, queued patterns, and song chains. Piano duration maps to the existing len lock; do not substitute V1 gate values or project format."
                    },
                    "step_locks": {
                        "disposition": "preserve_and_make_discoverable",
                        "ui": "Keep existing held/latched lock behavior; add an explicit selected-step inspector and keyboard edit action. A hold opens editing without toggling a trigger; cancel movement, pointer cancellation, and blur. Display/remove named locks with units and visible badges in both views.",
                        "contract": "Reuse PARAMS lock eligibility and locksPhys conversions. Never mutate the track patch when editing a step; keep autosave/export/import compatible."
                    },
                    "knob_selectors": {
                        "disposition": "retain_and_verify",
                        "ui": "Keep the existing held QWER/ASDF mouse-adjustment path. Verify selector release, concurrently held group release, blur, and focus changes end mouse adjustment. Preserve explicit wheel/arrow adjustment of a selected knob."
                    },
                    "acceptance_if_approved": [
                        "Browse click/arrow and failed/racing loads preserve sequence phase, owned notes, and queued MIDI without automatic extra audition notes.",
                        "Grain windows and numeric summaries match observed source frames and effective locked/modulated values.",
                        "Fold/view switching changes no musical state; piano edits preserve length, microtiming, probability, conditions, locks, and queued transitions.",
                        "Hold editing, touch note dragging, blank-roll scrolling, keyboard focus, and selector release work at laptop and phone sizes.",
                        "Existing V2 engine/sequencer tests and saved autosave/JSON projects continue to pass."
                    ]
                },
                "fischwerke": {
                    "owner": "instruments.fischwerke",
                    "fit": "Strong fit with the material atlas; browsing and grain telemetry refine existing capabilities. Editable sequencing is a larger addition because the current bench runs a fixed test phrase.",
                    "evidence": [
                        "public/fischwerke/wiring/session.js already prepares assets and atomically configures tracks.",
                        "public/fischwerke/adapters/view.js currently uses a sound select, grain points, latched numeric controls, and a fixed phrase button.",
                        "The active grain_visual contract already calls for source-time widths and stereo pan; the kernel supports captured event locks."
                    ],
                    "sound_browser": {
                        "disposition": "replace_select_with_focused_browser",
                        "ui": "Searchable material browser with focus-scoped Up/Down and a clear selected track/lane/zone destination. Browse without leaving the performance surface; show preparation and recoverable load failures.",
                        "flow": "Prepare and reserve the latest candidate, then assign atomically for future notes. Preserve phrase/pattern position, live-note ownership, sustain, MIDI queues, capture state, and active old-asset voices. Browsing never implicitly starts or stops recording.",
                        "contract": "Keep immutable PCM and existing asset-budget ownership. Snapshot the destination track/lane/zone; prevent stale requests from overwriting a newer selection or another destination."
                    },
                    "grain_display": {
                        "disposition": "complete_existing_visual_contract",
                        "ui": "Extend the atlas points with actual source windows, scan heads, direction, amplitude, pan, grain count/size, and mode-specific rate/division/overlap. Mark prior-asset voices and preview/unknown state honestly; retain reduced-motion numeric activity.",
                        "contract": "Extend observe telemetry with source/track/voice identities and source-frame window metadata outside render. Respect Eco/Studio observation budgets and live-buffer origins; never derive active windows solely from the current patch."
                    },
                    "sequence_editor": {
                        "disposition": "new_pattern_module",
                        "ui": "Replace the fixed phrase bench with an optional editable selected-track pattern, initially sixteen steps, with collapsible pads/Piano roll views. Keep transport, tracks, material and recording status accessible; preserve the atlas layout.",
                        "contract": "Add an injected pattern/scheduling boundary rather than grow the audio adapter. Use one shared step model for both views, with note/lane, velocity, duration, probability and locks. Keep track ownership, Kit lanes, Multi zones, MIDI tracks and cancellation epochs intact.",
                        "persistence": "Specify a versioned optional pattern field in the independent Fischwerke patch schema; migrate older patches to the existing test phrase. Do not read or convert V1/V2 stores."
                    },
                    "step_locks": {
                        "disposition": "expose_existing_engine_capability",
                        "ui": "Hold a step to edit without toggling; provide Edit step and Enter alternatives. Show named, unit-bearing locks and remove-to-inherit controls on the selected track/step.",
                        "contract": "Reuse prevalidated per-voice event locks and machine eligibility. Source locks require preloaded, budgeted assets; scoped routing, recording and other unsupported controls remain ineligible. Capture overrides at scheduling; never change unrelated voices or the base track."
                    },
                    "knob_selectors": {
                        "disposition": "change_keyboard_gesture_after_review",
                        "ui": "Keyboard-selected numeric controls adjust only while QWER/ASDF is held; selector/group key release, blur, or visibility loss ends adjustment. Distinguish explicit pointer editing and keep keyboard value entry available.",
                        "contract": "Replace the current keyboard latch/Enter-accept/Escape-restore gesture only after approval; define whether Escape cancels a held gesture or panics before implementation."
                    },
                    "acceptance_if_approved": [
                        "Focused material browsing preserves sound, phrase/pattern phase, source ownership and capture state on successful, racing, and failed preparations.",
                        "Window, direction and pan drawings match the kernel across Grain/Micro, loop wrapping, live buffers, and different source/device rates.",
                        "Fold/piano/pad views share notes and locks; pattern scheduling preserves track/lane ownership and Stop/Panic epochs.",
                        "Step locks affect only their scheduled voice and survive independent patch save/load; older patches restore the previous phrase.",
                        "Mouse, touch, keyboard, focus, selector release, reduced motion and resource limits pass browser checks before delivery."
                    ]
                }
            }
        }
    },
    "instruments": {
        "fischwerke": {
            "title": "Fischwerke",
            "path": "/fun-and-games/instruments/fischwerke",
            "entry": "public/fischwerke/index.html",
            "assets": {
                "root": "public/fischwerke",
                "vendor": "versions/claude/js/audio",
                "shared": "public/app"
            },
            "stage": "sound_engine",
            "catalog": "public/fischwerke/catalog.js",
            "design": "docs/design/fischwerke/fischwerke.md",
            "delivery": {
                "included": [
                    "sample",
                    "cycle",
                    "grain",
                    "kit",
                    "multi",
                    "tape",
                    "midi",
                    "polyphony",
                    "envelopes",
                    "voice_filters",
                    "voice_modulation",
                    "track_inserts",
                    "buses",
                    "sends",
                    "master",
                    "import",
                    "live_input",
                    "freeze",
                    "midi_notes",
                    "engine_bench",
                    "test_phrase",
                    "local_patch"
                ],
                "not_in_this_engine_task": [
                    "scenes",
                    "songs",
                    "full_pattern_editor",
                    "autosampler",
                    "disk_streaming",
                    "hardware_remote",
                    "effect_lfos"
                ],
                "rule": "Only delivered engine capabilities are enabled; broader design remains a roadmap."
            },
            "resources": {
                "profiles": {
                    "full": {
                        "global_voices": 64,
                        "voices_per_track": 8,
                        "grains_per_voice": 8,
                        "global_grains": 256,
                        "buffer_budget_mib": 256
                    },
                    "eco": {
                        "global_voices": 32,
                        "voices_per_track": 8,
                        "grains_per_voice": 8,
                        "global_grains": 128,
                        "buffer_budget_mib": 128
                    },
                    "mobile": {
                        "global_voices": 16,
                        "voices_per_track": 4,
                        "grains_per_voice": 4,
                        "global_grains": 64,
                        "buffer_budget_mib": 64
                    }
                },
                "limits_measured": false,
                "default_profile": "eco",
                "preferred_sample_rate_hz": 48000,
                "actual_sample_rate": "audio_context_rate",
                "hardcode_render_quantum_length": false,
                "worklet_allocations_during_render": "forbidden",
                "voice_steal": "releasing_first_then_oldest_active_with_5ms_fade",
                "grain_budget": "Skip new grains deterministically when full; expose drop count without changing saved settings.",
                "asset_eviction": "least_recently_used_unpinned_only",
                "immutable_assets_pinned_by": [
                    "active_voices",
                    "current_graph",
                    "pending_scene",
                    "unfinished_capture"
                ],
                "budget_includes": [
                    "decoded_PCM",
                    "DSP_buffers",
                    "capture_working_buffers",
                    "preloads",
                    "active_generation_assets"
                ],
                "budget_reservation_failure": "reject_before_changing_audible_state",
                "memory_example": "60_seconds_stereo_float32_at_48kHz_is_23040000_bytes_before_copies",
                "performance_acceptance": {
                    "reference_environment_required": [
                        "machine",
                        "os",
                        "browser_version",
                        "sample_rate",
                        "buffer_size",
                        "audio_interface"
                    ],
                    "worklet_time_target": "p95_below_80_percent_of_actual_render_quantum",
                    "stress_duration_minutes": 10,
                    "required_metrics": [
                        "underruns",
                        "voice_steals",
                        "grain_drops",
                        "input_latency",
                        "midi_audio_offset",
                        "clock_drift",
                        "peak_memory"
                    ],
                    "numbers_are": "validation_targets_not_measurements"
                }
            },
            "topology": {
                "playable_tracks": 8,
                "buses": 4,
                "send_returns": 3,
                "master": 1,
                "sequenced_track_count": 16,
                "track_ids": [
                    "t1",
                    "t2",
                    "t3",
                    "t4",
                    "t5",
                    "t6",
                    "t7",
                    "t8"
                ],
                "bus_ids": [
                    "b1",
                    "b2",
                    "b3",
                    "b4"
                ],
                "send_ids": [
                    "s1",
                    "s2",
                    "s3"
                ],
                "master_id": "main",
                "kit_lanes_per_track": 8,
                "kit_voice_limit_per_lane": 1,
                "midi_lanes_on_buses": "optional_and_simultaneous",
                "track_insert_slots": 2,
                "bus_insert_slots": 2,
                "return_effect_slots": 1,
                "master_effect_slots": 1,
                "default_output": "one_stereo_browser_destination",
                "alternate_hardware_outputs": "capability_gated_adapter_extension"
            },
            "parameters": {
                "source.tune": {
                    "kind": "number",
                    "unit": "semitones",
                    "min": -60,
                    "max": 60,
                    "step": 0.01,
                    "default": 0,
                    "scale": "linear",
                    "scope": "voice"
                },
                "source.play_mode": {
                    "kind": "enum",
                    "values": [
                        "forward",
                        "reverse",
                        "forward_loop",
                        "reverse_loop"
                    ],
                    "default": "forward",
                    "scope": "voice"
                },
                "source.crossfade": {
                    "kind": "number",
                    "unit": "ms",
                    "min": 0,
                    "max": 100,
                    "default": 5,
                    "scale": "log_with_zero",
                    "scope": "voice"
                },
                "source.asset": {
                    "kind": "asset_reference",
                    "default": null,
                    "scope": "voice",
                    "apply": "preload_then_commit"
                },
                "source.start": {
                    "kind": "number",
                    "unit": "normalized_region",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "voice"
                },
                "source.loop_start": {
                    "kind": "number",
                    "unit": "normalized_region",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "voice"
                },
                "source.loop_end": {
                    "kind": "number",
                    "unit": "normalized_region",
                    "min": 0,
                    "max": 1,
                    "default": 1,
                    "scope": "voice"
                },
                "source.end": {
                    "kind": "number",
                    "unit": "normalized_region",
                    "min": 0,
                    "max": 1,
                    "default": 1,
                    "scope": "voice"
                },
                "source.root": {
                    "kind": "integer",
                    "unit": "midi_note",
                    "min": 0,
                    "max": 127,
                    "default": 60,
                    "scope": "voice"
                },
                "source.loop_mode": {
                    "kind": "enum",
                    "values": [
                        "sustain_then_exit",
                        "continuous_through_release"
                    ],
                    "default": "sustain_then_exit",
                    "scope": "voice"
                },
                "cycle_a.tune": {
                    "kind": "number",
                    "unit": "semitones",
                    "min": -60,
                    "max": 60,
                    "default": 0,
                    "scope": "voice"
                },
                "cycle_a.level": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0.8,
                    "scope": "voice"
                },
                "cycle_a.frame": {
                    "kind": "number",
                    "unit": "normalized_frames",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "voice"
                },
                "cycle_a.bank": {
                    "kind": "asset_reference",
                    "default": null,
                    "scope": "voice"
                },
                "cycle_a.rate": {
                    "kind": "number",
                    "unit": "hz",
                    "min": 0.01,
                    "max": 20,
                    "default": 1,
                    "scale": "logarithmic",
                    "scope": "voice"
                },
                "cycle_a.level_depth": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": -1,
                    "max": 1,
                    "default": 0,
                    "scope": "voice"
                },
                "cycle_a.frame_depth": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": -1,
                    "max": 1,
                    "default": 0,
                    "scope": "voice"
                },
                "cycle_a.shape": {
                    "kind": "enum",
                    "values": [
                        "exp_down_once",
                        "ramp_down_once",
                        "triangle_once",
                        "ramp_up_once",
                        "exp_up_once",
                        "ramp_down",
                        "triangle",
                        "square",
                        "ramp_up",
                        "random",
                        "sample_hold",
                        "bank_wave"
                    ],
                    "default": "triangle",
                    "scope": "voice"
                },
                "cycle_b.tune": {
                    "kind": "number",
                    "unit": "semitones",
                    "min": -60,
                    "max": 60,
                    "default": 0,
                    "scope": "voice"
                },
                "cycle_b.level": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "voice"
                },
                "cycle_b.frame": {
                    "kind": "number",
                    "unit": "normalized_frames",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "voice"
                },
                "cycle_b.bank": {
                    "kind": "asset_reference",
                    "default": null,
                    "scope": "voice"
                },
                "cycle_b.rate": {
                    "kind": "number",
                    "unit": "hz",
                    "min": 0.01,
                    "max": 20,
                    "default": 1,
                    "scale": "logarithmic",
                    "scope": "voice"
                },
                "cycle_b.level_depth": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": -1,
                    "max": 1,
                    "default": 0,
                    "scope": "voice"
                },
                "cycle_b.frame_depth": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": -1,
                    "max": 1,
                    "default": 0,
                    "scope": "voice"
                },
                "cycle_b.shape": {
                    "kind": "enum",
                    "values": [
                        "exp_down_once",
                        "ramp_down_once",
                        "triangle_once",
                        "ramp_up_once",
                        "exp_up_once",
                        "ramp_down",
                        "triangle",
                        "square",
                        "ramp_up",
                        "random",
                        "sample_hold",
                        "bank_wave"
                    ],
                    "default": "triangle",
                    "scope": "voice"
                },
                "cycle_b.detune": {
                    "kind": "number",
                    "unit": "cents",
                    "min": -100,
                    "max": 100,
                    "default": 0,
                    "scope": "voice"
                },
                "multi.instrument": {
                    "kind": "instrument_reference",
                    "default": null,
                    "scope": "voice"
                },
                "multi.vibrato": {
                    "kind": "number",
                    "unit": "cents",
                    "min": 0,
                    "max": 100,
                    "default": 0,
                    "scope": "voice"
                },
                "multi.vibrato_rate": {
                    "kind": "number",
                    "unit": "hz",
                    "min": 0.01,
                    "max": 20,
                    "default": 5,
                    "scale": "logarithmic",
                    "scope": "voice"
                },
                "multi.vibrato_fade": {
                    "kind": "number",
                    "unit": "ms",
                    "min": 0,
                    "max": 10000,
                    "default": 0,
                    "scope": "voice"
                },
                "zone.root": {
                    "kind": "integer",
                    "unit": "midi_note",
                    "min": 0,
                    "max": 127,
                    "default": 60,
                    "scope": "zone"
                },
                "zone.low": {
                    "kind": "integer",
                    "unit": "midi_note",
                    "min": 0,
                    "max": 127,
                    "default": 0,
                    "scope": "zone"
                },
                "zone.high": {
                    "kind": "integer",
                    "unit": "midi_note",
                    "min": 0,
                    "max": 127,
                    "default": 127,
                    "scope": "zone"
                },
                "zone.velocity_low": {
                    "kind": "integer",
                    "unit": "midi_velocity",
                    "min": 1,
                    "max": 127,
                    "default": 1,
                    "scope": "zone"
                },
                "zone.velocity_high": {
                    "kind": "integer",
                    "unit": "midi_velocity",
                    "min": 1,
                    "max": 127,
                    "default": 127,
                    "scope": "zone"
                },
                "tape.speed": {
                    "kind": "number",
                    "unit": "ratio",
                    "min": -4,
                    "max": 4,
                    "default": 1,
                    "scope": "track"
                },
                "tape.length": {
                    "kind": "number",
                    "unit": "seconds",
                    "min": 0.01,
                    "max": 30,
                    "default": 4,
                    "scale": "logarithmic",
                    "scope": "track"
                },
                "tape.rotate": {
                    "kind": "number",
                    "unit": "normalized_buffer",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "track"
                },
                "tape.input_blend": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "track"
                },
                "tape.crossfade": {
                    "kind": "number",
                    "unit": "ms",
                    "min": 0,
                    "max": 100,
                    "default": 5,
                    "scope": "track"
                },
                "tape.level": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0.8,
                    "scope": "track"
                },
                "tape.retention": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 1,
                    "scope": "track"
                },
                "grain.mode": {
                    "kind": "enum",
                    "values": [
                        "random",
                        "clocked",
                        "overlap"
                    ],
                    "default": "overlap",
                    "scope": "voice"
                },
                "grain.rate": {
                    "kind": "number",
                    "unit": "grains_per_second",
                    "min": 0.5,
                    "max": 100,
                    "default": 20,
                    "scale": "logarithmic",
                    "scope": "voice"
                },
                "grain.division": {
                    "kind": "enum",
                    "values": [
                        "1/1",
                        "1/2",
                        "1/4",
                        "1/8",
                        "1/12",
                        "1/16",
                        "1/24",
                        "1/32",
                        "1/64"
                    ],
                    "default": "1/16",
                    "scope": "voice"
                },
                "grain.overlap": {
                    "kind": "number",
                    "unit": "fraction",
                    "min": 0,
                    "max": 0.95,
                    "default": 0.5,
                    "scope": "voice"
                },
                "grain.size": {
                    "kind": "number",
                    "unit": "ms",
                    "min": 10,
                    "max": 2000,
                    "default": 80,
                    "scale": "logarithmic",
                    "scope": "voice"
                },
                "grain.position": {
                    "kind": "number",
                    "unit": "normalized_region",
                    "min": 0,
                    "max": 1,
                    "default": 0.5,
                    "scope": "voice"
                },
                "grain.scan": {
                    "kind": "number",
                    "unit": "ratio",
                    "min": -4,
                    "max": 4,
                    "default": 0,
                    "scope": "voice"
                },
                "grain.spread": {
                    "kind": "number",
                    "unit": "normalized_region",
                    "min": 0,
                    "max": 1,
                    "default": 0.1,
                    "scope": "voice"
                },
                "grain.pan_spread": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0.3,
                    "scope": "voice"
                },
                "grain.limit": {
                    "kind": "integer",
                    "unit": "simultaneous_grains_per_voice",
                    "min": 1,
                    "max": 8,
                    "default": 4,
                    "scope": "voice"
                },
                "grain.reverse": {
                    "kind": "number",
                    "unit": "probability",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "voice"
                },
                "grain.window_fade": {
                    "kind": "number",
                    "unit": "fraction",
                    "min": 0,
                    "max": 1,
                    "default": 1,
                    "scope": "voice"
                },
                "grain.window_shape": {
                    "kind": "number",
                    "unit": "attack_fraction",
                    "min": 0,
                    "max": 1,
                    "default": 0.5,
                    "scope": "voice"
                },
                "grain.timing_jitter": {
                    "kind": "number",
                    "unit": "fraction_of_interval",
                    "min": 0,
                    "max": 0.49,
                    "default": 0,
                    "scope": "voice"
                },
                "grain.detune": {
                    "kind": "number",
                    "unit": "cents",
                    "min": 0,
                    "max": 100,
                    "default": 0,
                    "scope": "voice"
                },
                "grain.level_jitter": {
                    "kind": "number",
                    "unit": "fraction",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "voice"
                },
                "grain.start_mode": {
                    "kind": "enum",
                    "values": [
                        "cold",
                        "hot"
                    ],
                    "default": "cold",
                    "scope": "voice"
                },
                "grain.time_key_follow": {
                    "kind": "number",
                    "unit": "fraction",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "voice"
                },
                "filter.cutoff": {
                    "kind": "number",
                    "unit": "hz",
                    "min": 20,
                    "max": 20000,
                    "default": 12000,
                    "scale": "logarithmic",
                    "scope": "voice"
                },
                "filter.resonance": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0.1,
                    "scope": "voice"
                },
                "filter.mode": {
                    "kind": "enum",
                    "values": [
                        "off",
                        "low_pass_2",
                        "low_pass_4",
                        "band_pass",
                        "high_pass",
                        "notch"
                    ],
                    "default": "low_pass_2",
                    "scope": "voice"
                },
                "filter.envelope_depth": {
                    "kind": "number",
                    "unit": "octaves",
                    "min": -8,
                    "max": 8,
                    "default": 0,
                    "scope": "voice"
                },
                "filter.low_cut": {
                    "kind": "number",
                    "unit": "hz",
                    "min": 20,
                    "max": 20000,
                    "default": 20,
                    "scale": "logarithmic",
                    "scope": "voice"
                },
                "filter.high_cut": {
                    "kind": "number",
                    "unit": "hz",
                    "min": 20,
                    "max": 20000,
                    "default": 20000,
                    "scale": "logarithmic",
                    "scope": "voice"
                },
                "filter.key_follow": {
                    "kind": "number",
                    "unit": "fraction",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "voice"
                },
                "filter.drive": {
                    "kind": "number",
                    "unit": "dB",
                    "min": 0,
                    "max": 24,
                    "default": 0,
                    "scope": "voice"
                },
                "amp.attack": {
                    "kind": "number",
                    "unit": "ms",
                    "min": 0,
                    "max": 30000,
                    "default": 5,
                    "scale": "log_with_zero",
                    "scope": "voice"
                },
                "amp.decay": {
                    "kind": "number",
                    "unit": "ms",
                    "min": 0,
                    "max": 30000,
                    "default": 100,
                    "scale": "log_with_zero",
                    "scope": "voice"
                },
                "amp.sustain": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0.8,
                    "scope": "voice"
                },
                "amp.release": {
                    "kind": "number",
                    "unit": "ms",
                    "min": 0,
                    "max": 30000,
                    "default": 150,
                    "scale": "log_with_zero",
                    "scope": "voice"
                },
                "amp.level": {
                    "kind": "number",
                    "unit": "dB",
                    "min": -60,
                    "max": 6,
                    "default": -12,
                    "scope": "voice"
                },
                "amp.pan": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": -1,
                    "max": 1,
                    "default": 0,
                    "scope": "voice"
                },
                "amp.mode": {
                    "kind": "enum",
                    "values": [
                        "adsr",
                        "ahd",
                        "adr",
                        "cyclic"
                    ],
                    "default": "adsr",
                    "scope": "voice"
                },
                "amp.velocity": {
                    "kind": "enum",
                    "values": [
                        "off",
                        "linear",
                        "logarithmic",
                        "exponential"
                    ],
                    "default": "linear",
                    "scope": "voice"
                },
                "amp.hold": {
                    "kind": "number",
                    "unit": "ms",
                    "min": 0,
                    "max": 30000,
                    "default": 0,
                    "scale": "log_with_zero",
                    "scope": "voice"
                },
                "amp.reset": {
                    "kind": "boolean",
                    "default": false,
                    "scope": "voice"
                },
                "voice.mode": {
                    "kind": "enum",
                    "values": [
                        "poly",
                        "mono",
                        "mono_legato"
                    ],
                    "default": "poly",
                    "scope": "track"
                },
                "voice.priority": {
                    "kind": "enum",
                    "values": [
                        "last",
                        "low",
                        "high"
                    ],
                    "default": "last",
                    "scope": "track"
                },
                "mod.attack": {
                    "kind": "number",
                    "unit": "ms",
                    "min": 0,
                    "max": 30000,
                    "default": 5,
                    "scale": "log_with_zero",
                    "scope": "modulation_source"
                },
                "mod.decay": {
                    "kind": "number",
                    "unit": "ms",
                    "min": 0,
                    "max": 30000,
                    "default": 100,
                    "scale": "log_with_zero",
                    "scope": "modulation_source"
                },
                "mod.sustain": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "modulation_source"
                },
                "mod.release": {
                    "kind": "number",
                    "unit": "ms",
                    "min": 0,
                    "max": 30000,
                    "default": 150,
                    "scale": "log_with_zero",
                    "scope": "modulation_source"
                },
                "mod.delay": {
                    "kind": "number",
                    "unit": "ms",
                    "min": 0,
                    "max": 30000,
                    "default": 0,
                    "scale": "log_with_zero",
                    "scope": "modulation_source"
                },
                "mod.reset": {
                    "kind": "boolean",
                    "default": true,
                    "scope": "modulation_source"
                },
                "mod.destination": {
                    "kind": "parameter_reference",
                    "default": null,
                    "scope": "modulation_source",
                    "validation": "supported_parameter_and_no_dependency_cycle"
                },
                "mod.depth": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": -1,
                    "max": 1,
                    "default": 0,
                    "scope": "modulation_source"
                },
                "motion.rate": {
                    "kind": "number",
                    "unit": "hz",
                    "min": -20,
                    "max": 20,
                    "default": 1,
                    "scale": "signed_log",
                    "scope": "modulation_source"
                },
                "motion.amount": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": -1,
                    "max": 1,
                    "default": 0,
                    "scope": "modulation_source"
                },
                "motion.destination": {
                    "kind": "parameter_reference",
                    "default": null,
                    "scope": "modulation_source",
                    "validation": "supported_parameter_and_no_dependency_cycle"
                },
                "motion.wave": {
                    "kind": "enum",
                    "values": [
                        "sine",
                        "triangle",
                        "square",
                        "saw",
                        "ramp",
                        "exponential",
                        "random",
                        "sample_hold"
                    ],
                    "default": "sine",
                    "scope": "modulation_source"
                },
                "motion.phase": {
                    "kind": "number",
                    "unit": "normalized_cycle",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "modulation_source"
                },
                "motion.slew": {
                    "kind": "number",
                    "unit": "ms",
                    "min": 0,
                    "max": 5000,
                    "default": 0,
                    "scope": "modulation_source"
                },
                "motion.trigger": {
                    "kind": "enum",
                    "values": [
                        "free",
                        "retrigger",
                        "hold",
                        "once",
                        "half"
                    ],
                    "default": "free",
                    "scope": "modulation_source"
                },
                "motion.fade": {
                    "kind": "number",
                    "unit": "seconds",
                    "min": -30,
                    "max": 30,
                    "default": 0,
                    "scope": "modulation_source"
                },
                "event.note": {
                    "kind": "integer",
                    "unit": "midi_note",
                    "min": 0,
                    "max": 127,
                    "default": 60,
                    "scope": "event"
                },
                "event.velocity": {
                    "kind": "integer",
                    "unit": "midi_velocity",
                    "min": 1,
                    "max": 127,
                    "default": 100,
                    "scope": "event"
                },
                "event.gate": {
                    "kind": "number",
                    "unit": "steps",
                    "min": 0.01,
                    "max": 256,
                    "default": 1,
                    "scope": "event"
                },
                "event.probability": {
                    "kind": "number",
                    "unit": "probability",
                    "min": 0,
                    "max": 1,
                    "default": 1,
                    "scope": "event"
                },
                "event.microtiming": {
                    "kind": "number",
                    "unit": "steps",
                    "min": -0.49,
                    "max": 0.49,
                    "default": 0,
                    "scope": "event"
                },
                "event.retrigs": {
                    "kind": "integer",
                    "unit": "count",
                    "min": 1,
                    "max": 16,
                    "default": 1,
                    "scope": "event"
                },
                "event.retrig_fade": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": -1,
                    "max": 1,
                    "default": 0,
                    "scope": "event"
                },
                "event.condition": {
                    "kind": "condition_reference",
                    "default": "always",
                    "scope": "event"
                },
                "track.length": {
                    "kind": "integer",
                    "unit": "steps",
                    "min": 1,
                    "max": 256,
                    "default": 16,
                    "scope": "sequence"
                },
                "track.speed": {
                    "kind": "enum",
                    "values": [
                        "1/8",
                        "1/4",
                        "1/2",
                        "3/4",
                        1,
                        "3/2",
                        2
                    ],
                    "default": 1,
                    "scope": "sequence"
                },
                "track.swing": {
                    "kind": "number",
                    "unit": "delayed_pair_fraction",
                    "min": 0,
                    "max": 0.49,
                    "default": 0,
                    "scope": "sequence"
                },
                "track.quantize": {
                    "kind": "number",
                    "unit": "strength",
                    "min": 0,
                    "max": 1,
                    "default": 1,
                    "scope": "sequence"
                },
                "arp.mode": {
                    "kind": "enum",
                    "values": [
                        "off",
                        "played_order",
                        "up",
                        "down",
                        "up_down",
                        "random"
                    ],
                    "default": "off",
                    "scope": "sequence"
                },
                "arp.rate": {
                    "kind": "enum",
                    "values": [
                        "1/4",
                        "1/8",
                        "1/12",
                        "1/16",
                        "1/24",
                        "1/32"
                    ],
                    "default": "1/16",
                    "scope": "sequence"
                },
                "arp.range": {
                    "kind": "integer",
                    "unit": "octaves",
                    "min": 1,
                    "max": 4,
                    "default": 1,
                    "scope": "sequence"
                },
                "arp.gate": {
                    "kind": "number",
                    "unit": "fraction_of_interval",
                    "min": 0.01,
                    "max": 1,
                    "default": 0.8,
                    "scope": "sequence"
                },
                "macro.1": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "project_macro"
                },
                "macro.2": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "project_macro"
                },
                "macro.3": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "project_macro"
                },
                "macro.4": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "project_macro"
                },
                "macro.5": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "project_macro"
                },
                "macro.6": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "project_macro"
                },
                "macro.7": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "project_macro"
                },
                "macro.8": {
                    "kind": "number",
                    "unit": "normalized",
                    "min": 0,
                    "max": 1,
                    "default": 0,
                    "scope": "project_macro"
                }
            },
            "parameter_rules": {
                "source_region": "start_less_than_end_and_loop_start_less_than_loop_end_inside_region",
                "zone_interval": "note_low_less_than_or_equal_to_high_and_velocity_low_less_than_or_equal_to_high",
                "filter_band": "low_cut_less_than_or_equal_to_high_cut_and_below_actual_nyquist",
                "scope_validation": "Reject parameter on unsupported machine, selected lane or slot.",
                "default_scale": "linear",
                "log_with_zero": "Zero is a separate endpoint; positive interval starts at the parameter's DSP minimum.",
                "signed_log": "Signed continuous scale with exact zero; adapter defines versioned positive minimum.",
                "resolve_order": [
                    "committed_scene",
                    "temporary_edits",
                    "matching_step_lock",
                    "relative_macro_offsets",
                    "modulation",
                    "clamp_and_DSP_conversion"
                ],
                "modulation_domain": "normalized_declared_parameter_domain",
                "voice_lock": "capture_at_note_on_for_voice_lifetime",
                "track_lock": "persist_until_next_lock_or_track_loop_reset",
                "clip_or_warn": "Clamp modulation only; reject invalid persisted or explicit action values.",
                "enums": "No continuous interpolation; event lock or committed scene endpoint selects value.",
                "provenance_display": [
                    "base",
                    "temporary",
                    "step_lock",
                    "macro",
                    "modulation",
                    "effective"
                ]
            },
            "envelopes": {
                "adsr": "Attack to one; decay to sustain; hold while gated; note off releases from current level to zero.",
                "ahd": "Attack to one; hold for amp.hold; decay to zero without sustain; note off releases early from current level.",
                "adr": "Attack to one; decay toward zero while gated; note off enters release from current level; no sustain plateau.",
                "cyclic": "Repeat attack and decay between zero and one while gated; note off exits into release from current level.",
                "zero_stage": "Advance immediately without division by zero; adapter smooths the final amplitude over its declared minimum ramp.",
                "reset_false": "Retrigger from current level; reset true starts at zero with a click-safe ramp.",
                "mono_legato": "Reuse voice and preserve envelope for overlapping held notes; first note retriggers.",
                "modulation_envelope": "Delayed ADSR in declared normalized destination domain; separate from amp and grain windows."
            },
            "machines": {
                "sample": {
                    "tier": "core",
                    "sources": [
                        "immutable_mono_asset",
                        "immutable_stereo_asset"
                    ],
                    "pages": [
                        "sample_material",
                        "tone",
                        "envelope"
                    ],
                    "root_note_required": true,
                    "loop_mode": [
                        "sustain_then_exit",
                        "continuous_through_release"
                    ],
                    "region_edit": "nondestructive_frame_ranges"
                },
                "cycle": {
                    "tier": "core",
                    "sources": [
                        "single_cycle",
                        "wavetable"
                    ],
                    "pages": [
                        "cycle_a",
                        "cycle_b",
                        "tone",
                        "envelope"
                    ],
                    "oscillators": 2,
                    "frames_per_bank": [
                        1,
                        64
                    ],
                    "import_frame_lengths": [
                        64,
                        128,
                        256,
                        512,
                        1024,
                        2048,
                        4096
                    ],
                    "arbitrary_single_cycle": "Resample into a declared frame length; retain original asset and exact import metadata.",
                    "phase_increment": "requested_hz_divided_by_actual_sample_rate",
                    "anti_aliasing": "worker_generated_band_limited_tables_with_level_and_boundary_audition",
                    "animation": "per_oscillator_retriggerable_or_free",
                    "wavetable_builder": "Reuse the pinned V2 pure Fourier/mip builder behind AssetStore; no mutable V2 engine/session is imported."
                },
                "multi": {
                    "tier": "extended",
                    "sources": [
                        "zone_instrument"
                    ],
                    "pages": [
                        "multi_material",
                        "tone",
                        "envelope"
                    ],
                    "zone_fields": [
                        "asset_id",
                        "root_note",
                        "note_low",
                        "note_high",
                        "velocity_low",
                        "velocity_high",
                        "gain_db",
                        "region"
                    ],
                    "overlap_policy": "explicit_crossfade_or_priority_never_implicit",
                    "validation": "Every zone references a real asset and valid nonempty note/velocity interval."
                },
                "kit": {
                    "tier": "core",
                    "sources": [
                        "eight_lane_assets"
                    ],
                    "pages": [
                        "kit_material",
                        "tone",
                        "envelope"
                    ],
                    "per_lane": [
                        "source",
                        "filters",
                        "envelope",
                        "voice_lfos",
                        "sequence"
                    ],
                    "shared": [
                        "track_inserts",
                        "sends",
                        "fx_lfos",
                        "route"
                    ],
                    "shared_sequence_label": "Shared effects",
                    "choke_groups": [
                        "none",
                        "a",
                        "b"
                    ],
                    "choke_release_ms": 5
                },
                "grain": {
                    "tier": "core",
                    "live_source_tier": "extended",
                    "sources": [
                        "immutable_mono_asset",
                        "immutable_stereo_asset",
                        "rolling_live_buffer",
                        "frozen_asset"
                    ],
                    "pages": [
                        "grain_material",
                        "grain_scan",
                        "grain_character",
                        "tone",
                        "envelope"
                    ],
                    "parameter_overrides": {
                        "source.tune": {
                            "min": -24,
                            "max": 24
                        }
                    },
                    "scheduler": {
                        "random": "Seeded exponentially distributed intervals with mean 1/rate; jitter affects interval without negative time.",
                        "clocked": "Grain launch times follow declared beat division; optional bounded timing jitter.",
                        "overlap": "Next launch after nominal_output_grain_duration_times_one_minus_overlap."
                    },
                    "size_definition": "Source window duration at unity pitch before time-key-follow compensation.",
                    "time_key_follow": {
                        "zero": "Adjust source window for pitch so output grain duration stays fixed.",
                        "one": "Keep source window frames fixed so output duration changes with pitch.",
                        "intermediate": "interpolate_in_log_duration_domain"
                    },
                    "position_bounds": "selected_region",
                    "short_region": "Wrap grain reads within the selected region when the requested grain window exceeds available source frames; never hold an endpoint as DC.",
                    "scan_wrap": "region_start_to_end_in_signed_direction",
                    "spread_sampling": "seeded_uniform_offset_clamped_to_valid_source_window",
                    "reverse_sampling": "seeded_bernoulli_per_grain",
                    "jitter_sampling": "independent_seeded_streams_for_timing_pitch_level_and_pan",
                    "cold_start": "start_one_grain_then_fill_at_scheduler_intervals",
                    "hot_start": "fill_limit_at_note_on_with_deterministic_phase_offsets",
                    "micro_mode": {
                        "enabled_default": false,
                        "size_ms": [
                            1,
                            10
                        ],
                        "parameter_overrides": {
                            "grain.size": {
                                "min": 1,
                                "max": 10
                            }
                        },
                        "on_enter": "Remember regular size; restore last micro size or use 4ms in one undoable transaction.",
                        "on_exit": "Restore remembered regular size; never leave an out-of-range base value.",
                        "deliberate_clicks": "explicit_window_fade_zero"
                    },
                    "live_buffer_seconds": 4,
                    "freeze": "Copy or seal the current window into a reserved immutable asset before replacing the source reference."
                },
                "tape": {
                    "tier": "extended",
                    "sources": [
                        "live_input",
                        "immutable_asset"
                    ],
                    "pages": [
                        "tape_material",
                        "tone",
                        "envelope"
                    ],
                    "buffer_seconds": 30,
                    "speed_zero": "hold_read_head",
                    "speed_and_pitch": "varispeed_coupled",
                    "overdub": "new_input_plus_previous_buffer_times_retention_with_bounded_output",
                    "recording_start_stop": "quantizable",
                    "freeze": "Seal a versioned asset; never reuse its mutable backing store."
                },
                "midi": {
                    "tier": "core",
                    "sources": [
                        "external_target"
                    ],
                    "pages": [
                        "midi_target",
                        "midi_cc_1",
                        "midi_cc_2"
                    ],
                    "internal_source_audio": false,
                    "external_audio_into_inserts": "explicit_route",
                    "max_notes_per_event": 16,
                    "assignable_cc_lanes": 16,
                    "note_off_velocity": 0,
                    "echoed_input_default": false
                }
            },
            "ui": {
                "direction": "material_atlas",
                "drawing": "docs/design/fischwerke/fischwerke.svg",
                "palette": {
                    "paper": "#F1EFDE",
                    "ink": "#17201D",
                    "selected": "#244BD8",
                    "temporary": "#DCEB34",
                    "error": "#A83622"
                },
                "font_policy": "system_sans_labels_and_monospace_values_no_network_fonts",
                "regions": [
                    "project_transport",
                    "track_strip",
                    "material_canvas",
                    "context_rail",
                    "keyed_controls",
                    "sequence_strip",
                    "scene_rail"
                ],
                "views": [
                    "material",
                    "routes"
                ],
                "view_switch_changes_sound": false,
                "persistent_indicators": [
                    "project",
                    "audible_pattern",
                    "pending_transition",
                    "selected_track",
                    "clock_owner",
                    "capture_state",
                    "temporary_state",
                    "output_meter"
                ],
                "grain_visual": {
                    "x": "actual_source_time",
                    "y": "stereo_pan",
                    "width": "source_window",
                    "opacity": "amplitude",
                    "arrow": "direction",
                    "truthful_label": "source_time_not_playback_timeline",
                    "reduced_motion": "freeze_animation_but_keep_numeric_activity"
                },
                "selectors": [
                    "q",
                    "w",
                    "e",
                    "r",
                    "a",
                    "s",
                    "d",
                    "f"
                ],
                "groups": [
                    {
                        "id": "material",
                        "key": "1",
                        "label": "Material",
                        "pages": [
                            "sample_material",
                            "cycle_a",
                            "cycle_b",
                            "multi_material",
                            "kit_material",
                            "grain_material",
                            "tape_material",
                            "midi_target",
                            "midi_cc_1",
                            "midi_cc_2"
                        ]
                    },
                    {
                        "id": "grains",
                        "key": "2",
                        "label": "Grains",
                        "pages": [
                            "grain_scan",
                            "grain_character"
                        ]
                    },
                    {
                        "id": "tone",
                        "key": "3",
                        "label": "Tone",
                        "pages": [
                            "tone"
                        ]
                    },
                    {
                        "id": "shape",
                        "key": "4",
                        "label": "Shape",
                        "pages": [
                            "envelope",
                            "motion",
                            "mod_envelope"
                        ]
                    }
                ],
                "page_policy": "Filter by selected machine/target; repeated group press cycles named subpages; remember per machine.",
                "pages": {
                    "sample_material": {
                        "machines": [
                            "sample"
                        ],
                        "controls": [
                            "source.tune",
                            "source.play_mode",
                            "source.crossfade",
                            "source.asset",
                            "source.start",
                            "source.loop_start",
                            "source.loop_end",
                            "source.end"
                        ]
                    },
                    "cycle_a": {
                        "machines": [
                            "cycle"
                        ],
                        "controls": [
                            "cycle_a.tune",
                            "cycle_a.level",
                            "cycle_a.frame",
                            "cycle_a.bank",
                            "cycle_a.rate",
                            "cycle_a.level_depth",
                            "cycle_a.frame_depth",
                            "cycle_a.shape"
                        ]
                    },
                    "cycle_b": {
                        "machines": [
                            "cycle"
                        ],
                        "controls": [
                            "cycle_b.tune",
                            "cycle_b.level",
                            "cycle_b.frame",
                            "cycle_b.bank",
                            "cycle_b.rate",
                            "cycle_b.level_depth",
                            "cycle_b.frame_depth",
                            "cycle_b.shape"
                        ]
                    },
                    "multi_material": {
                        "machines": [
                            "multi"
                        ],
                        "controls": [
                            "source.tune",
                            "multi.instrument",
                            "multi.vibrato",
                            "multi.vibrato_rate",
                            "multi.vibrato_fade",
                            "zone.root",
                            "zone.low",
                            "zone.high"
                        ]
                    },
                    "kit_material": {
                        "machines": [
                            "kit"
                        ],
                        "context": "selected_lane",
                        "controls": [
                            "source.tune",
                            "source.play_mode",
                            "source.crossfade",
                            "source.asset",
                            "source.start",
                            "source.loop_start",
                            "source.loop_end",
                            "source.end"
                        ]
                    },
                    "grain_material": {
                        "machines": [
                            "grain"
                        ],
                        "controls": [
                            "source.tune",
                            "source.root",
                            "grain.time_key_follow",
                            "source.asset",
                            "source.start",
                            "source.end",
                            "grain.mode",
                            "grain.start_mode"
                        ]
                    },
                    "tape_material": {
                        "machines": [
                            "tape"
                        ],
                        "controls": [
                            "tape.speed",
                            "tape.length",
                            "tape.rotate",
                            "source.asset",
                            "tape.input_blend",
                            "tape.crossfade",
                            "tape.level",
                            "tape.retention"
                        ]
                    },
                    "grain_scan": {
                        "machines": [
                            "grain"
                        ],
                        "controls": [
                            "source.tune",
                            "grain.overlap",
                            "grain.size",
                            "source.asset",
                            "grain.position",
                            "grain.scan",
                            "grain.spread",
                            "grain.pan_spread"
                        ],
                        "slot_w_by_mode": {
                            "random": "grain.rate",
                            "clocked": "grain.division",
                            "overlap": "grain.overlap"
                        },
                        "mode_selector": "context_rail"
                    },
                    "grain_character": {
                        "machines": [
                            "grain"
                        ],
                        "controls": [
                            "grain.limit",
                            "grain.reverse",
                            "grain.window_fade",
                            "grain.window_shape",
                            "grain.timing_jitter",
                            "grain.detune",
                            "grain.level_jitter",
                            "grain.start_mode"
                        ]
                    },
                    "tone": {
                        "machines": [
                            "sample",
                            "cycle",
                            "multi",
                            "kit",
                            "grain",
                            "tape"
                        ],
                        "controls": [
                            "filter.cutoff",
                            "filter.resonance",
                            "filter.mode",
                            "filter.envelope_depth",
                            "filter.low_cut",
                            "filter.high_cut",
                            "filter.key_follow",
                            "filter.drive"
                        ]
                    },
                    "envelope": {
                        "machines": [
                            "sample",
                            "cycle",
                            "multi",
                            "kit",
                            "grain",
                            "tape"
                        ],
                        "controls": [
                            "amp.attack",
                            "amp.decay",
                            "amp.sustain",
                            "amp.release",
                            "amp.level",
                            "amp.pan",
                            "amp.mode",
                            "amp.velocity"
                        ]
                    },
                    "motion": {
                        "context": "selected_voice_or_fx_lfo",
                        "controls": [
                            "motion.rate",
                            "motion.amount",
                            "motion.destination",
                            "motion.wave",
                            "motion.phase",
                            "motion.slew",
                            "motion.trigger",
                            "motion.fade"
                        ]
                    },
                    "mod_envelope": {
                        "context": "selected_voice_mod_envelope",
                        "controls": [
                            "mod.attack",
                            "mod.decay",
                            "mod.sustain",
                            "mod.release",
                            "mod.delay",
                            "mod.reset",
                            "mod.destination",
                            "mod.depth"
                        ]
                    },
                    "event": {
                        "context": "selected_event",
                        "controls": [
                            "event.note",
                            "event.velocity",
                            "event.gate",
                            "event.probability",
                            "event.microtiming",
                            "event.retrigs",
                            "event.retrig_fade",
                            "event.condition"
                        ]
                    },
                    "track": {
                        "context": "selected_sequence_lane",
                        "controls": [
                            "track.length",
                            "track.speed",
                            "track.swing",
                            "track.quantize",
                            "arp.mode",
                            "arp.rate",
                            "arp.range",
                            "arp.gate"
                        ]
                    },
                    "macros": {
                        "context": "project",
                        "controls": [
                            "macro.1",
                            "macro.2",
                            "macro.3",
                            "macro.4",
                            "macro.5",
                            "macro.6",
                            "macro.7",
                            "macro.8"
                        ]
                    },
                    "midi_target": {
                        "machines": [
                            "midi"
                        ],
                        "editor_contract": "MidiTargetEditor",
                        "selectors": [
                            "port",
                            "channel",
                            "bank_msb",
                            "bank_lsb",
                            "program",
                            "bend_range",
                            "mod_wheel",
                            "breath"
                        ]
                    },
                    "midi_cc_1": {
                        "machines": [
                            "midi"
                        ],
                        "editor_contract": "MidiCcEditor",
                        "lane_indices": [
                            1,
                            2,
                            3,
                            4,
                            5,
                            6,
                            7,
                            8
                        ]
                    },
                    "midi_cc_2": {
                        "machines": [
                            "midi"
                        ],
                        "editor_contract": "MidiCcEditor",
                        "lane_indices": [
                            9,
                            10,
                            11,
                            12,
                            13,
                            14,
                            15,
                            16
                        ]
                    }
                },
                "context_editors": {
                    "always_available": [
                        "library",
                        "machine",
                        "pattern",
                        "track",
                        "scene"
                    ],
                    "source_detail": [
                        "source.root",
                        "source.loop_mode",
                        "region_frames",
                        "bank_frame_size",
                        "cycle_interpolation",
                        "cycle_b.detune"
                    ],
                    "grain_detail": [
                        "grain.mode",
                        "grain.time_key_follow",
                        "micro_mode",
                        "live_or_static",
                        "freeze",
                        "keyboard_address_mode"
                    ],
                    "multi_detail": [
                        "zone.velocity_low",
                        "zone.velocity_high",
                        "overlap_policy",
                        "remove_zone",
                        "audition_zone"
                    ],
                    "amp_detail": [
                        "amp.hold",
                        "amp.reset",
                        "voice.mode",
                        "voice.priority"
                    ],
                    "modulation_detail": [
                        "voice_lfo_1",
                        "voice_lfo_2",
                        "mod_envelope",
                        "destination_depth"
                    ],
                    "effect_detail": [
                        "slot_catalog",
                        "slot_parameters",
                        "swap_inserts"
                    ],
                    "routing_detail": [
                        "destination",
                        "pre_post_fader_send",
                        "record_tap"
                    ]
                },
                "breakpoints_css_px": {
                    "wide": 1100,
                    "tablet": 700
                },
                "mobile": "One track; selected material; labelled upper/lower four-control rows; sequence page navigation.",
                "minimum_target_css_px": 44,
                "accessibility": [
                    "literal_state_labels",
                    "visible_focus",
                    "semantic_DOM_controls",
                    "numeric_equivalent_for_graphics",
                    "reduced_motion",
                    "zoom_200_percent"
                ],
                "asset_editor_contract": {
                    "region_frames": "Four integers within decoded frame count; preserve ordering and derive normalized values without changing audio data.",
                    "bank_frame_size": "One of the Cycle import lengths; conversion creates a new immutable asset rather than mutating an existing bank.",
                    "cycle_interpolation": "Explicit boolean asset metadata; false selects nearest frame while table samples still use declared band-limited interpolation.",
                    "original_asset": "Always retained with conversion history and source hash."
                }
            },
            "gesture": {
                "select": "tap_key_or_click_selects_and_arms_without_value_change",
                "movement": "upward_increases_downward_decreases",
                "continuous_full_sweep_css_px": 240,
                "scale": "declared_parameter_scale",
                "fine_key": "Shift",
                "fine_multiplier": 0.1,
                "commit": [
                    "click",
                    "Enter"
                ],
                "cancel": "Escape_restores_pre_gesture_state",
                "disarm": [
                    "group_change",
                    "view_change",
                    "Tab",
                    "window_blur"
                ],
                "text_input_shortcuts": "disabled",
                "pointer_lock": "not_required",
                "computer_keyboard_play_mode": {
                    "toggle": "K",
                    "default": false,
                    "indicator": "Computer keyboard plays notes",
                    "parameter_letter_keys_while_playing": "disabled",
                    "on_mode_exit": "release_all_computer_keyboard_owned_notes"
                },
                "transport_shortcut": "Space_unless_text_input",
                "panic_shortcut": "Shift+Escape",
                "pin": "protect_from_scene_and_random_changes",
                "learn": "explicit_separate_channel_scoped_action"
            },
            "persistence": {
                "database": "ferenc.fischwerke.engine.v1",
                "patch_key": "ferenc.fischwerke.engine.patch.v1",
                "stores": [
                    "assets"
                ],
                "save": "Validate and persist engine configuration and assets independently of V1/V2; storage failures preserve playable state."
            },
            "apis": {
                "library": {
                    "method": "GET",
                    "path": "/fun-and-games/instruments/samples/library.json"
                },
                "sample": {
                    "method": "GET",
                    "path": "/fun-and-games/instruments/samples/{id}.wav"
                },
                "other_server_writes": "not_exposed_in_engine_delivery"
            },
            "routes": {
                "graph_kind": "directed_acyclic_normal_audio_graph",
                "allowed_edges": [
                    "track_to_bus",
                    "track_to_master",
                    "track_to_send",
                    "bus_to_master",
                    "bus_to_send",
                    "send_to_master",
                    "external_to_track_inserts",
                    "external_to_bus",
                    "external_to_master",
                    "source_to_recorder_tap"
                ],
                "denied_edges": [
                    "bus_to_itself",
                    "send_to_itself",
                    "arbitrary_backward_feedback",
                    "recorder_write_to_same_live_read_graph"
                ],
                "effect_internal_feedback": "declared_bounded_DSP_only",
                "pre_post_send_choice": "explicit",
                "graph_layout_move": "visual_only",
                "cable_edit": "validates_and_prepares_then_commits_at_safe_boundary",
                "invalid_edit": "Preserve previous graph and show invalid connection reason.",
                "external_input_to_inserts": "skips_voice_source_filter_and_amp_unless_explicit_processor_module_inserted"
            },
            "dsp": {
                "control_stride": 32,
                "minimum_ramp_ms": 1,
                "steal_fade_ms": 5,
                "panic_fade_ms": 5,
                "graph_crossfade_ms": 20,
                "event_queue_limit": 4096,
                "observe_interval_ms": 100,
                "input_channels": 2,
                "wave_table_size": 2048,
                "wave_table_peak": 0.9,
                "default_cycle_frames": 1,
                "max_cycle_frames": 64,
                "tape_buffer_seconds": 30,
                "grain_live_seconds": 4,
                "granular_seed": 1967,
                "default_bpm": 118,
                "output_headroom_db": -12,
                "default_effects": [
                    "bypass",
                    "bypass"
                ],
                "input_monitoring_default": false,
                "send_wet_default": 1,
                "render_allocations": "No explicit JS object/array allocations in render(); configure/load/observe messages may allocate outside process()."
            },
            "events": {
                "types": [
                    "note_on",
                    "note_off",
                    "parameter",
                    "bend",
                    "stop",
                    "panic"
                ],
                "fields": [
                    "id",
                    "track",
                    "note",
                    "velocity",
                    "frame",
                    "duration_frames",
                    "epoch",
                    "locks",
                    "lane"
                ],
                "frame": "Integer absolute render frame; audio adapter maps seconds using actual context rate.",
                "epoch": "Cancellation clears pending events and releases only owned voices; Panic clears effects/live writers too.",
                "queue_overflow": "Reject whole schedule transaction; never accept a partial set.",
                "parameter_events": "Prevalidated numeric or categorical voice and modulation controls only; asset and track transactions are prepared outside render.",
                "locks": "Validate before queueing; capture overrides per voice; source references must be preloaded.",
                "time_order": "Frame then stable insertion order; note-on before its own note-off."
            },
            "modulation": {
                "voice_lfos": 2,
                "mod_envelopes": 1,
                "domain": "Normalized declared parameter domain; additive then bounded; categorical/asset destinations rejected.",
                "control_rate_frames": 32,
                "destinations": "Supported numeric voice parameters only; pitch/pan/filter/grain controls remain independent.",
                "tempo": "Clocked grains and the test phrase use the same committed BPM.",
                "live_changes": "Un-locked voice controls follow track changes with smoothing; locked controls stay captured until release."
            },
            "bench": {
                "phrase_steps": 16,
                "phrase_notes": [
                    48,
                    55,
                    60,
                    55,
                    48,
                    58,
                    60,
                    67,
                    48,
                    55,
                    60,
                    55,
                    48,
                    58,
                    67,
                    60
                ],
                "step_division": 4,
                "gate": 0.6,
                "lookahead_seconds": 0.1,
                "poll_ms": 20,
                "keyboard_notes": [
                    48,
                    50,
                    52,
                    53,
                    55,
                    57,
                    59,
                    60,
                    62,
                    64,
                    65,
                    67
                ],
                "keyboard_keys": [
                    "z",
                    "x",
                    "c",
                    "v",
                    "b",
                    "n",
                    "m",
                    "comma",
                    "period",
                    "slash",
                    "bracketleft",
                    "bracketright"
                ],
                "default_track": 0,
                "initial_machine": "cycle",
                "initial_sound": "demo:sine",
                "groups": "Use four delivered musical groups; broader Time/Stage workflows remain in the design.",
                "graph_editor": "Typed route and insert selectors; source/map view observes the actual engine.",
                "shortcuts": "1\u20134 groups; QWER/ASDF tap to arm; mouse up/down; Enter commit; Escape revert; K explicit play mode; no shortcuts in text fields."
            },
            "contracts": {
                "AudioEngine": {
                    "methods": [
                        "enable()",
                        "loadAsset(asset)",
                        "configureTrack(track, state)",
                        "configureGraph(graph)",
                        "configureState(tracks, graph)",
                        "setTempo(bpm)",
                        "schedule(events)",
                        "change(track, changes)",
                        "release(id, time)",
                        "bend(track, semitones)",
                        "cancel(epoch)",
                        "stop(panic)",
                        "observe()",
                        "freeze(track)",
                        "connectInput(stream)",
                        "now()",
                        "suspend()"
                    ],
                    "invariants": [
                        "Reject invalid state before changing the audible engine",
                        "Keep active assets until their voices finish",
                        "Enforce selected resource profile",
                        "Render uses actual sample rate and block length",
                        "Each owned note releases independently",
                        "Panic invalidates scheduled starts even while enable() is pending"
                    ]
                },
                "AssetStore": {
                    "methods": [
                        "list()",
                        "read(id)",
                        "import(file, options)",
                        "keep(asset)",
                        "get(id)"
                    ],
                    "invariants": [
                        "Independent browser namespace",
                        "Never upload imported bytes implicitly",
                        "Keep originals and versioned cycle recipe",
                        "Reject memory exhaustion before replacing sound"
                    ]
                },
                "MidiLink": {
                    "methods": [
                        "connect()",
                        "ports()",
                        "chooseInput(id)",
                        "chooseOutput(id)",
                        "play(note, velocity, channel, time, duration)",
                        "release(id, time)",
                        "panic()"
                    ],
                    "invariants": [
                        "No input echo",
                        "Channels one through sixteen",
                        "Clear future output before panic",
                        "Panic only owned channels",
                        "Same-pitch notes retain ownership until final release"
                    ]
                },
                "EngineSession": {
                    "methods": [
                        "initialize()",
                        "playNote(note, velocity, owner)",
                        "releaseNote(owner)",
                        "shapeSound(id, value)",
                        "chooseMachine(machine)",
                        "chooseSound(id, options)",
                        "stopSound(panic)",
                        "savePatch()",
                        "loadPatch(state)"
                    ],
                    "invariants": [
                        "Intent knows no browser protocols",
                        "Injected adapters are interchangeable",
                        "Failed preparation preserves current track",
                        "Never touch V1/V2 state"
                    ]
                }
            },
            "actions": {
                "enableAudio": "User gesture initializes AudioWorklet; error leaves retry available.",
                "playNote": "Validate owner/note/velocity; bind voice to track; honor sustain and mono priority.",
                "releaseNote": "Release only this owner; scheduled releases cannot silence another same-pitch voice.",
                "shapeSound": "Validate against generated bounds and machine scope; apply live or captured modulation settings. Changing outer region boundaries atomically clamps loop boundaries into the remaining nonempty region.",
                "chooseSound": "Prepare immutable PCM/band-limited bank off render thread, reserve budget, then assign atomically.",
                "chooseMachine": "Prepare required source/buffer before applying; preserve current sound on failure.",
                "freezeSound": "Copy live source outside render callback; keep immutable asset durably before displaying frozen state.",
                "connectRoute": "Reject cycles/unsupported routes; prepare typed graph and crossfade at boundary.",
                "stopSound": "Cancel future events and notes; stop live writing; Panic also clears tails.",
                "savePatch": "Save validated engine state in independent namespace; offer file export when storage fails."
            },
            "flows": {
                "play": [
                    "open_fischwerke",
                    "enableAudio",
                    "chooseSound",
                    "chooseMachine",
                    "playNote",
                    "releaseNote"
                ],
                "grain": [
                    "chooseSound",
                    "select_grain",
                    "play_chord",
                    "shapeSound",
                    "releaseNote"
                ],
                "live": [
                    "enableAudio",
                    "connect_input",
                    "choose_tape_or_live_grain",
                    "arm_input",
                    "playNote",
                    "freezeSound",
                    "keep_asset"
                ],
                "phrase": [
                    "set_tempo",
                    "start_test_phrase",
                    "change_sound",
                    "stopSound"
                ],
                "restore": [
                    "savePatch",
                    "reload",
                    "read_assets",
                    "loadPatch",
                    "playNote"
                ]
            },
            "acceptance": [
                "Fischwerke entry/assets/library resolve under /fischwerke/ while V1/V2 still pass all route checks.",
                "Single cycles render 440Hz at MIDI 69 across 44100/48000/96000Hz; frame interpolation does not change pitch and mip levels suppress out-of-band harmonics.",
                "Sample boundaries, reverse, looping, crossfade and sustain-exit behavior preserve source bytes.",
                "Granular pitch and scan are independent; seed yields repeatable audio; all three scheduler modes and grain windows behave distinctly; budgets never overflow.",
                "ADSR/AHD/ADR/cyclic envelopes and zero stages release correctly; overlapping same-pitch owners and stolen voices do not strand notes.",
                "Kit lanes are independent and mono; Multi zones respect key/velocity bounds and explicit overlap policy; Tape handles signed/zero speed and live overdub/freeze.",
                "Two voice LFOs and modulation envelope change declared normalized destinations with bounded effective values.",
                "Inserts/buses/sends route real stereo audio; invalid graphs preserve old routing; effects remain finite and panic clears tails.",
                "Stop/Panic cancel queued epochs including starts waiting on enable; MIDI queue clear and owned-channel cleanup survive errors.",
                "MG/imported sound plays; independent local assets/patch survive reload; failed load/save leaves playable state and a recovery message.",
                "Browser AudioWorklet runs the same kernel tested offline; keyboard, selected controls, responsive layout and imports work; physical MIDI remains separately measured."
            ],
            "effects": {
                "bypass": {
                    "slots": [
                        "track",
                        "bus",
                        "send",
                        "master"
                    ],
                    "parameters": [],
                    "algorithm": "unity"
                },
                "filter": {
                    "slots": [
                        "track",
                        "bus",
                        "master"
                    ],
                    "parameters": {
                        "mode": {
                            "kind": "enum",
                            "values": [
                                "low_pass",
                                "high_pass",
                                "band_pass",
                                "notch"
                            ],
                            "default": "low_pass"
                        },
                        "cutoff_hz": {
                            "min": 20,
                            "max": 20000,
                            "default": 12000,
                            "scale": "logarithmic"
                        },
                        "resonance": {
                            "min": 0,
                            "max": 0.98,
                            "default": 0.1
                        },
                        "wet": {
                            "min": 0,
                            "max": 1,
                            "default": 1
                        }
                    },
                    "algorithm": "stable_TPT_state_variable"
                },
                "drive": {
                    "slots": [
                        "track",
                        "bus",
                        "master"
                    ],
                    "parameters": {
                        "gain_db": {
                            "min": 0,
                            "max": 36,
                            "default": 6
                        },
                        "tone_hz": {
                            "min": 20,
                            "max": 20000,
                            "default": 12000
                        },
                        "wet": {
                            "min": 0,
                            "max": 1,
                            "default": 1
                        }
                    },
                    "algorithm": "tanh_with_lowpass_tone"
                },
                "crush": {
                    "slots": [
                        "track",
                        "bus",
                        "master"
                    ],
                    "parameters": {
                        "bits": {
                            "min": 2,
                            "max": 16,
                            "default": 8,
                            "kind": "integer"
                        },
                        "rate_ratio": {
                            "min": 0.01,
                            "max": 1,
                            "default": 0.5
                        },
                        "wet": {
                            "min": 0,
                            "max": 1,
                            "default": 1
                        }
                    },
                    "algorithm": "symmetric_quantizer_and_sample_hold"
                },
                "compressor": {
                    "slots": [
                        "track",
                        "bus",
                        "send",
                        "master"
                    ],
                    "parameters": {
                        "threshold_db": {
                            "min": -60,
                            "max": 0,
                            "default": -18
                        },
                        "ratio": {
                            "min": 1,
                            "max": 20,
                            "default": 4
                        },
                        "attack_ms": {
                            "min": 0.1,
                            "max": 200,
                            "default": 10
                        },
                        "release_ms": {
                            "min": 5,
                            "max": 3000,
                            "default": 150
                        },
                        "makeup_db": {
                            "min": 0,
                            "max": 24,
                            "default": 0
                        }
                    },
                    "algorithm": "linked_stereo_peak_detector"
                },
                "chorus": {
                    "slots": [
                        "track",
                        "bus",
                        "send",
                        "master"
                    ],
                    "parameters": {
                        "rate_hz": {
                            "min": 0.02,
                            "max": 8,
                            "default": 0.5
                        },
                        "depth_ms": {
                            "min": 0,
                            "max": 15,
                            "default": 5
                        },
                        "feedback": {
                            "min": -0.95,
                            "max": 0.95,
                            "default": 0
                        },
                        "wet": {
                            "min": 0,
                            "max": 1,
                            "default": 0.3
                        }
                    },
                    "algorithm": "20ms_base_dual_phase_modulated_delay"
                },
                "stereo_delay": {
                    "slots": [
                        "track",
                        "bus",
                        "send",
                        "master"
                    ],
                    "parameters": {
                        "time_ms": {
                            "min": 1,
                            "max": 4000,
                            "default": 375
                        },
                        "feedback": {
                            "min": 0,
                            "max": 0.98,
                            "default": 0.4
                        },
                        "damping_hz": {
                            "min": 20,
                            "max": 20000,
                            "default": 8000
                        },
                        "stereo_mode": {
                            "kind": "enum",
                            "values": [
                                "stereo",
                                "ping_pong",
                                "mono"
                            ],
                            "default": "ping_pong"
                        },
                        "wet": {
                            "min": 0,
                            "max": 1,
                            "default": 0.3
                        }
                    },
                    "algorithm": "bounded_stereo_feedback_delay"
                },
                "room_reverb": {
                    "slots": [
                        "bus",
                        "send",
                        "master"
                    ],
                    "parameters": {
                        "decay_seconds": {
                            "min": 0.1,
                            "max": 20,
                            "default": 2
                        },
                        "damping_hz": {
                            "min": 20,
                            "max": 20000,
                            "default": 8000
                        },
                        "predelay_ms": {
                            "min": 0,
                            "max": 250,
                            "default": 0
                        },
                        "width": {
                            "min": 0,
                            "max": 1,
                            "default": 1
                        },
                        "wet": {
                            "min": 0,
                            "max": 1,
                            "default": 0.3
                        }
                    },
                    "algorithm": "four_parallel_damped_combs_and_two_allpasses_per_channel"
                }
            },
            "catalogHash": "d2f699e6ae622d32671dd1976df72d45240c1d81ebda20c4b3fbdc1c8a7063e0"
        }
    },
    "sourceHash": "d99ce42d0e1021250452bb2a66f751e9b86dec28af7c180ad215586c21fb5cba",
    "deployment": {
        "projects": "browser"
    },
    "activeVersion": "v1",
    "assetBasePath": "/fun-and-games/instruments/v1"
}