Skip to content

Latest commit

 

History

History

adr

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

Architecture decision records

Each record captures one decision, the situation that forced it, and what it costs. Format is Michael Nygard's: context, decision, consequences.

Index

Scope and shape

# Decision
0001 Target .NET 10 and current C#
0002 Split the engine from the UI shell
0003 Render on the CPU, leave a GPU backend possible (user-directed)
0004 Author patches in a visual node editor (user-directed)
0065 A text language, which parses to a patch (user-directed; the pipe rule amended by 0144, the panel added by 0145, loops by 0208)
0067 A module keeps its name, and its memory, across a rebuild (user-directed)
0068 The file that was opened decides who owns the patch (user-directed)
0119 The website shows a module by photographing one (user-directed)
0123 A third program plays a patch and writes nothing (user-directed)
0144 A pipe lands where the text says (user-directed)
0145 The panel is written in the text (user-directed)
0208 The text has loops, unrolled as it is built (user-directed)
0160 A patch plays in a browser on the engine compiled to WebAssembly (user-directed)
0162 The editor runs in a browser, with the picture on a canvas of its own (user-directed; a few settings given back by 0182)
0163 A build draws every preset's still once, for every program to show (user-directed)
0166 flyback-cli shot draws the editor's window with no screen (user-directed)
0183 The binder is one walk, and what it asks for is handed nothing back (user-directed)
0187 The plate scrolls with the reading and folds into a pinned header (user-directed; supersedes 0122's pinned plate and right-hand name)
0184 The editor runs on Android, with its plugins linked in (user-directed)
0211 The editor builds for iOS as it does for Android (user-directed; its simulator run made daily by 0212)
0189 The architecture is a C4 model in Structurizr DSL (user-directed; superseded by 0204)
0204 The C4 views are Mermaid flowcharts (user-directed)

The engine

# Decision
0005 Compile patches to a flat register machine
0006 Scalar interpreter parallelised over rows, not SIMD
0007 Values are register slots; scalars broadcast to colors
0008 Modules are data in a single catalogue
0009 Every input port carries an editable default (color ports and Output's left exempted by 0084; values moved to the tooltip on compact modules by 0149)
0010 Any-typed ports make maths modules polymorphic
0011 Compile backwards from the Output node (unread ops inside a reached module swept by 0096)
0012 Feedback is an explicit module, not a graph cycle (cycles superseded by 0075)
0013 Guard arithmetic instead of propagating NaN
0014 Coordinate and value conventions
0021 Recompile the whole patch on every edit
0031 A sequencer is eight inputs and no memory (user-directed)
0032 The registers are double precision
0035 A GLSL backend for the video path (time carried in two floats by 0137; Windows moved off ANGLE by 0155; export moved to the GPU by 0157)
0137 The clock reaches the GPU in two floats (user-directed)
0155 Windows draws through native OpenGL (user-directed)
0157 flyback-cli render draws on the GPU (user-directed)
0214 The picture draws through Metal on Apple's machines (user-directed)
0215 A picture is smoothed fast live and best on export (user-directed)
0216 A Mac's installed plugins live outside the bundle
0167 The shader reads a table as a float texture (user-directed)
0040 A probe is a second compile root, not a second machine (user-directed; what a sweep shares amended by 0143)
0043 A Scan is a Probe read backwards (user-directed; its Output knob superseded by 0077)
0056 A patch can be played, and what plays it is one opcode (user-directed)
0048 Time is seconds, and nothing else (user-directed)
0050 Normalled sockets carry a signal with no wire (user-directed)
0051 A quantiser's scale is a set on the node (user-directed)
0052 A patch names its samples rather than carrying them (user-directed; a built preset carries its own by 0151; drawn on the GPU by 0167)
0059 A picture comes in as a texture (user-directed)
0053 A Scope records what the speakers played (user-directed)
0058 The picture is told how loud the sound is (user-directed)
0073 An Analyzer is a Scope filled with a spectrum (user-directed)
0203 A Plot draws a pitch function as a curve in the picture (user-directed)
0213 A Raymarch is its scene written out once a step (user-directed)
0181 A Beam draws two taps against each other on phosphor (user-directed)
0185 A drawing is played by a plugin as three tables gone round once a cycle (user-directed)
0054 What a module carries is a part, not a subtype (user-directed)
0055 A plugin's extra declares its editor (user-directed; a fourth shape, text, added by 0104 and given several lines by 0105)
0061 What a module carries is kept in one store (user-directed)
0062 MIDI input is polyphonic through indexed voices (user-directed; a voice can belong to one channel, and a busy voice keeps what it held, by amendment)
0099 The computer keyboard can be laid out by scale, and the patch says how (user-directed)
0086 Panel knobs are read as live values, and a MIDI controller turns a knob rather than a socket (user-directed; a learn walks the panel and keeps a drum machine's channel by amendment)
0170 The knob panel randomizes, and a held knob sits it out (user-directed)
0209 A module rolls its own knobs, as an undo step (user-directed)
0205 A panel knob can be played as a switch or a button (user-directed)
0139 A patch keeps to an instrument's clock (user-directed)
0179 A MIDI file plays as tables read by the clock (user-directed)
0140 An instrument is known by a profile file (user-directed)
0057 A shape is a distance, and one module inks it (user-directed)
0105 Text is a shape, baked into a picture (user-directed)
0095 A module may be a handful of others, if it is exactly them (user-directed; the Bell it left out counted again and added by 0097; its "the engine's own presets may not need a plugin" reused for six primitives by 0128)
0097 A wrapping module carries a setting where what it wraps differed (user-directed)
0130 An Auto remap reads its ranges off its wires (user-directed)
0104 A formula is one block, and exactly the modules it names (user-directed)
0106 A sum in the text is one Expression (user-directed; its printing of every Expression as a call replaced by 0107)
0107 An Expression is printed as the sum it is (user-directed; a sum with a pipeline for an operand made a call by 0108)
0108 A preset arrives with its arithmetic folded into Expressions (user-directed; what it leaves alone narrowed by 0109)
0109 The Expression stands for the small Maths modules (user-directed)
0064 A pixel runs only what a pixel changes
0074 A cell is a plane on the video path (user-directed)
0075 A cycle carries its own delay (user-directed)
0126 A bus is a wire with no cable (user-directed)
0174 A polyphonic wire is lowered once per voice (user-directed)
0076 The processor runs a program as IL once it is built (user-directed)
0096 An op nothing reads is left out (user-directed)
0143 A module a sweep reads is lowered once wherever it reads the same
0077 The picture is heard only through a Scan (user-directed; its aspect table's shell-export row retired by 0078; its Live-engine row amended by 0083)
0083 The live engine's aspect follows the preview (user-directed)
0084 A socket with nothing to dial gets no knob (user-directed)

The shell

# Decision
0015 Avalonia for the UI shell
0016 Build the UI in C#, without XAML
0017 Draw the node editor in one custom control
0018 Never render frames on the UI thread
0039 One window class, across a file per region (superseded by 0148)
0044 Lay patches out in layers, not with springs (user-directed; what becomes of a drawing too large for the canvas settled by 0092; runnable over a selection by 0110)
0045 What is copied is a patch file (user-directed)
0046 The module list is a gesture, not a panel (user-directed; a left-drag pan offered as a setting by 0182)
0182 Drag to pan is a setting that trades the left and right buttons (user-directed)
0070 A preset declares no coordinates (user-directed)
0071 Two undo stacks, and which one a press lands on
0078 Export leaves the shell for the CLI that already writes it (user-directed; its Output-panel Record… row moved to the toolbar by 0080; playing without the editor is 0123)
0080 Record moves to the toolbar, with a glyph and Ctrl+R (user-directed; its Output-panel Rewind row moved to the toolbar by 0081; its press given a count-in by 0090)
0081 Rewind moves to the toolbar, beside Record (user-directed; every take does it first by 0090)
0164 Volume is on the toolbar too (user-directed)
0082 The Output settings move to the settings window, and are kept (user-directed; its next-launch latency made immediate by 0085; kept in one file with every other setting by 0171)
0171 Every setting is kept in one file (user-directed)
0087 The assistant moves to a column, beside the patch (user-directed)
0090 A take is counted in, and starts at zero (user-directed; its fixed three seconds and unconditional rewind made settings by 0091)
0091 How a take begins is two settings (user-directed)
0092 A drawing too wide for the canvas shuts a box, and one that cannot fit moves nothing (user-directed)
0110 The layout can be given the selection instead of the patch (user-directed)
0093 A startup preset is a Graphics setting (user-directed; its row moved to Files by an amendment)
0103 Unsaved work outlives a crash (user-directed)
0111 The panel's actions are a row of glyphs (user-directed)
0116 A module is drawn as its category, and a standout as itself (user-directed)
0117 A module switched off is a wire (user-directed)
0118 A plugin paints its own module's background (user-directed)
0121 The window is left as it was left (user-directed)
0129 Full screen on another monitor is a window of its own (user-directed)
0122 The panel wears the block it is about (user-directed; the face it borrows is 0116)
0124 What two shells draw with is a project of its own (user-directed)
0147 A socket says what it is for, in words the panel and the assistant share (user-directed)
0148 The window is its hubs and the regions around them (user-directed; the layout and the keys handed to EditorView by 0162)
0150 The editor and the viewer are each composed in a container (user-directed)
0194 The editor's folders stand on a footing that names no feature (user-directed)
0149 Compact modules share rows and put their values in the tooltip (user-directed)

Sound

# Decision
0022 Audio and video are two sinks over one patch
0023 Oversample the audio path rather than band-limiting modules (2× and a setting by 0168)
0168 The sound is oversampled 2× by default, and the factor is a setting (user-directed)
0024 Sample generation in the engine, the audio device in the shell
0178 A Line In hears the microphone while the sound plays (user-directed)
0027 Delay lines give the audio path a memory (user-directed)
0029 Linux sound through ALSA, on a thread of our own
0190 Linux sound goes through jackd when one is running
0191 The Sound tab picks the backend where more than one can play
0192 Windows sound plays through ASIO when it is picked (user-directed)
0030 Oscillators accumulate their phase on the audio path
0036 Export video as Motion JPEG in an AVI (user-directed; demoted to the fallback by 0089)
0089 ffmpeg encodes what it can, and the AVI is the fallback (user-directed)
0154 An MP3 sample is read by ffmpeg (user-directed)
0049 Record the GPU frame, not the interpreter (user-directed)
0038 A sequencer's notes are a list on the node (user-directed)
0153 An Arrangement is a grid of parts on the node (user-directed)
0037 One Output block, which every patch has (user-directed; its gain socket renamed volume by 0079; its picture settings moved to the settings window by 0082)
0041 A plugin can hold state without a new opcode (user-directed; the filter itself, and five more like it, moved into the engine by 0128)
0042 The clock and the memory flag belong to the emitter
0079 The gain knob becomes Volume, and nought is off (user-directed)
0100 Mastering is a plugin of stateful primitives (user-directed)
0101 A one-knob maximizer is a module of its own (user-directed)
0125 A Duck is a sidechain with the depth on a knob (user-directed)
0128 Filter, Random, Slew, Drive, Delay and Reverb are the engine's own (user-directed)
0142 Figures is three modules that are each a picture and a sound (user-directed)
0146 Fractals is three modules about one point c (user-directed)
0152 Easy is a synth and a drum that cannot be set wrong (user-directed)

Boundaries

# Decision
0019 No third-party dependencies in the engine (read as being about dependencies rather than about programs by 0089)
0020 JSON patch files keyed by string type IDs
0060 A bundle is a patch and what it names (user-directed)
0131 Shared presets live on a site that only reads its media (user-directed)
0136 A letter goes to the author through the preset site (user-directed)
0138 The preset site starts with presets kept as files (user-directed)
0151 A built preset carries its files in its assembly (user-directed)
0141 The preset site starts with a plugin its build packs and the release key signs (user-directed)
0175 The preset site is a Worker, and GitHub reads what is submitted (user-directed)
0176 The preset site lives at flybackmodular.app alone (user-directed)
0177 The .NET site is gone (user-directed)
0025 Platform I/O behind plugins loaded at run time
0026 Modules may come from plugins, and the file records which (user-directed)
0028 Publish one platform at a time, with only that platform's plugins
0063 One plugin per platform, carrying its sound and its MIDI (user-directed)
0102 A plugin is compiled against a contract with a version of its own (user-directed)
0132 A plugin package says what it is, and installs only when asked (user-directed)
0134 A plugin declares its modules, and is refused for one it did not (user-directed)
0133 A shared plugin is unpublished until the admin publishes it (user-directed)
0135 A patch that names a plugin you do not have offers it (user-directed)
0158 A plugin loads only once somebody said yes, and never holds a key (user-directed)
0033 Patches may be authored by an agent, behind the plugin boundary (user-directed; its all-or-nothing prose budget replaced by 0098)
0034 Settings in a file, the key in the operating system's store (user-directed; the file made the one every setting shares by 0171)
0088 A release installs itself at the next start, if its signature says it is ours (user-directed)
0127 The person chooses what opens a Flyback file (user-directed)
0120 Every change passes the gate a release passes
0193 The pages are checked in a headless Chromium the gate carries
0195 CI runs on GitHub's runners on every OS; the self-hosted ones are by hand (user-directed; superseded by 0212)
0198 A release ships the Android editor as an APK signed with the release key (user-directed)
0197 Dependabot's updates arrive as one pull request and merge when Build passes (user-directed)
0199 The Microsoft Store gets an MSIX the Store signs and updates (user-directed)
0200 macOS runs by hand and before a release, not on every push (user-directed; superseded by 0212)
0212 macOS runs on every push; the device runs and the measuring run daily; no self-hosted runners (user-directed)
0196 A tutorial step is a patch the editor checks against
0094 A run says what it played, and nothing about who played it (user-directed; two more events and a wait at the end added by 0103)
0103 A run says how it ended, in bands (user-directed; the modules picked and more of the editor added 2026-09-28)
0047 The agent gets an ear, which is a second model (user-directed)
0066 A second wire format, so one model can hear what it built (user-directed)
0069 An assistant declares its own settings (user-directed; its field vocabulary shared with every plugin by 0085)
0085 A sound backend declares its own settings (user-directed; the backend itself picked by 0191)
0072 A conversation is saved with the patch it is about (user-directed)
0098 The briefing has a budget, and a list that outranks it (user-directed; its default raised to 100,000 by 0101)
0113 The workbench does not limit how large a patch is (user-directed)
0115 The assistant may read the presets (user-directed)
0156 A conversation follows its patch while the modules and wires hold (user-directed)
0159 A turn pays only for what is new (user-directed)
0180 A conversation is bounded by how large it has grown (user-directed)
0161 A turn is the host's, and a provider writes only its format (user-directed)
0169 The host's side of a conversation is a project of its own (user-directed)
0172 Claude Code is an assistant by running the program the person signed in (user-directed; superseded by 0201)
0173 Codex is an assistant the same way, and Gemini is not (user-directed)
0201 Claude Code is not an assistant until Anthropic says it may be (user-directed)
0202 A coding assistant the person runs edits the open patch over MCP (user-directed; its tools changed by 0210)
0210 The assistant edits the patch's text (user-directed)
0186 A decision model answers typed questions behind the plugin boundary (user-directed)
0188 What every host reads is a project of its own (user-directed)
0206 The gate has a ten-minute time budget (user-directed)
0207 Mutation testing plants bugs in each commit's diff (user-directed)