Spec version 0.1, status: draft. This is the normative description of the AMPLE source language (.amplet) and of the data files around it. If a page elsewhere in ample/docs/ disagrees with this folder, this folder wins and the other page is fixed.
This spec is maintained. Every change to the language, diagnostics, lint rules, block templates or CLI must update ../../spec/ (the data), the page here, a conformance case in ../../spec-tests/, an example and CHANGELOG.md, in the same change. See 99-maintenance.md.
Which syntax? The syntax specified here is the mock syntax of the clickable sketch (../sketch/index.html): block headers (drums beats stable@1.2 at 1 for 16 {), parameters, from x.out -> in lines. The older example songs in ../examples/ use a different, statement style (track, voice, pattern, mix); that style is not part of this spec yet. See the open question in ../open-questions.md ("Two syntaxes").
../../spec/ holds the same facts as data, so the parser, linter, language server, formatter and help.html never hard-code them: grammar.ebnf, tokens.json, types.json, classes.json, templates/*.json, diagnostics.json, lint-rules.json, plugins.json, version.json and JSON Schemas in schema/. The conformance corpus is in ../../spec-tests/. Check everything with node ample/tools/validate-spec.mjs.
01 Overview
Goals
A song is text that can be read, diffed, versioned and generated. The node editor, the arrangement and the source editor are three views of the same text (source-and-views).
Reproducible by default: every random choice is derived from a master seed; AI results are recorded as takes with their plans (06).
Good errors: one front end produces diagnostics with stable codes and exact ranges for the editor, the terminal and CI (08).
Small and checkable: a line-oriented syntax a person can write by hand and a tool can recover from.
Non-goals (spec 0.1)
General-purpose programming (no variables, loops or functions yet); defining new block types in the language (templates come from the library and plugins); audio DSP details (the engine's business).
The model in one paragraph
A song file declares a project (name, tempo, master seed), optional features and lint settings, and a list of blocks. A block has a type (from the template catalogue), a unique name, a class@version, optional placements on the timeline, optional flags, parameters and from lines that connect another block's output port to one of its own input ports. The blocks and their from lines form a directed acyclic graph that the engine builds into audio.
A tool conforms to spec 0.1 when it passes the corpus in spec-tests/: the same diagnostics (code, severity, range) for every case in invalid/, no errors for every case in valid/, identical output for format/, roundtrip/ and the recovery expectations in recovery/.
02 Lexical structure
Text
Encoding UTF-8, no BOM required (a BOM is accepted and removed by the formatter). Line endings \n or \r\n; the formatter writes \n.
The language is line oriented: one statement per line, except a block, which opens with a header line ending in { and closes with } alone on a line.
Positions are 1-based line and column, columns counted in characters (code points). The language server converts to UTF-16 for editors. A diagnostic range is [col, endCol) on one line.
Comments
# starts a comment that runs to the end of the line. A comment that reads # ample-ignore L001, L005 silences those lint rules for the next block (08). Comments are preserved by the formatter.
Names
identifier: one or more of letters, digits, _ and -. Examples: drums, kit, lm_seed, master-seed, felt-piano.
string: double quoted, with the escapes \", \\, \n, \t; any other backslash sequence stands for the escaped character itself (a parser never fails on an escape). A name is an identifier or a string; use a string when the name contains spaces or other characters.
Block names are compared exactly (case sensitive); two names that differ only by case trigger lint rule L008.
Numbers
integer is digits; number is digits with an optional .digits. There are no negative literals in structure positions (tempo, bars, seeds); a parameter value is free text and may contain a leading - (for example -4 dB). Seeds are unsigned 64-bit integers from 0 to 18446744073709551615.
Keywords
Keywords are reserved only in their own position (a block may be named at if it is quoted). The list is in ../../spec/tokens.json: project features lint layout tempo master-seed at for as from bake lock refresh freeze seeds plan stable pinned live on off assist diff button auto info warning error.
Operators and punctuation
{ } , . -> @. -> and . appear only in from lines; @ only in class@version; , only between placement parts and inside nothing else.
Limits (spec 0.1)
Tempo 20 to 300. Project master seeds and block seeds range from 0 to 18446744073709551615. Bars start at 1; a part is at least one bar; a part reaching past bar 1024 is a warning (AMP5002), not an error.
03 Syntax
The grammar below is the same text as ../../spec/grammar.ebnf (a test compares them). It describes the structure; the meaning of block types, parameters and ports comes from the template data (05).
plugin settings of this song, pairs of plugin.name and a value (text with spaces in quotes); part of the plan of the blocks of that plugin, so changing one makes a new take; at most one line; malformed is AMP1112. Project-level settings are only the starting point for new songs
layout
layout "name" 280 20
node editor position; no meaning for the sound
block
type name class@version [at ...] [flags] [freeze ...] { ... }
see below
comment
# text
# ample-ignore L001 silences rules for the next block
Finals are project metadata kept in .ample/finals.json and managed through the Finals view or API; they are not a statement or block in the AMPLE song language.
Blocks
drums beats stable@1.2 at 1 for 8 as intro, 9 for 8 as outro lock {
kit kit/tape
from chords.notes -> trigger
}
Header order is fixed: type, name, class@version, placement, flags, freeze.
Placement:at <bar> for <bars> [as <name>], several separated by commas. Parts of one block may not overlap (lint L004).
Flags:bake (the take is stored and used), lock (the value is kept), refresh (always fetch again), disabled (the block produces no take). A connected block that requires a disabled block is implicitly disabled with the warning AMP5253; an unconnected disabled block is simply omitted. lock with refresh is contradictory (L003).
Body lines: a parameter is name value (the value is the rest of the line, free text, validated by the template); a from line is from <block>.<output> -> <input>. A parameter name appears once per block.
The closing } is alone on its line.
Expanded MIDI notes
Only midi-clip may use a nested notes body. It represents the same notes as the compact notes parameter and remains in source after editing:
midi-clip verse stable@1.1 {
ppq 480
notes {
# comment stays with this note
note {
bar 18
start 22
length 72
pitch 121
velocity 96
channel 5
bank 0
program 60
tie-id 2
}
}
}
bar is one-based. start and length use quarter-note beats (up to six decimal places) when ppq is absent or uses the legacy million-unit scale; otherwise they use integer ticks at the clip's ppq. pitch is 0–127 and velocity is 1–127. channel, bank and program are optional as a complete set; tie-id is optional and identifies bar-split segments. The clip remains in 4/4 for note placement; imported meter events stay available in the MIDI event source. # comments are retained verbatim and ignored when building note data.
Error recovery (normative)
A conforming parser never stops at the first error and never throws on any input. Required behaviour:
A line that is not any statement reports AMP1100 and parsing continues with the next line.
A bad block header reports an error (AMP1102-AMP1106); the block body is still consumed up to its } so later blocks are found. If the header yielded a type and name, the block is kept in the tree (so the editor can still show it); otherwise its body is skipped.
A bad body line (AMP1107-AMP1109) does not remove the block; the rest of the body is parsed.
An unclosed block at the end of the file reports AMP1101 at the block header and keeps the block.
Semantic checks (AMP2xxx, lint) run on everything that parsed, even when syntax errors exist; blocks that are known to be broken (skipped) take no part in them.
The order of diagnostics in the output is by position (line, column, then code) when a tool prints them; implementations may produce them in another order internally, and the corpus compares them as a set sorted by (line, column, code).
The recovery corpus (../../spec-tests/recovery/) fixes the expected blocks and codes for broken inputs.
Modules (use and module files)
use <module>[@version] as <name> { ... } puts an instance of a reusable module into the song. The body gives the module's parameters (name value) and connects its inputs (from block.output -> input, where the last word is the module's input name). Other blocks read the instance like a block: from intro.audio -> mix, where audio is one of the module's outputs. The parser only checks the shape; the module is found and expanded when the song is built (ample modules expand, 07).
04 Types and ports
A connection can be temporarily switched off without removing it: from kit.audio -> in disabled. The canvas retains it as a dimmed dashed line; its context menu enables it again. Disabled connections supply no input and do not create active build dependencies. Mixer inputs are optional; a mixer processes the active channels, or produces no take when none remain. Required consumers and audio/render effects with their main input switched off become implicitly disabled. Enabling a connection restores its original endpoints.
Every block template declares input ports and output ports, each with a type. The types of spec 0.1 are in ../../spec/types.json:
Type
Meaning
audio
rendered sound
render
an immutable source recipe with ordered effects, materialized when audio is requested or the effect is explicitly built
midi
notes and timing (textual MIDI)
midi-sequence
a bounded MIDI event stream with tick order
text
words, such as lyrics
data
structured data: style, external values
any
matches every type except render; used by groups
Connection rule
from <block>.<output> -> <input> connects when the output type and input type are equal, or either is any, except that render only connects to render. This keeps source recipes distinct from materialized audio even at group boundaries. There are no implicit conversions. A mismatch is AMP2007.
Render recipes
Audio sources expose audio and render outputs. audio is the ordinary built sound. render carries an immutable recipe for that source and does not change its ordinary take. Effects accept either active in:audio or active render_in:render; both connections may be present when one or both are disabled. An effect can pass the recipe onward through render or materialize it at its existing audio output.
The effect chain is collected in order and the source is rendered with those effects when its audio output is consumed or the final effect is explicitly built. Intermediate render links create no takes. The mixer receives the effected variant on in1 and ordinary drums on in2. Two render branches remain independent. Connecting drums.audio -> eq.in keeps the existing post-render effect path.
The plan and cache identity include the source and its inputs, then the ordered effects and parameters. A changed effect rebuilds its variant and dependent audio consumers. render is not MIDI/events, and any cannot convert it.
Port rules
An input takes one source (AMP2008 for a second). To combine several sources use a mixer-like block.
A block cannot read itself (AMP2009); the graph must be acyclic (AMP2010, with the cycle path in the message).
A required input with no source is lint rule L002 (a warning by default): the template marks which inputs are required.
Naming a missing block, output or input is AMP2004, AMP2005, AMP2006; the hint lists the valid names.
Output ports may stay unconnected. Unused outputs are lint rule L001 (info); audio that never reaches a master block is L007.
Groups
A group block has one any input and one any output and may contain an inner graph that is edited in the node editor. Inner graphs are not part of the .amplet text in spec 0.1.
05 Blocks and templates
A template defines a block type: its ports, parameters, class, version, whether it is dynamic, and which plugin it needs. The catalogue is data: one file per template in ../../spec/templates/<id>.json, validated by ../../spec/schema/template.schema.json. The generated help (help.html) renders one card per template; this page defines the rules.
The provisional voice-clone block reads a project voice sample from file and speaks its text. model selects XTTS, Qwen3 or Chatterbox; language is passed to that model. Qwen3 additionally needs reference_text, unless auto_detect on is selected. The sample is hashed into the build plan, so changing it invalidates the generated take. Common audio formats are accepted and converted to WAV internally when needed. Model weights are configured locally and are not downloaded by the plugin.
Class and version
Every block header carries class@version:
Class
Meaning
stable
The block's behaviour does not change within a version.
pinned
Locked by content hash; an older pinned version than the template's is lint L006 and is only changed on purpose (it changes the sound).
live
May change between builds (dynamic): AI voices, the master chain. Lint L005 (off by default) wants them baked or locked before a release.
The version is dotted numbers (1.2, 0.9). The template states its current version; a block states the version it uses.
Parameters
splitter stable@1.0 takes audio in and exposes mono audio outputs left and right. It preserves channel level and duration; mono input is duplicated onto both outputs. The node preview plays the original input. Connect a render chain's materialized audio output to the splitter; the splitter itself does not forward render recipes.
splitter channels stable@1.0 {from level.out -> in
}eq left_eq stable@1.0 {from channels.left -> in
}eq right_eq stable@1.0 {from channels.right -> in
}
The volume stable@1.0 effect accepts either audio in or render_in. It exposes three fixed audio outputs: left and right are individual mono output channels; mixed contains both output channels as stereo. The render output continues the recipe. The earlier out identifier remains a source compatibility alias for mixed, hidden on the canvas. The block preview plays the mixed output.
The two channel faders use left_gain and right_gain (−60 to +12 dB, default 0). With linked on (default), moving either fader moves both by the same amount and preserves their difference, stopping at the shared bounds. Properties can set exact values independently. Linking is a UI setting and does not alter audio by itself. The existing gain parameter remains a common gain, default 0 dB. Positive gain can clip rendered PCM.
mono on averages the input channels before applying common gain, separate channel gains and mutes. Left and right remain separate ports with their connections intact; turning mono off restores stereo routing. left off and right off silence their respective output channels. Mono input feeds both channels. All settings are available in source and Properties; the canvas and Advanced panel provide L/R faders, meters, mutes and Mono. enabled off bypasses processing while preserving every output.
volume level stable@1.0 {
left_gain -3 dB
right_gain -6 dB
linked on
left on
right off
mono offfrom kit.audio -> in
}
A block accepts only the parameters its template lists (AMP2003 otherwise; the hint lists the valid ones).
The template gives each parameter a type (number or text) and a default. A missing parameter takes the default.
A parameter appears once per block (AMP1109).
Seed parameters (seed, lm_seed) are the word derived or an unsigned 64-bit integer 0 to 18446744073709551615 (AMP2011). A custom seed is allowed and recorded (L009).
A template may declare modalEditor: true for text that benefits from the property's expanded … editor, and multiline: true when line breaks are meaningful. Multiline values are stored as the existing \n source escape and restored as line breaks by the property editor; a single-line value visibly wraps but line breaks are converted to spaces when saved.
A template may declare affectsOutput: false on a parameter that is an execution policy rather than a sound setting: a hint about how the renderer gets to its result (for example a time/memory trade-off) where every option gives byte-identical audio. It defaults to true. A studio panel may show such parameters apart from the musical controls (denoise's optimize does); a renderer that sets it false must prove the claim (same input, same output, for every option) before relying on it, since nothing else enforces it.
The universal block header flag disabled is independent of template parameters and effect enabled off bypass. It keeps the block visible but returns no take. Blocks depending on its required inputs, including audio and render effect chains, become implicitly disabled without a build failure; the block reports the warning AMP5253 so it is visible why it produced nothing. Optional inputs, such as mixer channels, omit the disabled branch so other channels can still render. A mixer with no connected inputs or only disabled inputs produces no take and is implicitly disabled. Implicit disabling is derived from connections and disappears when an upstream input becomes active; it does not write a disabled flag into downstream source.
Audio sources expose a render output alongside ordinary audio. Built-in audio effects accept an audio in for post-render processing or a render_in recipe for a source variant. Both connections may be present when at least one is disabled; only active connections determine the effect input. The next audio connection materializes the whole recipe as a variant take; explicitly building a render-connected effect also materializes that effect's variant. Intermediate render links create no takes. Effects remain independent blocks with their own parameters.
Block takes use local time: their first placed part starts at local bar 1, without leading silence from the arrangement position. Moving the whole block preserves its take and the takes of its audio/render effects; relative gaps between multiple parts remain part of its content. Mixer and master inputs apply arrangement placement and range cropping, and their plans include that placement. Moving a block invalidates the arranged mix, which can be regenerated using cached local takes. The local-v1 plan marker separates these takes from earlier song-length renders; existing selected frozen takes retain their original content until explicitly replaced.
The midi-clip template also accepts an optional structured notes value. It can be a space-separated list of bar:beat:length:pitch:velocity entries or a persistent notes { note { ... } } body with named fields. Beat and length use quarter-note beats with up to six decimal places by default; set ppq to use exact integer ticks per quarter note instead. Eight-field entries can append channel:bank:program to retain source instrument routing; generated nine-field entries add a tie identity so bar-split sustains remain distinct from repeated notes. A compact entry may append ;pan=, ;fine=, ;rel=, ;cut=, ;slide= or ;port=; the language server completes these keys after a semicolon, and invalid values produce AMP2012. The expanded body accepts bar, start, length, pitch, velocity, optional routing fields and optional tie-id; # comments stay in source beside the note or field they describe. Notes are polyphonic, must fit within one 4/4 bar, and represent a tie across a bar as one entry in each bar. GM/SF2, synth and sample-set piano/bass/guitar render per-note expression; slide renders a glide without a retrigger. MIDI module import/export retains supported note-start expression and writes pitch-bend ramps for slide notes. Portamento settings round-trip as CC65/CC5; renderer portamento is not implemented. The empty default keeps existing chords and melody behavior unchanged.
Compact notes additionally accept ;tie=N (integer 0 through 2000000) to join consecutive bar-split pieces without explicitly setting channel, bank or program. Expanded tie-id also works without routing fields. Nine-field ties remain supported; writing both forms on one entry, or repeating tie, is invalid. A long piano-roll note is stored as these valid bar segments; the editor merges them for display and resizing. This preserves the connected instrument's inherited routing when a note grows over a bar line.
The midi block exposes notes, the complete typed sequence, and audio ports. Connect the sequence to midi-render to render the file through a project SF2 file or the installed GM pack with a gain control. MIDI, midi-render and GM instrument blocks default to an integer soft limiter before PCM output; set limiter off only when hard clipping is wanted. The song mixer also soft-limits summed inputs. If an SF2 bank lacks a source bank/program, the GM renderer uses the matching GM program (or piano) and reports the fallback. The notes port remains available for piano, GM and other instrument blocks.
The midi data block reads a project .mid or .midi file. Its track parameter is all or a zero-based track index; its notes:midi output has the same shape as midi-clip, and its optional warnings:text output describes unsupported note-adjacent events. The block may also retain source-file, variant-file and variant-module for an editable MIDI variant: Edit MIDI variant opens the module, saving it exports a new active MIDI asset, and the context menu can switch between Original and Edited.
File parameters and what can be dropped on them. A parameter of kind: file lists the extensions it takes in accept (for example .mid, .midi for the file of midi; .sf2 for gm and midi-render); a parameter of kind: sampleset takes a folder. The web app uses these lists to decide which blocks a file can be used in (Use in song, drag and drop). A new file parameter that should take drops needs an accept list.
Fades: fadein, fadeout and the fade and crossfade blocks
A fade is a value that goes from silence to full (a fade in) or from full to exact silence (a fade out). It is written as the value of a volume gain or of the mix of an effect:
volume v stable@1.0 {
from drums.audio -> in
gain fadeout(5, 2, equal) # from bar 5 over two bars, ends in silence
}
fadein(start, length [, shape]) and fadeout(start, length [, shape]). A fade in is silent before its start and full after its end; a fade out is full before its start and exactly silent after its end.
Units: a plain number is bars (5 is the start of bar 5, 5.5 is half way through it; the start counts from bar 1; the length 0.5 is half a bar). A number with an s is seconds (12s, 1.5s); a start in seconds counts from the beginning of the song. Start and length may use different units. Up to six decimals.
Shapes:lin (a straight line in volume, the default), equal (equal power: a fade in and a fade out of the same length add up to constant power for unrelated sounds, so use it for a crossfade) and exp (starts slowly, ends fast).
All arithmetic is integer, so every machine gets the same bytes. A fade on a pan, a cutoff or a threshold is refused with AMP5371.
The fade block does the same with one audio input and one output (direction out or in, start, length, shape). The crossfade block has two inputs, a and b: a fades out and b fades in over the same stretch (start, length, shape, equal power by default). Both build the same fade text and use the same engine as the formula, so a block and a formula with the same settings give the same bytes. The curve editor (the ∿ button) has a Fade… button for a gain and a mix.
Patterns and sections.drums and guitar take two optional parameters, patterns (named patterns, name=grids / name=grids) and song (the sections in order, verse*4 fill chorus*8); see Samples.
Time stretch.stretch.speed is a percentage from 25 to 400; 50 plays at half speed and keeps pitch. It also accepts auto(bar:percent, ...), such as speed auto(1:100, 9:50), for a linear speed ramp by bar. The rendered block keeps its length. This does not change the project's tempo.
Dynamic blocks and plugins
A template can be dynamic (its result can change, for example an AI take) and can require a plugin. A block whose plugin is not installed is AMP5001, a warning: AMPLE reports it instead of silently skipping the block. Plugins are listed in ../../spec/plugins.json (opt-in or built in).
The catalogue (spec 0.1, provisional)
midi-clip, midi, gm, synth (a small subtractive synth on MIDI notes), sampler (one sample played on notes, moved in pitch from a root note), import-audio (plays a WAV file of the project), the MIDI effects transpose, midi-humanize, quantize, arpeggiator and chord (notes in, notes out, between a clip and an instrument), send (a shared effect bus: through and send outputs), drums, bass, piano, the effects eq, distortion, delay, reverb, compressor, filter, limiter, width, pan, gate, deesser, chorus, flanger, phaser, bitcrusher, pitch, stretch and duck (audio in, audio out, with an enabled off bypass), ai-voice, ace-music, mood-scene, choir, lyrics, weather, import-audio, mixer, master, variants, scorer, group. gm plays a zero-based General MIDI program and bank from the installed MuseScore General SF2 pack or a project-local .sf2 file. ai-voice is for isolated vocals: its voice parameter is a vocal direction, not a selected speaker identity, and it requires linked lyrics. Both ACE blocks expose guidance (CFG) as a stored generation parameter, visibly resolved to 7 for Base/SFT and 1 for Turbo; changing it changes the requested ACE guidance scale and can change the sound. Keep it fixed when comparing seeds. ace-music intentionally allows accompaniment and has optional lyrics, so it can make an instrumental. mood-scene.style supplies reusable scene direction to either ACE block. Adding or changing a template is a language change (99).
Filter wobble. A filter block can move its cutoff with lfo_rate (off, 1/1 through 1/32, and triplet rates 1/4t, 1/8t, 1/16t), lfo_depth (0–8 octaves above the base cutoff), and lfo_shape (sine, triangle, saw, square). The oscillator phase follows the song bar, so rendering a selected range keeps the same phase as rendering the whole song.
Example
example: compiles
project"demo"{tempo92master-seed424242}midi-clip chords stable@1.0 {
chords Am F C G
}pianopianopinned@2.0 at1for8as verse {from chords.notes -> notes
}
project "name" { ... master-seed N } sets the master seed (unsigned 64-bit integer 0 to 18446744073709551615). Explicit block seeds use the same range; derived follows AMPLE's stable generator. Every block with random behaviour derives its own seeds from the master seed and a stable path (block identity and purpose), so a song is a pure function of its text and its recorded takes. The derivation uses the stable integer-only generator specified in chunk 30; its algorithm and test vectors will be added here and are frozen once released (rngVersion).
Seed parameters
A block may set seed or lm_seed to derived (default) or to a number. A number is a custom seed: allowed, recorded, and reported as lint L009 (info). Changing a seed later means takes made with the old one cannot be reproduced from the new one.
Flags and freeze
Syntax
Meaning
bake
the rendered take is stored and used instead of rendering again
lock
the value (for data blocks) is recorded and kept
refresh
always fetch again (contradicts lock: L003)
freeze seeds
the seeds are fixed; the same inputs and seeds are rendered the same way (when the plugin is deterministic)
freeze plan
seeds and the melody plan are stored; the voice may be re-rendered
bake is the top of the freeze ladder (free, seeds, plan, take). freeze and bake are mutually consistent: a baked block needs no freeze word (the serializer omits it).
What the language does not promise
Regenerating an AI block is only guaranteed to give the same result on the same machine and toolprint. Notices AMP5101 and AMP5102 say so (08); the stored take is what guarantees the song.
Algorithms (RngVersion 1, frozen once released)
Seed expansion: SplitMix64 (state += 0x9E3779B97F4A7C15; z = (z ^ z>>30) * 0xBF58476D1CE4E5B9; z = (z ^ z>>27) * 0x94D049BB133111EB; output z ^ z>>31). A seed is expanded into four 64-bit state words by four calls.
Bounded integers: unbiased by rejection: discard values below (264 - bound) mod bound, then take the value mod bound. Unit fractions: the top 53 bits as an integer divided by 253 (the only floating-point step; both numbers are exact). Shuffle: Fisher-Yates from the end, j = bounded(i + 1). Pick: bounded(count).
Seed derivation: SipHash-2-4 with key (k0 = master seed, k1 = 0x414D504C45534545, the ASCII text "AMPLESEE") over the bytes: the RngVersion byte, then for each path part either 0x01, the UTF-8 length as 4 bytes little endian and the UTF-8 bytes (a string), or 0x02 and the value as 8 bytes little endian (a non-negative integer). The 32-bit seed is the low 32 bits.
Verification: the vectors in spec-tests/prng/vectors.json (generated by ample/tools/gen-prng-vectors.mjs, checked against the published SplitMix64 outputs for seed 1234567 and the SipHash-2-4 paper vector) are tested by the C# code (Ample.Core.Random) and the JavaScript twin (web/lib/stable-rng.js). Changing any of this creates a new RngVersion; plans store the version they used.
Guarantee layers
A stored take plus the deterministic mix is bit-identical (hash verified).
Everything AMPLE renders itself is identical on every supported platform.
Regenerating an AI block is guaranteed only locally; the regeneration flow verifies and defaults to restoring the previous take.
What a change of the master seed reaches (2026-10-08)
The master seed only changes a block whose seed is derived from it. A block does not follow it when it has a custom seed (seed 77), when its seed is locked (the take bank keeps the locked value), or when its block type never reads a seed (clips, mixer, master, groups, lyrics, import, effects and the like): the plan of such a block carries seed 0, so it keeps its take when the master seed or the block's name changes. The editor shows the same: after a new master seed only the blocks that follow it, and the blocks that read them, lose their built state (GET /api/doc/inputs lists them as followsMaster).
07 References and modules
Names and references
Block names are unique in a song (AMP2002) and are the only identifiers references use. A from line names <block>.<output>; both parts may be quoted when they contain spaces.
Reference resolution is by exact name. A near miss gets a hint ("Did you mean ...?") and a quick fix, never an automatic correction.
Apply (the editor's action) matches text blocks to the existing graph by name and type: the same name and type keeps its identity, its inside and its cache state; a new name creates a block; a name that is gone removes the block (source-and-views).
Files
Blocks that point at files (for example lyrics with file lyrics/chorus.lyr) use project-relative paths with / separators; a path never escapes the project or a registered link. Resolution order (library chunk 85): project, linked folders, the user library, installed packs. A missing file is a diagnostic with a "relocate" fix. (Built in chunk 85 as a service, not yet as a static diagnostic; see the library manager. Names are written without the file extension; the order is the project folder (<name> or assets/<name>), the project's linked folders, the user's linked folders, installed packs; codes AMP5290, AMP5291, AMP5294.)
Plugins, packs, locks
Plugins and packs are referenced by name and version in the project and pinned by hash in ample.lock, which the tool writes and people do not edit.
A template that needs a plugin that is not installed is AMP5001 (a warning).
Modules (spec 0.1)
features { ... } switches opt-in modules on or off: quality, credits, signing, secrets, lyrics, git-mirror; assist sets how much help the editor gives (off diff button auto). Reusable block modules are defined below (added in chunk 94).
Not yet specified
include of other .amplet files, imports of audio, MIDI or lyrics files, and the statement style of the older examples. They are tracked in open-questions.
Reusable modules (use and module files)
A module is a file modules/<name>.module in the project (or in a linked folder or pack, found the same way as other assets). Its first line says what it is, then it declares its knobs and doors, then it holds ordinary blocks:
module groove version 1.2
param kit kit/tape # a parameter with its default
input chords # a door the song connects
output audio mix.mix # what the song can read: <block>.<port> inside the module
bass bass stable@1.1 {
from in.chords -> notes # "in.<input>" reads the module's input
}
drums drums stable@1.2 {
kit $kit # $name stands for the parameter
}
mixer mix stable@1.0 {
from drums.audio -> in1
from bass.audio -> in2
}
The song uses it with use groove@1.0 as intro { kit kit/808 from chords.notes -> chords } and reads from intro.audio -> mix.
Expansion. Before a song is built each use becomes plain blocks named <instance>/<block> (nested instances: outer/inner/block). The tool expands in memory; the song file keeps the short use. ample modules expand song.amplet prints the result.
Seeds. A block's seed is derived from its name, so the seeds of an instance depend on the instance path: the same module used twice gives two different sets of seeds, each stable, and renaming an instance changes its seeds (like renaming a block).
Parameters. The use may set any declared parameter; the others keep their default. $name is replaced in parameter values only.
Versions.@1.0 asks for the same major version and a minor version that is not older; no @ takes any. The versions and hashes that were used are pinned in ample.lock (a modules section); a changed module is AMP5328 until ample lock update.
Not in a module: placement (at ... for ...) works as written but cannot be moved by the use, and a module cannot read a block of the song except through its inputs.
Diagnostics (runtime): AMP5320 to AMP5328 are reported at the use line. The editor (parser) does not load modules; it accepts the shape of use and references to an instance.
08 Diagnostics and lint
The catalogue of codes is data: ../../spec/diagnostics.json (code, severity, title, explanation, wrong and right example, since, stage) and ../../spec/lint-rules.json. The generated help renders every code on its own anchor (help.html#AMP2003); ample explain AMP2003 prints the same text. This page defines the rules around them. Background: language-support.
The record
Every diagnostic has: code, severity (error, warning, info), a range (line, col, endCol, 1-based, one line), message (one plain sentence), optional hint, optional related (another place that matters), and optional fixes (a label and a text edit, marked safe or risky).
Code ranges
Range
Meaning
AMP1xxx
syntax: the text is not shaped right (AMP10xx project, features and lint lines; AMP11xx blocks)
AMP2xxx
meaning: the text is shaped right but says something impossible
AMP5xxx
environment and runtime: depends on what is installed or what happened (AMP50xx static warnings, AMP51xx regeneration notices)
Lxxx
lint rules: compile but probably not what you meant
plugin prefixes (ACE1xxx)
codes contributed by a plugin, declared in its manifest, same record shape
Stability: a code is never reused. A retired code stays in the catalogue (retired: true). Each code's stage is static (found by reading the song) or runtime (found by running or regenerating). Messages may be reworded; codes, severity and ranges are the contract.
Severity and blocking
error blocks Apply and leaving the source editor. warning and info never block. A lint rule raised to error blocks like any error.
Ranges
A diagnostic points at the smallest range that is wrong (the parameter name, not the whole line). Where a code can point at two places the range is the one the message talks about; the other is related.
Lint rules
Nine rules in spec 0.1 (see lint-rules.json for names, defaults and descriptions): L001 unused-output, L002 missing-input, L003 lock-and-refresh, L004 overlapping-parts, L005 dynamic-not-fixed, L006 pinned-outdated, L007 no-path-to-master, L008 name-case-clash, L009 custom-seed.
Level per rule in the song: lint { L004 error L005 info } (only entries that differ from the default). Unknown rule AMP1007, bad level AMP1008.
Suppression: a comment # ample-ignore L004, L005 silences those rules for the next block only.
Lint runs only on blocks that parsed; it never changes the sound.
Notices about regeneration
AMP5101 (info): regeneration is only guaranteed to give the same result on this machine with the same plugin, model and GPU setup. AMP5102 (warning): the toolprint differs from the one that made the take, or the plugin's determinism is unverified. Both are runtime stage; the exact wording is in diagnostics.json and is shown by the UI, the CLI and the help (regeneration safety).
Output formats
Text (for people), JSON (the record above, one per diagnostic) and SARIF (later). The corpus compares JSON records as a set sorted by (line, col, code).
09 Formatting
The output of the formatter is the canonical form. ample fmt rewrites a file to it; the language server's formatting does the same; the corpus (../../spec-tests/format/ pairs and roundtrip/ files) defines it by example.
Rules (spec 0.1)
UTF-8, \n line endings, no trailing whitespace, exactly one final newline; a BOM is removed.
Indentation is two spaces inside blocks; tabs are replaced.
Blank lines: a run of blank lines collapses to one; blank lines inside a block are kept as one.
Single spaces between words in a header, between a parameter name and its value, and around ->; no space before , and one after it.
Brace lines (project, features, lint) read keyword { pair pair }: one space inside the braces, two spaces between pairs.
A block header ends with {; the closing } is alone on its line.
Comments are preserved exactly, except that trailing whitespace is removed and the line is indented to its context (no indent at top level, two spaces inside a block). A comment after code on the same line is not part of the language (it would make the line unreadable to the parser).
The formatter never reorders statements, parameters or from lines, and never changes values or names.
The formatter is idempotent: formatting its own output changes nothing, and parsing then printing a canonical file gives the same bytes (round trip).
Behaviour on broken input
The formatter formats everything it understands and leaves a line it cannot parse unchanged (besides trailing whitespace and indentation inside blocks). It never refuses.
10 Lyrics format
Status: only partly specified. The own lyric format .amplelrc (all lyric information in one extensible structure: words, line and word timing, syllables, sounds, singers, language, notes; it states format: amplelrc and a version) is decided in principle (decisions, "Lyric formats") but its field-by-field layout is still open design work (open-questions).
What exists in the mock (provisional)
The sketch's lyrics block points at a plain file (lyrics/chorus.lyr) with one line per row, written [bar 9] text. The file is the canonical source: the Lyrics editor reads and writes it, and linked AI voice or ACE music blocks receive its marker-free words. The old text parameter remains as a compatibility fallback only when the file is absent or empty; it is not edited by the Lyrics editor. The editor checks live: the first line within 5 seconds, lines in order, lines outside the block's placement, empty lines, long lines, too many words for the gap.
Importers (planned, not part of the grammar)
.lrc, enhanced .lrc, .srt/.vtt, TextGrid and aligner JSON convert to and from .amplelrc (chunk 20 and later).
Plan
This page becomes normative when .amplelrc is designed: a JSON Schema in ../../spec/schema/, a conformance corpus under ../../spec-tests/lyrics/, and the checks listed above as diagnostic codes.
11 Versioning and compatibility
Spec version
../../spec/version.json holds specVersion (major.minor) and status (draft or stable). A song does not state a language version in spec 0.1; the first line of a future file may (reserved).
Patch-level wording fixes do not change the version.
Minor versions add things in a backward-compatible way (a new template, a new optional parameter, a new diagnostic code).
Major versions break compatibility and come with a migration note in CHANGELOG.md.
Before 1.0 a minor version may break; every break has a migration note and the tool offers ample migrate once real songs exist.
Stability of the data
Diagnostic and lint codes are never reused or renumbered; retired codes stay listed.
Template ids are never reused; a template is provisional, stable, deprecated or retired, with since and optionally deprecated (the version and the replacement).
The canonical plan form and the stable random generator are frozen once released and carry their own version (rngVersion); see 06.
Compatibility promise (at 1.0)
A song that is valid at 1.x stays valid at every later 1.y and builds to the same take references; deprecated features keep working with a warning for at least one minor version.
File format versions
.amplet, .amplelrc, .ample exports and plugin.json each carry a format version; opening an older format runs a migration with a backup copy first (project-management).
99 Maintaining this specification
The language specification must be kept true for as long as AMPLE exists. It is the one place where parser, linter, language server, formatter, help generator and UI agree on what the language is.
The rules
The spec is normative.ample/docs/ pages explain; this folder and ample/spec/ define. On conflict, the spec wins and the other page is fixed.
A language change is five edits in one change:
the data in ample/spec/ (template, diagnostic, lint rule, grammar, tokens);
the prose page in this folder;
a conformance case in ample/spec-tests/ (a valid case for new syntax, an invalid case with expected diagnostics for a new code);
an updated example (in the page, tagged ```amplet run once the help generator executes them, chunk 12);
a line in CHANGELOG.md, with a migration note if it breaks anything.
CI enforces it:node ample/tools/validate-spec.mjs (schemas, unique ids, grammar equals prose, links, every static diagnostic and lint rule has a failing example) and, from chunk 12, ample docs check (the generated help.html is up to date and all examples compile or fail as stated). A change that breaks either is not done.
The help is generated, never edited by hand. It is built from this folder and ample/spec/ (chunk 12).
Codes and ids are never reused. Retire, do not delete.
Provisional is allowed, silent is not. A not-yet-decided detail is marked provisional in the data and listed in ../open-questions.md; it is never guessed in code.
Corpus before code. When the implementation and the corpus disagree, decide which is right, fix the spec and the corpus first, then the code. Never edit the corpus just to make a test pass.
Review points
Whenever a chunk that touches the language is marked done, the agent re-runs validate-spec and reads README.md for staleness.
At each milestone (M1 to M5 in the plan) the owner reviews the spec index and the changelog.
Before 1.0: a full read-through against the working product, with every difference fixed on the side that is wrong.
Where the corpus came from
The first corpus (spec version 0.1) was generated by ample/tools/gen-corpus.mjs from the clickable sketch's mock parser, because that was the only executable reference of the mock syntax. Expected diagnostics therefore describe the mock; chunk 20's real parser must match them, and from then on the corpus is edited by hand. The templates, codes and lint rules were seeded by ample/tools/extract-spec-from-sketch.mjs and are hand-maintained now.
Language changelog
Piano-roll ties (2026-10-18)
Compact notes accept ;tie=0..2000000 to join bar-split sustains while keeping instrument routing inherited. Legacy nine-field ties remain valid;
duplicate or conflicting tie identities are rejected. Expanded tie-id also works without explicit channel/bank/program.
The built-in midi-clip renderer is version 4 so cached note output includes the new tie semantics.
Render stream extension (implementation in progress; verification paused)
Added the render port type, render outputs on audio sources, and render_in inputs on built-in audio effects. Effects may use their existing audio input or the render recipe input; both may be connected when one or both connections are disabled.
Added AMP2020 for connecting both effect input modes while both are active. Source variants now retain ordered render recipes, materialize at audio boundaries, and record effect parameters and seeds in the plan. Full build, cache, UI and conformance acceptance remains in progress; testing is paused at the owner's request.
Added docs/examples/16-audio-vs-render/, a self-contained song with matching EQ/reverb chains on ordinary audio and render paths, mixed for direct comparison.
Added in MIDI M03 (2026-10-04)
midi-clip is version 1.1 and accepts optional structured polyphonic notes as bar:beat:length:pitch:velocity entries. Beat values use integer microbeats internally with up to six decimal places in source; ties crossing bars are split. Existing chord and melody songs are unchanged.
Added static diagnostic AMP2012 for malformed structured MIDI note entries. Migration: none; the new parameter defaults to empty.
Added in MIDI M13 (2026-10-04)
midi-clip retains imported note onset and duration in source PPQ ticks, including output metadata, while preserving readable beat fields.
Added in MIDI M15 (2026-10-04)
The MIDI file template exposes note, typed sequence and audio outputs. The midi-render template accepts sequence or note input and provides a SoundFont-backed audio conversion path.
Added in MIDI M02 (2026-10-04)
Added the midi source template for bounded .mid/.midi project files, deterministic track selection and notes:midi rendering, with a text warnings output.
Added build diagnostics AMP5501–AMP5503 for invalid assets, unsupported exact note timing and missing track indices.
0.1 draft (2026-10-03)
Added mood-scene (a stable style:data source) and ace-music (a live ACE-Step block with optional lyrics:text, optional melody:midi, optional style:data and audio:audio). ai-voice remains a live ACE-Step block with required lyrics and a vocal-only contract.
Template parameters can now describe modalEditor and multiline property editing behavior. The display label and short documentation are also template data.
Migration: existing ai-voice voice values retain their parameter name, but the UI calls it Vocal direction. Existing inline lyrics text values still render only when the linked file has no content; move new work into the Lyrics editor file.
0.1 draft (2026-10-02)
First written specification: lexical rules, grammar, error recovery, types and ports, template rules, seeds and determinism, references, diagnostics and lint, formatting, versioning, maintenance.
Data seeded from the clickable sketch: 14 templates, 31 static diagnostic codes (AMP1xxx, AMP2xxx, AMP5001, AMP5002), 9 lint rules, plus the runtime notices AMP5101 and AMP5102 (regeneration safety).
Conformance corpus: 15 valid, 42 invalid (one per code and lint rule), 8 recovery, 10 format pairs, 7 round trip.
Known gaps: the statement-style syntax of ../examples/, include and imports, .amplelrc, inner graphs of groups, defining new block types (all in ../open-questions.md).
Added in chunk 94 (2026-10-03)
New statement use <module>[@version] as <name> { ... } and the module file format (module, param, input, output); new static code AMP1110 and runtime codes AMP5320 to AMP5328; keyword use. Nothing that parsed before changes meaning (a block type called use did not exist). Migration: none.
Added after chunk 98 (2026-10-03)
drums is now 1.3 (S07): optional choke parameter (open:hat,pedal; rules separated by ;) cuts a voice when a closing voice is hit, with a 7 ms fade. Sample folders may hold a set.json with gain (dB) and tune (semitones) for the whole set. Songs that say drums ... stable@1.2 stay valid (the class is stable, not pinned). Migration: none.
Sampler velocity layers (S05): sample files name_v<L>_r<R>.wav are layer L, round robin R; accents, strokes and note velocity choose the layer. melody notes take an optional third field, the MIDI velocity (c4:1:110, default 96, unchanged output). Round robins (S06) are chosen per hit from a separate seeded stream (no change to humanize bytes). Built-in drums, bass, piano and guitar renderers are version 2. Migration: none, folders without layer files sound the same.
Template parameters can say how the property panel edits them: kind (text, number, choice, combo, seed, sampleset, file, pattern, channels) with options, min, max, step, unit, placeholder, accept and count. Parameters no longer start with made-up text; an empty value shows a placeholder. Migration: none, old values keep their meaning.
Six audio effect templates (category Effects, stable@1.0): eq, distortion, delay, reverb, compressor and filter. Each has one required in:audio, one out:audio and an enabled on|off bypass. Integer arithmetic only, so they are bit-reproducible. Migration: none.
midi-clip has an optional melody parameter (note name, octave, length in beats; r is a rest). When it is set, the clip plays the melody instead of the chords. Notes now carry beat and beats; the built-in piano plays them at that place and length (notes without them still last the whole bar). Migration: none.
lyrics is now 1.1: the legacy inline text parameter is gone. The words live in the lyrics file, which the lyrics editor creates and names (lyrics/<block name>.lyr) the first time you open it. Migration: move old inline text into a lyrics file ([bar N] words per line) and set file; songs that still say text get AMP2003.
piano is now 2.1 and bass 1.2: set can name a sample folder of the project (note-named WAV files, like the guitar), and both have a gain in dB. The built-in sounds are unchanged. Migration: none.
ai-voice is now 0.10: the optional parameter sing (bars) says how long the voice sings. ACE fills all the time it is given, so a voice longer than its words repeats the last sounds; with sing the voice is made that long and the rest of the block stays silent (short fade at the end). Empty means the whole block, as before. Migration: none.
ai-voicesing also takes the word auto: the voice is as long as the linked lyrics file needs (from the start of the block to the end of the last timed line; the last line gets as many bars as the one before it). The build replaces the word by the number, so the plan holds the real length.
The lyrics file can hold section tags as lines of their own ([Verse], [Chorus], [Outro]). They have no bar, are not timed, are passed to ACE with the words and show as heading rows in the lyrics editor. Migration: none (such lines used to be reported as unreadable).
ai-voice is now 0.11: the optional parameter fit (off or stretch). ACE tends to sing a little faster than the music and then runs out; stretch slows the voice down without changing its pitch so it ends where the lyrics end (at most 1.6 times as long). The factor is written into the take (voiceStretch). Default off. Migration: none.
ai-voice 0.12 and ace-music 0.2: steps is empty by default, which takes the number of steps the chosen ACE model wants (turbo 8, sft and base 50). A block that says steps itself keeps that number. The model is a plugin setting (synthModel). Migration: none.
New statement settings { plugin.name value ... } for the plugin settings of one song (new static code AMP1112 for a malformed line). The settings of a song are part of the plan of the blocks of that plugin, so choosing another ACE model or isolation gives a new take instead of the cached one. The plugin settings that were stored once per project (.ample/plugin-settings.json) are now only the defaults for new songs: a build reads the settings of the song, not those of the project. Migration: a song that relied on project-level settings needs a settings line (the plugin settings screen has "Use the project defaults" and "Apply and back" writes it).
Plugins can report progress with sub-steps (ctx.progress(fraction, step, stepFraction)), shown as bars in the node editor; the server sends the event build.block. No change to songs.
A song that lacks a setting its plugin marks as required fails its blocks with AMP5370, which names the setting and the project default; the node menu offers "Use the project default" (writes it into the song's settings line and builds again). New songs are made with + New song (POST /api/songs) and start with the project defaults in their settings line. No change to existing songs.
New effect block duck (stable@1.0, category Effects): the sound dips at every hit of a 16-step pattern (default every beat) by depth dB and comes back over release ms, like a sidechain pumping effect. Integer gain, deterministic. Migration: none.
ai-voicesing auto also looks at the words: the length is the smaller of the lyrics timeline and an estimate from the text (about four syllables a second plus a breath per line), so a short text is no longer stretched over a long block. Voices with sing auto get a new take once.
ACE plugin 0.5.1: a voice or music block that sits later in the song (at 5 for 4) is now made as long as its own bars and placed where the block starts; before, the sound began at the start of the song. Voice takes are made again once.
ACE plugin 0.6.0: the setting backend (server, cli, server-then-cli) with aceBinFolder and cliLmModel. A plugin can mark settings every song must state itself (x-songRequired); a song that does not gets the editor warning AMP2013 with a quick fix (the project default) and AMP5370 when it is built. The example songs now state acestep.backend server. Migration: songs that use ai-voice or ace-music need settings { acestep.backend server } (the quick fix writes it).
2026-10-04
Added the midi file block with project-contained inputs, deterministic track selection, canonical notes output and unsupported-event warnings.
Block templates
Every block type of the language, generated from spec/templates/. Click a type for its ports and parameters.
ACE music (ACE-Step) ace-music
Creates intended backing music or a full song. Lyrics are optional: without them it makes instrumental music.
live@0.4Voicesdynamicplugin: acestepprovisional since 0.1
Sings the linked timed lyrics as isolated vocals only; it never intentionally asks ACE-Step for backing music.
live@0.14Voicesdynamicplugin: acestepprovisional since 0.1
Inputs
Name
Type
Required
lyrics
text
yes
melody
midi
no
style
data
no
Outputs
Name
Type
vocal
audio
render
render
Parameters
Name
Type
Default
voice
text
sing
text
fit
text
off
steps
text
guidance
text
shift
text
solver
text
euler
lm_temperature
text
0.85
lm_cfg_scale
text
2
lm_top_p
text
0.9
lm_top_k
text
0
lm_negative_prompt
text
lm_seed
text
derived
seed
text
derived
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}ai-voice example live@0.14 {
fit off
solver euler
lm_temperature 0.85
lm_cfg_scale 2
lm_top_p 0.9
lm_top_k 0
lm_seed derived
seed derived
channels auto}
Ambience ambience
A recording (rain, a room tone, a crowd) turned into a seamless ambience of any length: stretches of it are chained with crossfades, from random places, so no loop can be heard.
stable@1.0Dataprovisional since 0.3
Inputs
None.
Outputs
Name
Type
audio
audio
out
audio
Parameters
Name
Type
Default
enabled
text
on
file
text
start
text
end
text
segment
text
12
crossfade
text
3
variation
text
40
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}ambience example stable@1.0 {
enabled on
segment 12crossfade3
variation 40
channels auto}
Arpeggiator arpeggiator
Turns chords into runs of single notes.
stable@1.0MIDI effectsprovisional since 0.2
Inputs
Name
Type
Required
notes
midi
no
Outputs
Name
Type
notes
midi
Parameters
Name
Type
Default
enabled
text
on
rate
text
1/16
pattern
text
up
octaves
text
1
gate
text
80
Example
example: compiles
project"demo"{tempo92master-seed424242}arpeggiator example stable@1.0 {
enabled on
rate 1/16
pattern up
octaves 1gate80}
Bass bass
Bass synth.
stable@1.2Instrumentsprovisional since 0.1
Inputs
Name
Type
Required
notes
midi
yes
Outputs
Name
Type
audio
audio
render
render
Parameters
Name
Type
Default
set
text
analog-808
pattern
text
bass-A
gain
number
0
humanize
number
0
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}bass example stable@1.2 {
set analog-808
pattern bass-A
gain 0
humanize 0
channels auto}
Bitcrusher bitcrusher
Lo-fi: fewer bits and a lower sample rate.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
bits
text
8
downsample
text
1
mix
text
100
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}bitcrusher example stable@1.0 {
enabled on
bits 8
downsample 1
mix 100
channels auto}
Bus bus
A group fader: sums the tracks that connect to it (like a mixer) and has one gain and one mute for the whole scene. Put the rain, the pub and the music on their own bus.
stable@1.0Mixprovisional since 0.1
Inputs
Name
Type
Required
in1
audio
no
in2
audio
no
in3
audio
no
in4
audio
no
in5
audio
no
in6
audio
no
in7
audio
no
in8
audio
no
in9
audio
no
in10
audio
no
in11
audio
no
in12
audio
no
in13
audio
no
in14
audio
no
in15
audio
no
in16
audio
no
Outputs
Name
Type
mix
audio
Parameters
Name
Type
Default
gains
text
0 0 0 0
pan
text
c c c c
inputs
text
4
gain
text
0
mute
text
off
solo
text
off
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}bus example stable@1.0 {
gains 0000pan c c c c
inputs 4
gain 0
mute off
solo off
channels auto}
Choir layer (overlap) choir
Layers voices.
stable@1.0Voicesdynamicplugin: acestepprovisional since 0.1
Inputs
Name
Type
Required
lyrics
text
yes
Outputs
Name
Type
audio
audio
render
render
Parameters
Name
Type
Default
voices
number
3
spread
text
12%
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}choir example stable@1.0 {
voices 3
spread 12%
channels auto}
Chord chord
Turns every note into a chord.
stable@1.0MIDI effectsprovisional since 0.2
Inputs
Name
Type
Required
notes
midi
no
Outputs
Name
Type
notes
midi
Parameters
Name
Type
Default
enabled
text
on
type
text
major
inversion
text
0
Example
example: compiles
project"demo"{tempo92master-seed424242}chord example stable@1.0 {
enabled on
type major
inversion 0}
Chorus chorus
Thickens the sound with a slowly moving copy.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
depth
text
5 ms
period
text
1500 ms
mix
text
50
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}chorus example stable@1.0 {
enabled on
depth 5 ms
period 1500 ms
mix 50
channels auto}
Compressor compressor
Evens out loud and soft parts. Connect a sound to key to make it a sidechain compressor (the key drives the reduction).
stable@1.2Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
key
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
threshold
text
-18 dB
ratio
text
4
attack
text
10 ms
release
text
120 ms
makeup
text
0 dB
keyFilter
text
0 Hz
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}compressor example stable@1.2 {
enabled on
threshold -18 dB
ratio 4
attack 10 ms
release 120 ms
makeup 0 dB
keyFilter 0 Hz
channels auto}
Convolution reverb convolution
Puts the sound in a real space: it is convolved with an impulse response (a WAV file of the project), for example a room, a plate or a street.
stable@1.0Effectsprovisional since 0.3
Inputs
Name
Type
Required
in
audio
no
Outputs
Name
Type
out
audio
Parameters
Name
Type
Default
enabled
text
on
ir
text
file
file
text
mix
text
30
predelay
text
0
length
text
3
level
text
0
normalize
text
on
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}convolution example stable@1.0 {
enabled on
ir file
mix 30
predelay 0
length 3
level 0normalizeon
channels auto}
Crossfade crossfade
Input a fades out while input b fades in over the same stretch, in bars or seconds. Equal power by default.
Keep a stretch of the input: from a start to an end, or only the first or the last seconds, once or repeated.
stable@1.0Effectsprovisional since 0.3
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
audio
audio
out
audio
Parameters
Name
Type
Default
enabled
text
on
start
text
end
text
first
text
last
text
loop
text
off
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}cut example stable@1.0 {
enabled on
loop off
channels auto}
De-esser deesser
Tames harsh s sounds in a voice.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
frequency
text
6000 Hz
threshold
text
-30 dB
ratio
text
4
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}deesser example stable@1.0 {
enabled on
frequency 6000 Hz
threshold -30 dB
ratio 4
channels auto}
Delay (echo) delay
Repeats the sound after a time.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
time
text
250 ms
feedback
text
35%
mix
text
30%
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}delay example stable@1.0 {
enabled on
time 250 ms
feedback 35%
mix 30%
channels auto}
Denoise denoise
Takes steady noise (hiss, room tone, rain on a recording) away: the noise level of every frequency band is estimated and subtracted.
stable@1.0Effectsprovisional since 0.3
Inputs
Name
Type
Required
in
audio
no
Outputs
Name
Type
out
audio
Parameters
Name
Type
Default
enabled
text
on
reduction
text
12
sensitivity
text
50
noise
text
auto
learn
text
0.5
learn_from
text
0
channels
text
auto
optimize
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}denoise example stable@1.0 {
enabled on
reduction 12
sensitivity 50
noise auto
learn 0.5
learn_from 0
channels auto
optimize auto}
Distortion distortion
Warm saturation up to heavy distortion; also useful to make a voice rougher.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
drive
text
12 dB
mode
text
soft
tone
text
8000 Hz
level
text
0 dB
mix
text
100%
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}distortion example stable@1.0 {
enabled on
drive 12 dB
mode soft
tone 8000 Hz
level 0 dB
mix 100%
channels auto}
Makes the sound dip at every hit of a pattern and come back, like sidechain compression keyed by the kick.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
depth
text
10 dB
release
text
160 ms
pattern
text
x...x...x...x...
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}duck example stable@1.0 {
enabled on
depth 10 dB
release 160 ms
pattern x...x...x...x...
channels auto}
Equalizer eq
Boost or cut the low, middle and high of a sound.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
low
text
0 dB
mid
text
0 dB
high
text
0 dB
low_hz
text
250 Hz
high_hz
text
3000 Hz
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}eq example stable@1.0 {
enabled on
low 0 dB
mid 0 dB
high 0 dB
low_hz 250 Hz
high_hz 3000 Hz
channels auto}
Fade fade
Fades a sound in or out, in bars or seconds, and ends in exact silence. The same as a volume gain with fadein(...) or fadeout(...).
stable@1.0Effectsprovisional since 0.3
Inputs
Name
Type
Required
in
audio
no
Outputs
Name
Type
out
audio
Parameters
Name
Type
Default
enabled
text
on
direction
text
out
start
text
1
length
text
2
shape
text
lin
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}fade example stable@1.0 {
enabled on
direction out
start 1
length 2
shape lin
channels auto}
Filter filter
Removes the highs (lowpass) or the lows (highpass).
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
type
text
lowpass
cutoff
text
1000 Hz
lfo_rate
text
off
lfo_depth
text
0 oct
lfo_shape
text
sine
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}filter example stable@1.0 {
enabled on
type lowpass
cutoff 1000 Hz
lfo_rate off
lfo_depth 0 oct
lfo_shape sine
channels auto}
Flanger flanger
A short moving delay with feedback (jet sweep).
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
depth
text
2 ms
period
text
2000 ms
feedback
text
50
mix
text
50
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}flanger example stable@1.0 {
enabled on
depth 2 ms
period 2000 ms
feedback 50
mix 50
channels auto}
Gate gate
Silences noise between phrases.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
threshold
text
-50 dB
attack
text
5 ms
hold
text
80 ms
release
text
150 ms
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}gate example stable@1.0 {
enabled on
threshold -50 dB
attack 5 ms
hold 80 ms
release 150 ms
channels auto}
General MIDI gm
A GM program from a project SoundFont 2 bank. Compact notes support per-note pan, fine pitch, release, cutoff and slide without a second attack.
stable@1.0Instrumentsstable since 0.1
Inputs
Name
Type
Required
notes
midi
yes
Outputs
Name
Type
audio
audio
warnings
text
render
render
Parameters
Name
Type
Default
file
text
samples/musescore-general/MuseScore_General.sf2
bank
number
0
program
number
0
channel
number
-1
gain
number
0
limiter
text
soft
level
text
auto
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}gm example stable@1.0 {
file samples/musescore-general/MuseScore_General.sf2
bank 0
program 0
channel -1
gain 0limiter soft
level auto
channels auto}
Granular granular
Cuts the sound into short windowed grains and scatters them along its length (with a little pitch variation): a longer, never repeating texture from a short sound.
stable@1.0Effectsprovisional since 0.3
Inputs
Name
Type
Required
in
audio
no
Outputs
Name
Type
out
audio
Parameters
Name
Type
Default
enabled
text
on
grain
text
120
density
text
40
scatter
text
60
pitch
text
0
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}granular example stable@1.0 {
enabled on
grain 120
density 40
scatter 60pitch0
channels auto}
Group (empty block) group
Blocks can contain blocks.
stable@1.0Controlprovisional since 0.1
Inputs
Name
Type
Required
in
any
no
Outputs
Name
Type
out
any
Parameters
None.
Example
example: compiles
project"demo"{tempo92master-seed424242}group example stable@1.0 {}
Guitar (samples) guitar
Plays recorded guitar notes from a sample folder of the project as a strummed chord pattern.
stable@1.0Instrumentsprovisional since 0.1
Inputs
Name
Type
Required
notes
midi
yes
Outputs
Name
Type
audio
audio
render
render
Parameters
Name
Type
Default
set
text
strum
text
D...D...D.u.D.u.
voicing
text
open
stagger
number
22
gain
number
0
humanize
number
0
patterns
text
song
text
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}guitar example stable@1.0 {
strum D...D...D.u.D.u.
voicing open
stagger 22
gain 0
humanize 0
channels auto}
Import audio import-audio
Hashed on import.
stable@1.0Dataprovisional since 0.1
Inputs
None.
Outputs
Name
Type
audio
audio
render
render
Parameters
Name
Type
Default
file
text
start
text
end
text
offset
text
loop
text
off
crossfade
text
fingerprint
text
content
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}import-audio example stable@1.0 {
loop off
fingerprint content
channels auto}
Join one after the other join
Puts the connected inputs one after the other (in1, then in2, then in3 ...) with a gap between them: spoken phrases with a pause between, hits in a row. The gap is seconds plus beats; the result is as long as all parts and the gaps together.
stable@1.0Effectsprovisional since 0.3
Inputs
Name
Type
Required
in1
audio
no
in2
audio
no
in3
audio
no
in4
audio
no
in5
audio
no
in6
audio
no
in7
audio
no
in8
audio
no
Outputs
Name
Type
audio
audio
out
audio
Parameters
Name
Type
Default
enabled
text
on
seconds
text
0.5
beats
text
0
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}join example stable@1.0 {
enabled on
seconds 0.5
beats 0
channels auto}
Limiter limiter
Keeps the sound under a ceiling.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
ceiling
text
-1 dB
release
text
100 ms
lookahead
text
5 ms
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}limiter example stable@1.0 {
enabled on
ceiling -1 dB
release 100 ms
lookahead 5 ms
channels auto}
Timed lyrics lyrics
Timed words kept in the linked lyrics file; this is the canonical source read by voice and music blocks.
stable@1.1Lyricsprovisional since 0.1
Inputs
None.
Outputs
Name
Type
words
text
Parameters
Name
Type
Default
lang
text
nl
timing
text
per-word
first5s
text
check
file
text
Example
example: compiles
project"demo"{tempo92master-seed424242}lyrics example stable@1.1 {
lang nl
timing per-word
first5s check
}
Master chain master
EQ, compressor, limiter. A slice can cut one part of the arrangement out (slice_from and slice_to, in bars; set it by dragging on the ruler of the Arrangement).
live@1.3Mixdynamicprovisional since 0.1
Inputs
Name
Type
Required
mix
audio
yes
Outputs
Name
Type
master
audio
Parameters
Name
Type
Default
limiter
text
-1 dB
target
text
-14 LUFS
slice_from
text
slice_to
text
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}master example live@1.3 {limiter-1 dB
target -14 LUFS
channels auto}
Meter meter
Passes the sound on unchanged and reports its peak, RMS, integrated loudness, crest factor and clipped samples as text (the report output; ample build prints it).
stable@1.0Effectsprovisional since 0.3
Inputs
Name
Type
Required
in
audio
no
Outputs
Name
Type
out
audio
report
text
Parameters
Name
Type
Default
enabled
text
on
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}meter example stable@1.0 {
enabled on
channels auto}
MIDI clip / chords midi-clip
Textual MIDI.
stable@1.1Instrumentsprovisional since 0.1
Inputs
None.
Outputs
Name
Type
notes
midi
Parameters
Name
Type
Default
ppq
number
1000000
chords
text
melody
text
notes
text
scale
text
none
bars
number
8
Example
example: compiles
project"demo"{tempo92master-seed424242}midi-clip example stable@1.1 {
ppq 1000000
scale none
bars 8}
MIDI humanize midi-humanize
A little variation in the velocity and start of every note.
stable@1.0MIDI effectsprovisional since 0.2
Inputs
Name
Type
Required
notes
midi
no
Outputs
Name
Type
notes
midi
Parameters
Name
Type
Default
enabled
text
on
velocity
text
10
timing
text
20
Example
example: compiles
project"demo"{tempo92master-seed424242}midi-humanize example stable@1.0 {
enabled on
velocity 10
timing 20}
MIDI to audio midi-render
Renders MIDI notes through a SoundFont with integer DSP.
stable@1.0Instrumentsstable since 0.1
Inputs
Name
Type
Required
sequence
midi-sequence
no
notes
midi
no
Outputs
Name
Type
audio
audio
warnings
text
render
render
Parameters
Name
Type
Default
file
text
pack:musescore-general
bank
text
source
program
text
source
gain
number
0
limiter
text
soft
track
text
all
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}midi-render example stable@1.0 {
file pack:musescore-general
bank source
program source
gain 0limiter soft
track all
channels auto}
MIDI file midi
Reads a bounded Standard MIDI File and emits its note events.
stable@1.0Datastable since 0.1
Inputs
None.
Outputs
Name
Type
notes
midi
sequence
midi-sequence
audio
audio
warnings
text
render
render
Parameters
Name
Type
Default
file
text
source-file
text
variant-file
text
variant-module
text
track
text
all
bars
number
8
limiter
text
soft
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}midi example stable@1.0 {
track all
bars 8limiter soft
channels auto}
Mixer mixer
Sums tracks. Drag the grip at the bottom right to add input lanes (4 to 16); lanes that are in use cannot be taken away.
stable@1.1Mixprovisional since 0.1
Inputs
Name
Type
Required
in1
audio
no
in2
audio
no
in3
audio
no
in4
audio
no
in5
audio
no
in6
audio
no
in7
audio
no
in8
audio
no
in9
audio
no
in10
audio
no
in11
audio
no
in12
audio
no
in13
audio
no
in14
audio
no
in15
audio
no
in16
audio
no
Outputs
Name
Type
mix
audio
Parameters
Name
Type
Default
gains
text
0 0 0 0
pan
text
c c c c
inputs
text
4
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}mixer example stable@1.1 {
gains 0000pan c c c c
inputs 4
channels auto}
Mood / scene mood-scene
A reusable musical scene and emotional direction for blocks that accept style data.
stable@1.0Directionprovisional since 0.1
Inputs
None.
Outputs
Name
Type
style
data
Parameters
Name
Type
Default
direction
text
Example
example: compiles
project"demo"{tempo92master-seed424242}mood-scene example stable@1.0 {}
Normalize normalize
Scales the sound to a target peak or a target integrated loudness (never past -0.1 dBFS): a very quiet recording needs no hand-found gain.
stable@1.0Effectsprovisional since 0.3
Inputs
Name
Type
Required
in
audio
no
Outputs
Name
Type
out
audio
Parameters
Name
Type
Default
enabled
text
on
mode
text
peak
peak
text
-1
loudness
text
-18
max_gain
text
48
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}normalize example stable@1.0 {
enabled on
mode peak
peak -1
loudness -18
max_gain 48
channels auto}
Pan pan
Places the sound left or right.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
pan
text
0
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}pan example stable@1.0 {
enabled onpan0
channels auto}
Phaser phaser
Moving notches in the sound (swirl).
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
stages
text
4
period
text
2500 ms
mix
text
50
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}phaser example stable@1.0 {
enabled on
stages 4
period 2500 ms
mix 50
channels auto}
Felt piano piano
Sampled piano, pinned by hash.
pinned@2.1Instrumentsprovisional since 0.1
Inputs
Name
Type
Required
notes
midi
yes
Outputs
Name
Type
audio
audio
render
render
Parameters
Name
Type
Default
set
text
felt-piano
velocity
text
natural
gain
number
0
humanize
number
0
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}piano example pinned@2.1 {
set felt-piano
velocity natural
gain 0
humanize 0
channels auto}
Pitch shift pitch
Moves the pitch up or down and keeps the length. The method matters: wsola is clean for instruments and sounds, voice keeps the character of a singing or speaking voice, granular is the simple first one.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
semitones
text
0
method
text
wsola
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}pitch example stable@1.0 {
enabled on
semitones 0
method wsola
channels auto}
Quantize quantize
Snaps the start of notes to a grid.
stable@1.0MIDI effectsprovisional since 0.2
Inputs
Name
Type
Required
notes
midi
no
Outputs
Name
Type
notes
midi
Parameters
Name
Type
Default
enabled
text
on
grid
text
1/16
strength
text
100
lengths
text
off
Example
example: compiles
project"demo"{tempo92master-seed424242}quantize example stable@1.0 {
enabled on
grid 1/16
strength 100
lengths off}
Reverb reverb
Puts the sound in a room.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
size
text
50%
decay
text
60%
mix
text
25%
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}reverb example stable@1.0 {
enabled on
size 50%
decay 60%
mix 25%
channels auto}
Reverse reverse
Plays the sound backwards: a reversed hit or cymbal makes a swell that lands on the beat.
stable@1.0Effectsprovisional since 0.3
Inputs
Name
Type
Required
in
audio
no
Outputs
Name
Type
out
audio
Parameters
Name
Type
Default
enabled
text
on
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}reverse example stable@1.0 {
enabled on
channels auto}
Sampler sampler
Plays one sample on the notes it receives, moved in pitch from a root note.
stable@1.0Instrumentsprovisional since 0.2
Inputs
Name
Type
Required
notes
midi
yes
Outputs
Name
Type
audio
audio
Parameters
Name
Type
Default
enabled
text
on
file
text
root
text
60
mode
text
oneshot
release
text
30 ms
gain
text
0 dB
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}sampler example stable@1.0 {
enabled on
root 60
mode oneshot
release 30 ms
gain 0 dB
channels auto}
Scorer: timing error scorer
Opt-in plugin (own model).
stable@1.0Controlplugin: timingprovisional since 0.1
Inputs
Name
Type
Required
audio
audio
yes
Outputs
Name
Type
score
data
Parameters
Name
Type
Default
limit
text
120 ms
Example
example: compiles
project"demo"{tempo92master-seed424242}scorer example stable@1.0 {
limit 120 ms
}
Scratch (DJ) scratch
Moves the sound like a record under the hand of a DJ: per step the record is pushed forward and pulled back, pulled back only, spun back or cut, so the sound squeals up and down in pitch.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
pattern
text
x.x.b...x.x.r...
depth
text
120 ms
bars
text
mix
text
100%
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}scratch example stable@1.0 {
enabled on
pattern x.x.b...x.x.r...
depth 120 ms
mix 100%
channels auto}
Send bus send
Sends a part of up to four sounds to a shared effect: through is the plain sum, send the sum with a send level per input.
stable@1.0Mixprovisional since 0.1
Inputs
Name
Type
Required
in1
audio
no
in2
audio
no
in3
audio
no
in4
audio
no
Outputs
Name
Type
through
audio
send
audio
Parameters
Name
Type
Default
levels
text
-12 -12 -12 -12
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}send example stable@1.0 {
levels -12-12-12-12
channels auto}
Sequencer sequencer
An advanced block: channels, patterns and a playlist from a .ampleseq file or kept in the song, mixed into one output and into up to eight lanes that name the channels they carry.
stable@1.0Advancedprovisional since 0.3
Inputs
None.
Outputs
Name
Type
mix
audio
lane1
audio
lane2
audio
lane3
audio
lane4
audio
lane5
audio
lane6
audio
lane7
audio
lane8
audio
Parameters
Name
Type
Default
file
text
lanes
text
gain
text
0 dB
sequence
text
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}sequencer example stable@1.0 {
gain 0 dB
channels auto}
Shift in time shift
Start the input a little later (or earlier): put it after one block to move only that block by some seconds or beats, without moving it on the timeline.
stable@1.0Effectsprovisional since 0.3
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
audio
audio
out
audio
Parameters
Name
Type
Default
enabled
text
on
seconds
text
0
beats
text
0
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}shift example stable@1.0 {
enabled on
seconds 0
beats 0
channels auto}
Stereo splitter splitter
Split stereo audio into independent left and right mono outputs. Mono input appears on both outputs.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
yes
Outputs
Name
Type
left
audio
right
audio
Parameters
Name
Type
Default
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}splitter example stable@1.0 {
channels auto}
Time stretch stretch
Plays the sound faster or slower, cut or padded to the same length.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
speed
text
100
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}stretch example stable@1.0 {
enabled on
speed 100
channels auto}
Synth synth
A small subtractive synth: oscillator, ADSR envelope, optional detune and a low-pass filter. Compact notes support per-note pan, fine pitch, release, cutoff and slide without a second attack.
stable@1.0Instrumentsprovisional since 0.2
Inputs
Name
Type
Required
notes
midi
yes
Outputs
Name
Type
audio
audio
Parameters
Name
Type
Default
enabled
text
on
wave
text
saw
attack
text
10 ms
decay
text
200 ms
sustain
text
70
release
text
250 ms
detune
text
0
cutoff
text
8000 Hz
gain
text
-6 dB
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}synth example stable@1.0 {
enabled on
wave saw
attack 10 ms
decay 200 ms
sustain 70
release 250 ms
detune 0
cutoff 8000 Hz
gain -6 dB
channels auto}
Transpose transpose
Moves every note up or down by semitones.
stable@1.0MIDI effectsprovisional since 0.2
Inputs
Name
Type
Required
notes
midi
no
Outputs
Name
Type
notes
midi
Parameters
Name
Type
Default
enabled
text
on
semitones
text
0
Example
example: compiles
project"demo"{tempo92master-seed424242}transpose example stable@1.0 {
enabled on
semitones 0}
Tremolo / autopan tremolo
Moves the level of the sound (tremolo) or its place between left and right (autopan), at a rate in Hz or locked to the tempo.
stable@1.0Effectsprovisional since 0.3
Inputs
Name
Type
Required
in
audio
no
Outputs
Name
Type
out
audio
Parameters
Name
Type
Default
enabled
text
on
mode
text
tremolo
rate
text
4
sync
text
off
depth
text
50
shape
text
sine
phase
text
0
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}tremolo example stable@1.0 {
enabled on
mode tremolo
rate 4
sync off
depth 50
shape sine
phase 0
channels auto}
Variants set variants
Make N takes, pick one.
stable@1.0Controlprovisional since 0.1
Inputs
Name
Type
Required
block
audio
yes
Outputs
Name
Type
pick
audio
Parameters
Name
Type
Default
count
number
20
vary
text
seed
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}variants example stable@1.0 {
count 20
vary seed
channels auto}
Spoken voice (voice clone) voice-clone
Speaks the text in a selected voice using a reference recording. The chosen voice sample is not connected to the song's audible mix.
live@0.1Voicesdynamicplugin: voice-cloneprovisional since 0.1
Inputs
None.
Outputs
Name
Type
audio
audio
render
render
Parameters
Name
Type
Default
file
text
text
text
model
text
xtts
language
text
nl
reference_text
text
auto_detect
text
off
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}voice-clone example live@0.1 {
model xtts
language nl
auto_detect off
channels auto}
Volume / channels volume
Adjust gain, mute either channel or fold audio to mono.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
mixed
audio
left
audio
right
audio
render
render
out
audio
Parameters
Name
Type
Default
enabled
text
on
gain
text
0 dB
left_gain
text
0 dB
right_gain
text
0 dB
linked
text
on
left
text
on
right
text
on
mono
text
off
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}volume example stable@1.0 {
enabled on
gain 0 dB
left_gain 0 dB
right_gain 0 dB
linked on
left on
right on
mono off
channels auto}
Width width
Widens or narrows the stereo image.
stable@1.0Effectsprovisional since 0.2
Inputs
Name
Type
Required
in
audio
no
render_in
render
no
Outputs
Name
Type
out
audio
render
render
Parameters
Name
Type
Default
enabled
text
on
width
text
100
channels
text
auto
Example
example: compiles
project"demo"{tempo92master-seed424242}width example stable@1.0 {
enabled onwidth100
channels auto}
Diagnostics
Every diagnostic code, generated from spec/diagnostics.json. ample explain CODE prints the same text. Codes are never reused.
AMP2020 An effect has active audio and render inputs
errorstaticsince 0.1
An effect consumes existing audio through in or an isolated source render recipe through render_in. Both connections may be present when at least one is disabled.
Wrong
example: AMP2020
eq tone stable@1.0 {fromdrums.audio -> in fromdrums.render -> render_in }
Right
eq tone stable@1.0 {fromdrums.audio -> in fromdrums.render -> render_in disabled}
AMP1001 Missing project line
errorstaticsince 0.1
Every song starts with a project line that names it and sets the tempo and the master seed.
Wrong
example: AMP1001
features{lyricson}
Right
project"my-song"{tempo92master-seed424242}
AMP1002 Tempo out of range
errorstaticsince 0.1
The tempo must be between 20 and 300 beats per minute.
Wrong
example: AMP1002
project"x"{tempo999master-seed1}
Right
project"x"{tempo120master-seed1}
AMP1003 Malformed project line
errorstaticsince 0.1
The project line has a fixed shape: a quoted name, then tempo and master-seed in braces.
Wrong
example: AMP1003
project my-song tempo92
Right
project"my-song"{tempo92master-seed424242}
AMP1004 Unknown module
errorstaticsince 0.1
features lists the opt-in modules: quality, credits, signing, secrets, lyrics, git-mirror.
Wrong
example: AMP1004
features{ qualty on}
Right
features{ quality on}
AMP1005 Module value
errorstaticsince 0.1
A module is either on or off.
Wrong
example: AMP1005
features{ quality maybe }
Right
features{ quality on}
AMP1006 Assist level
errorstaticsince 0.1
assist is one of off, diff, button, auto.
Wrong
example: AMP1006
features{assist always }
Right
features{assistdiff}
AMP1007 Unknown lint rule
errorstaticsince 0.1
The lint line sets severities for rules that exist (see the Lint rules page).
Wrong
example: AMP1007
lint{ L099 error}
Right
lint{ L004 error}
AMP1008 Lint level
errorstaticsince 0.1
A rule level is off, info, warning or error.
Wrong
example: AMP1008
lint{ L004 loud }
Right
lint{ L004 error}
AMP1009 Master seed out of range
errorstaticsince 0.1
The master seed must be an unsigned 64-bit whole number.
A line must be a comment, a project, features, lint or layout line, a block header ending with {, or inside a block.
Wrong
example: AMP1100
drumsstable@1.2
Right
drumsdrumsstable@1.2 {}
AMP1101 Block not closed
errorstaticsince 0.1
Every block opened with { must be closed with } on its own line.
Wrong
example: AMP1101
drums d stable@1.2 {
kit kit/tape
Right
drums d stable@1.2 {
kit kit/tape
}
AMP1102 Bad block header
errorstaticsince 0.1
A block header has a type, a name and class@version: <type> <name> <class>@<version> [at ...] {
Wrong
example: AMP1102
drums{
Right
drums d stable@1.2 {
AMP1103 Bad class or version
errorstaticsince 0.1
The class is stable, pinned or live; the version is dotted numbers.
Wrong
example: AMP1103
drums d fixed@1 {
Right
drums d stable@1.2 {
AMP1104 Bad placement
errorstaticsince 0.1
Placement reads: at <bar> for <bars> [as <name>], several separated by commas.
Wrong
example: AMP1104
drums d stable@1.2 at3{
Right
drums d stable@1.2 at3for8as intro {
AMP1105 Unknown word in header
errorstaticsince 0.1
After the placement only bake, lock, refresh, disabled and freeze seeds|plan are allowed before {.
Wrong
example: AMP1105
drums d stable@1.2 freeze{
Right
drums d stable@1.2 lock{
AMP1106 Bar number or length
errorstaticsince 0.1
Bars start at 1 and a part is at least 1 bar long.
Wrong
example: AMP1106
at0for4
Right
at1for4
AMP1107 Bad from line
errorstaticsince 0.1
A connection reads: from <block>.<output> -> <input>.
Wrong
example: AMP1107
from chords -> notes
Right
from chords.notes -> notes
AMP1108 Bad parameter line
errorstaticsince 0.1
A parameter is a name followed by its value.
Wrong
example: AMP1108
kit
Right
kit kit/tape
AMP1109 Duplicate parameter
errorstaticsince 0.1
A parameter can be given once per block.
Wrong
example: AMP1109
kit a
kit b
Right
kit a
AMP1110 Bad use line
errorstaticsince 0.1
A module instance is written: use <module>[@version] as <name> {
Wrong
example: AMP1110
use groove {
Right
use groove@1.0 as intro {
AMP1112 Settings line
errorstaticsince 0.1
The settings line holds plugin settings of this song as pairs of plugin.name and a value, for example settings { acestep.synthModel sft }. Text with spaces goes in quotes.
Wrong
example: AMP1112
settings { synthModel sft }
Right
settings { acestep.synthModel sft }
AMP2001 Unknown block type
errorstaticsince 0.1
The type must exist in the library (see Block types).
Wrong
example: AMP2001
drumz d stable@1.2 {
Right
drums d stable@1.2 {
AMP2002 Duplicate block name
errorstaticsince 0.1
Block names are unique within the song.
Wrong
example: AMP2002
drums a ... {}bass a ... {}
Right
drums a ... {}bass b ... {}
AMP2003 Unknown parameter
errorstaticsince 0.1
A block only accepts the parameters of its type (listed in the hint).
Wrong
example: AMP2003
patern beat-A
Right
pattern beat-A
AMP2004 No such block
errorstaticsince 0.1
A from line must name a block that exists in the song.
Wrong
example: AMP2004
from chordz.notes -> notes
Right
from chords.notes -> notes
AMP2005 No such output
errorstaticsince 0.1
The named output does not exist on that block.
Wrong
example: AMP2005
from chords.note -> notes
Right
from chords.notes -> notes
AMP2006 No such input
errorstaticsince 0.1
The named input does not exist on this block.
Wrong
example: AMP2006
from chords.notes -> note
Right
from chords.notes -> notes
AMP2007 Type mismatch
errorstaticsince 0.1
Ports only connect when their types agree (audio to audio, midi to midi, text to text, data to data; any matches everything).
Wrong
example: AMP2007
fromdrums.audio -> chorus_vox.lyrics
Right
fromlyrics.words ->lyrics
AMP2008 Input already connected
errorstaticsince 0.1
An input takes one source. Use a mixer to combine several.
Wrong
example: AMP2008
two from lines to one input
Right
one from line per input
AMP2009 Block reads itself
errorstaticsince 0.1
A block cannot be its own source.
Wrong
example: AMP2009
frombass.audio -> notes (inside bass)
Right
read from another block
AMP2010 Cycle
errorstaticsince 0.1
Blocks may not depend on each other in a loop; the path is shown in the message.
Wrong
example: AMP2010
a reads b, b reads a
Right
break the loop
AMP2011 Bad seed value
errorstaticsince 0.1
A seed is the word derived or an unsigned 64-bit whole number from 0 to 18446744073709551615.
Wrong
example: AMP2011
lm_seed -5
Right
lm_seed 18446744073709551615
AMP2013 A song must state a required plugin setting
warningruntimesince 0.1
The song uses a plugin that wants every song to state some settings itself (for the ACE-Step plugin: acestep.backend, how the sound is made). The editors offer a quick fix that writes the project default into the settings line. Without it the block fails to build with AMP5370.
Right
settings { acestep.backend server }
AMP2012 Invalid structured MIDI notes
errorstaticsince 0.1
A midi-clip notes parameter must contain valid bar:beat:length:pitch:velocity entries within the supported integer precision and one 4/4 bar.
Wrong
example: AMP2012
notes 1:4:1:60:96
Right
notes 1:3:1:60:96
AMP5501 MIDI file unavailable or invalid
errorruntimesince 0.1
The MIDI block requires a project-owned, bounded Standard MIDI File that can be parsed safely.
Wrong
example: AMP5501
midi source stable@1.0 { file ../outside.mid }
Right
choose a valid .mid file inside the project
AMP5502 MIDI timing cannot be represented exactly
errorruntimesince 0.1
A note event falls between the supported six-decimal beat positions.
Wrong
example: AMP5502
a note position not divisible into microbeats
Right
use MIDI note positions representable at six decimal places
AMP5503 MIDI track is unavailable
errorruntimesince 0.1
The selected track index does not exist in this MIDI file.
Wrong
example: AMP5503
track 5for a file with one track
Right
track 0 or track all
AMP5504 MIDI module conversion cannot be completed
errorruntimesince 0.1
The generated module name, structure or supported size limits are invalid.
Wrong
example: AMP5504
a module name containing a path separator
Right
a short path-safe module name such as midi-import
AMP2015 Invalid MIDI track selector
errorstaticsince 0.1
The midi block track parameter must be all or a nonnegative zero-based track index.
Wrong
example: AMP2015
track first
Right
track all
AMP2021 Number outside the range of the parameter
warningstaticsince 0.3
A numeric parameter (or a point of an auto(...) curve) is outside the range its template declares. The build cuts it to the nearest limit without a word, so the sound is not what the number says.
Wrong
example: AMP2021
gain 54on a volume block (the range is -60 to +48 dB)
Right
gain 48, or two volume blocks in a row
AMP5001 Plugin not installed
warningstaticsince 0.1
The block needs an opt-in plugin that is not installed. AMPLE reports it instead of skipping the block.
Wrong
example: AMP5001
scorer s stable@1.0 {
Right
install the plugin, or remove the block
AMP5002 Part past bar 1024
warningstaticsince 0.1
Very long parts are probably a typo.
Wrong
example: AMP5002
at1for5000
Right
at1for500
AMP5101 Regeneration is only guaranteed on this machine
inforuntimesince 0.1
Careful: regeneration is only guaranteed to give the same result on this machine with the same plugin, model and GPU setup. On another machine, driver or version it may sound different. Your current take is kept safe either way. See docs/regeneration.md.
AMP5102 Toolprint differs or determinism unverified
warningruntimesince 0.1
The toolprint of this machine differs from the one that made the take, or the plugin determinism is unverified, so a regeneration may not reproduce the take. Keep the stored take; regeneration verifies the result and defaults to restoring the previous take.
AMP5201 Path leaves the project
errorruntimesince 0.1
A project path must be relative and stay inside the project (or a registered link). Absolute paths and paths that climb out with .. are refused.
AMP5202 Invalid path or name
errorruntimesince 0.1
The path is empty, contains characters that are not allowed, has a name ending in a dot or a space, or uses a name that is reserved on Windows (CON, NUL, COM1 and so on).
AMP5203 Link leads outside the project
errorruntimesince 0.1
A folder or file inside the project is a symbolic link or junction whose target lies outside the project and outside every registered link.
AMP5204 Project is open elsewhere
errorruntimesince 0.1
Only one AMPLE instance may write to a project. The message says who holds the lock. Open the project read-only or close the other instance; a held lock is never broken automatically.
AMP5205 project.json is not valid
errorruntimesince 0.1
The project file is missing a field or is not valid JSON, so the project cannot be opened. Restore it from a backup or from .ample/backups.
AMP5206 Project made by a newer AMPLE
errorruntimesince 0.1
The project format version is newer than this AMPLE understands. Open the project with a newer AMPLE.
AMP5207 No migration available
errorruntimesince 0.1
The project uses an old format for which this AMPLE has no upgrade step.
AMP5208 Folder is already a project
errorruntimesince 0.1
A new project cannot be created in a folder that already has an .ample/project.json.
AMP5209 Project needs an upgrade
warningruntimesince 0.1
The project uses an older format. Opening it for writing upgrades it after making a backup copy of the old project file; a read-only open does not upgrade.
AMP5210 Not an AMPLE project
errorruntimesince 0.1
The folder has no .ample/project.json. A project is a folder with a mandatory .ample folder.
AMP5211 Project folder missing
warningruntimesince 0.1
A folder AMPLE expects inside .ample is missing. It can be created again; nothing is lost by that.
AMP5212 Unknown entry in .ample
inforuntimesince 0.1
There is a file or folder inside .ample that AMPLE does not create. It is left alone.
AMP5213 Leftover temporary file
warningruntimesince 0.1
A temporary file from an interrupted write was found. The real file is intact; the leftover can be deleted.
AMP5214 Project lock is held
inforuntimesince 0.1
Another AMPLE instance has the project open for writing. The heartbeat tells who and when it was last seen.
AMP5215 Old lock information
inforuntimesince 0.1
Nobody holds the project lock, but a heartbeat of an earlier holder remains. The lock is free to take.
AMP5216 Less free disk than the black box budget
warningruntimesince 0.1
The volume has less free space than the black box budget in the project settings. The journal may fill the disk before it is thinned.
AMP5220 A take needs a stored plan
errorruntimesince 0.1
The Always rule: a take can only be created after its plan is stored. The takebank refuses a take whose plan is not in the database, also when the database is used directly.
AMP5221 Data is not on disk
errorruntimesince 0.1
A stored blob listed by a take is missing. The take and its plan are still there: re-render the take from its plan (when the plugin is reproducible) or restore the data from a backup.
AMP5223 Unknown take, plan or data id
errorruntimesince 0.1
There is no take, plan or data with that id, an id prefix matches more than one, or the id is not of the form sha256: followed by 64 hex digits.
AMP5224 Plans and takes are immutable
errorruntimesince 0.1
Plans, takes and their data references are written once. They cannot be changed or deleted; dropping a take is a tombstone.
AMP5225 Plan is not valid
errorruntimesince 0.1
A plan has an unknown intent or schema, or its settings or toolprint are not valid JSON.
AMP5230 Black box could not be written
errorruntimesince 0.1
Every change is written to the black box journal before it is made. When the journal cannot be written (disk full, no access) the command is not run: the black box fails closed.
AMP5231 Nothing to restore
errorruntimesince 0.1
The black box record does not exist, is not a start record, or has no pre-image that fits inside the journal (very large files are only noted by size).
AMP5232 Dangerous act not accepted
errorruntimesince 0.1
A dangerous act needs the exact acceptance text typed by the user and a reason. Nothing was changed.
AMP5226 Data belongs to a take
errorruntimesince 0.1
A stored blob that a take uses is never removed by history cleanup. Takes are dropped with a tombstone, and their audio only through a separate dangerous act.
AMP5240 Tag is locked
errorruntimesince 0.1
A tag exists and is locked. Moving or removing it is a dangerous act: dry run, typed acceptance, reason and a tombstone.
AMP5241 Line name taken or protected
errorruntimesince 0.1
A line with that name already exists, or the line is studio (the real version), which cannot be dropped.
AMP5242 Line is dropped or in use
errorruntimesince 0.1
The line is dropped (restore it first), or it is the line you are working on and cannot be dropped (go to another line first).
AMP5243 Unknown line, tag or node
errorruntimesince 0.1
There is no line, tag or history node with that name, or a node prefix matches more than one.
AMP5244 History is immutable
errorruntimesince 0.1
History nodes are written once and are never changed or deleted. Cleanup only marks the content of unreachable nodes as reclaimed.
AMP5245 Cleanup plan is out of date
errorruntimesince 0.1
Something became reachable after the dry run, so the cleanup was refused. Run the dry run again.
AMP5250 Blocks form a cycle
errorruntimesince 0.1
The build engine found blocks that depend on each other in a loop, so there is no order in which to render them.
AMP5251 No renderer for this block
warningruntimesince 0.1
The block type has no renderer yet (AI voices and other plugin blocks need their plugin), or an input comes from a block that has no take. The block is skipped and the build goes on.
AMP5252 Could not mark the block as baked
errorruntimesince 0.1
The block was rendered but the song source could not be edited to add the bake flag (for example the block is not in the file).
AMP5253 Block implicitly disabled by a disabled source
warningruntimesince 0.2
A required input of this block comes from a block that is disabled, so this block has nothing to work on and is disabled with it. The build goes on without it. Optional inputs from a disabled block are simply left out.
AMP5254 This block is disabled
warningruntimesince 0.2
The block has the disabled flag, so it produces no take. Remove the flag to build it again.
AMP5255 An effect has no input to work on
errorruntimesince 0.2
An effect that accepts a render recipe needs an audio input (in) or a render input (render_in) at build time.
AMP5256 A drum voice comes from the fallback kit
warningruntimesince 0.2
The kit does not have a sample for a voice used in the pattern, so the sample of the fallback kit plays instead. Add the voice to the kit, map it with the map parameter, or accept the fallback.
AMP5371 An automation expression cannot be read
errorruntimesince 0.2
A value that changes over the song is written auto(bar:value, bar:value, ...): whole bar numbers from 1, a colon and a whole number. The volume gain is in dB. A fade is written fadein(start, length, shape) or fadeout(start, length, shape): a start and a length in bars (5, 5.5) or seconds (12s, 1.5s), the shape lin, equal or exp, and it works on a volume gain and an effect mix only.
Wrong
example: AMP5371
gain auto(1,5:0)
Right
gain auto(1:-60,5:0,33:0,37:-60)
AMP5289 The project tempo differs from the tempo of a MIDI file
warningruntimesince 0.2
The notes of a MIDI file follow its own tempo, the bars of the song follow the project tempo. When they differ the notes slide against the bars and a note that goes on over a bar line is cut in two. Set the project tempo to the tempo of the file.
AMP5260 Seed is locked
errorruntimesince 0.1
Seeds are locked by default. Changing one probably makes reproduction of earlier takes impossible, so it needs an explicit unlock; every change is kept in the seed history.
AMP5261 Nothing to regenerate from
errorruntimesince 0.1
The block has no selected take (build it and select a take first), or a variant could not be made.
AMP5262 The selected take cannot be discarded
errorruntimesince 0.1
Select another take for the block first; the selected take is the one the song uses.
AMP5263 No such open decision
errorruntimesince 0.1
There is no open regeneration decision with that id (it was already answered), or the answer is not restore or keep-variant. Replacing the take needs the dangerous-act contract.
AMP5270 Plugin manifest is not valid
errorruntimesince 0.1
plugin.json is missing a required field or has a wrong value. The message lists every problem found.
AMP5271 Plugin protocol not supported
errorruntimesince 0.1
The plugin needs a newer plugin protocol than this AMPLE speaks, or it did not answer the handshake with protocol version 1.
AMP5272 Plugin output missing or outside its job folder
errorruntimesince 0.1
A plugin may only write inside its private job folder. It named an output outside it, named a file it did not write, or finished without any output.
AMP5273 Plugin sent invalid output
errorruntimesince 0.1
The plugin printed something to its output that is not a JSON-RPC message, so it was stopped.
AMP5274 Plugin output too large
errorruntimesince 0.1
A message or an output file was larger than the limit, so the plugin was stopped or its output refused.
AMP5275 Plugin crashed
errorruntimesince 0.1
The plugin process ended while a request was open. The render is recorded as a failed attempt; no take was made and the plan is kept.
AMP5276 Plugin timed out
errorruntimesince 0.1
The plugin did not answer within the time allowed and was stopped.
AMP5277 Plugin could not be started
errorruntimesince 0.1
The program named in the manifest could not be started, or the plugin does not support this operating system.
AMP5278 Plugin reported an error
errorruntimesince 0.1
The plugin answered a request with an error, for example a missing model. The message is the plugin's own.
AMP5279 Plugin settings are not valid
errorruntimesince 0.1
The settings do not match the settings schema of the plugin. The message lists what is wrong.
AMP5280 The document changed
errorruntimesince 0.1
The song was changed by someone else (another window or an edit in between) since you loaded it. Reload it and apply your change again.
AMP5281 No open dry run for this confirmation
errorruntimesince 0.1
A dangerous act can only be confirmed with the token of a dry run that is still open (one use, ten minutes). Start the act again.
AMP5282 Host not allowed
errorruntimesince 0.1
The server only answers requests addressed to localhost or 127.0.0.1. Use the address that ample serve printed, or start it with --allow-lan on a trusted network.
AMP5283 Origin refused
errorruntimesince 0.1
A web page from another address tried to call the local server. It is refused so that no other site can drive your project.
AMP5284 Session token missing or wrong
errorruntimesince 0.1
Every API call needs the session token that ample serve printed, in the header X-Ample-Token.
AMP5285 Request not valid
errorruntimesince 0.1
A field is missing or has the wrong type. The message names it.
AMP5286 Edit refused
errorruntimesince 0.1
The graph edit cannot be applied, for example an unknown block or a name that is already used. The message says why.
AMP5287 The song has errors
errorruntimesince 0.1
The song has errors and cannot be built. Fix the diagnostics first.
AMP5288 A plugin render takes long
warningruntimesince 0.2
A plugin block has been rendering for more than five minutes. This is only a notice in the log: the render goes on until the time limit (for ACE-Step the setting timeoutS, 900 seconds by default, at most 3600; the host adds a minute so the plugin's own message comes first).
AMP5290 A linked folder is not reachable
errorruntimesince 0.1
The folder of a linked catalog is not there (a drive that is not connected, a folder that was renamed or moved). Connect the drive, or relocate the link to the new place; no song needs to change.
AMP5291 Asset not found
errorruntimesince 0.1
The asset is not in the project, in its linked folders, in the user library or in the packs, or its file is gone from the folder. Link the folder that holds it, relocate, or restore the file.
AMP5292 Not the same catalog
errorruntimesince 0.1
The folder you pointed at does not carry the marker of that catalog, has another content fingerprint and does not hold most of its files. Check the folder, or relocate with force if you know it is the right one.
AMP5293 Read-only linked folder
errorruntimesince 0.1
A linked folder that is read-only is never written to (by AMPLE or by a plugin) and cannot be an output folder. Make the link read-write first, if you really want that.
AMP5294 Asset changed
errorruntimesince 0.1
The file is there but its content is not the one that was pinned in ample.lock. Accept the change (ample lock update) or restore the pinned file from the catalog or a backup.
AMP5295 Pack manifest not valid
errorruntimesince 0.1
pack.json is missing or has no name and version, or the name uses characters that are not allowed.
AMP5296 ample.lock is damaged
errorruntimesince 0.1
The lock file cannot be read, or there is none. It is written by ample lock update; do not edit it by hand.
AMP5297 --allow-all needs --login
errorruntimesince 0.1
Listening on every address with any host name is only allowed together with --login; otherwise the launch token would be the only protection.
AMP5298 Sign-in not set up
errorruntimesince 0.1
--login needs a stored user and password first: run ample auth set-password --user NAME.
AMP5299 Sign-in refused
errorruntimesince 0.1
Wrong user or password, or too many wrong attempts (locked for a minute).
AMP5300 Not enough room for the backup
errorruntimesince 0.1
The backup target has less free space than this backup needs. Nothing was written. Free space or choose another target.
AMP5301 Backup cannot be restored
errorruntimesince 0.1
The folder is not empty, or a stored file of the backup is missing or damaged (its hash is not the recorded one). A restore goes into a new folder, and nothing is overwritten with damaged data.
AMP5302 Archive not valid
errorruntimesince 0.1
The archive is not an AMPLE archive, has a format version this AMPLE does not read, is missing a file, or a file does not match its recorded hash. Nothing was imported.
AMP5303 Backup not found
errorruntimesince 0.1
There is no backup with that id in the target, or the start of the id matches several. Give more of the id (ample backup list).
AMP5304 SFZ region has no sample
warningruntimesince 0.1
An SFZ region did not name a sample and was skipped. Add a valid sample opcode to the region or remove the empty region.
AMP5305 Backup before the act failed
errorruntimesince 0.1
You asked for a backup before every dangerous act, and that backup failed, so the act did not run. Fix the backup target or turn the option off.
AMP5306 Invalid SFZ opcode value
warningruntimesince 0.1
An SFZ opcode value could not be interpreted and was ignored. Correct the reported opcode value in the SFZ mapping.
AMP5310 Encrypted data cannot be opened
errorruntimesince 0.1
The project (or a backup) is encrypted and the passphrase is missing or wrong, or a sealed record or file is damaged. Give the passphrase in AMPLE_PASSPHRASE. If it is lost, the recovery kit holds the key.
AMP5311 Vault or passphrase problem
errorruntimesince 0.1
The vault cannot be opened (wrong passphrase, damaged file, no vault yet) or a secret name or value is not acceptable. Passphrases and secret values are never taken from arguments.
AMP5312 Signature check failed
errorruntimesince 0.1
A signature does not match: the signed record or file was changed, the signature log was edited, records were cut off, or the signer is not the key you trust.
AMP5313 Signing key problem
errorruntimesince 0.1
There is no active signing key, the key file is damaged, or the passphrase does not open the private key. Make one with ample keys generate.
AMP5314 Encryption setting does not fit
errorruntimesince 0.1
Encryption is already on (or not on), or a backup folder holds backups sealed with another key. Use another folder for this project.
AMP5320 Module not found
errorruntimesince 0.1
The song uses a module that is not in the project (modules/), its linked folders or the installed packs. Link the folder that holds it or add the file.
AMP5321 Module version does not fit
errorruntimesince 0.1
The use asks for a version the module file does not satisfy: it needs the same major version and a minor version that is not older.
AMP5322 Unknown module parameter
errorruntimesince 0.1
The use sets a parameter the module does not declare.
AMP5323 Module input problem
errorruntimesince 0.1
The use connects an input the module does not have, or leaves an input of the module unconnected.
AMP5324 Unknown placeholder in a module
errorruntimesince 0.1
A value in the module uses $name but the module does not declare the parameter name.
AMP5325 Module has no such output
errorruntimesince 0.1
A block reads an output that the module instance does not offer, or the module points an output at a block that is not in it.
AMP5326 Module file is damaged
errorruntimesince 0.1
The module file lacks its module line or has a declaration (param, input, output) that cannot be read.
AMP5327 Modules loop or nest too deep
errorruntimesince 0.1
Module instances depend on each other in a loop, or are nested more than eight deep.
AMP5328 Module changed since it was pinned
errorruntimesince 0.1
ample.lock pins this module by hash and the file now has other content. Review it, then run ample lock update to accept it.
AMP5330 Quality rule failed
warningruntimesince 0.1
A take does not pass a quality rule (clipping, true peak, loudness, silence, DC offset, sample rate). Advisory: it never blocks saving unless the project policy requires the rule for a tag or promote.
AMP5331 Quality check could not run
errorruntimesince 0.1
The take has no audio, the audio is not 16-bit PCM WAV, or the quality policy file (.ample/quality.json) is damaged.
AMP5332 Credit or licence warning
warningruntimesince 0.1
A library asset in use has a licence that may not fit the project (for example non-commercial in a release), no licence at all, or the credits file is damaged. Advisory, not legal advice.
AMP5333 Quality policy stops this
errorruntimesince 0.1
The project policy requires rules to pass before a tag or a promote and the newest take of the master block does not pass them (or there is none). Fix the sound or change .ample/quality.json.
AMP5340 Analysis plugin needs permission
errorruntimesince 0.1
The analysis plugin wants the GPU or the network and was not allowed to. Nothing was started. Allow it on purpose (for example ample analyze --allow-gpu).
AMP5341 Unknown analyser
errorruntimesince 0.1
There is no analyser of that name. Run ample analyze --list.
AMP5342 Take cannot be analysed
errorruntimesince 0.1
The take has no audio, or its audio is not 16-bit PCM WAV, or no take was given.
AMP5343 Analysis result does not fit its schema
errorruntimesince 0.1
An analyser or plugin returned a result that breaks spec/schema/analysis-<kind>.schema.json, so it was not stored.
AMP5344 Analysis plugin problem
errorruntimesince 0.1
The plugin is not of kind analysis, does not offer that analysis, or did not answer the analyze request with a result.
AMP5350 Sample set problem
errorruntimesince 0.1
A drum kit or guitar block names a sample folder that is missing, holds no WAV files, lacks a file the pattern needs (for example tom.wav), or holds a file that cannot be played (only PCM or float WAV, one or two channels).
AMP5351 Pattern, strum or voicing not understood
errorruntimesince 0.1
A sample drum pattern is not a name or a list of voice:steps grids, a grid or strum holds characters that are not allowed, or the guitar voicing is not open, triad or power.
AMP5360 The server hit an unexpected error
errorruntimesince 0.1
Something failed inside the server that no rule foresaw. The message names the kind of error; the window that runs ample serve has the full details. If it repeats, write down what you did and report it.
AMP5370 A required plugin setting is missing in this song
errorruntimesince 0.1
A plugin marked a setting as required and this song has no value for it. Plugin settings belong to the song; the project-level settings are only the defaults for new songs. Use the quick fix to copy the project default into the song (the settings line), or set it in the plugin settings of this song.
The joined inputs and gaps would produce more than ten minutes of audio. Shorten the inputs or reduce the number or length of gaps.
AMP5352 Unknown limiter option
errorruntimesince 0.1
This renderer accepts only the soft limiter or off. Choose one of those values for the limiter parameter.
Wrong
example: AMP5352
gmpianostable@1.0 {limiter fast }
Right
gmpianostable@1.0 {limiter soft }
AMP5380 Sequencer data cannot be used
errorruntimesince 0.3
The sequencer file, embedded sequence, lane assignment, channel data or referenced sample could not be read or used. Correct the reported field or provide a valid project file.
AMP5390 No blocks selected for the bundle
errorruntimesince 0.1
Choose at least one block before saving a bundle.
AMP5391 Bundle not found
errorruntimesince 0.1
The requested bundle is not present in the library. Check its name or add the bundle to the library first.
AMP5392 Invalid block name in bundle operation
errorruntimesince 0.1
A block name may contain only ASCII letters, digits, underscores and hyphens.
AMP5393 Bundle conflicts with existing block versions
errorruntimesince 0.1
The project already contains different versions of one or more bundle blocks. Choose how to place or resolve those conflicts before continuing.
AMP5394 SFZ opcode not read
warningruntimesince 0.3
An .sfz sample set used an opcode this version does not read yet, or a <region> could not be built (no sample, or no key/lokey/pitch_keycenter). The opcode's value is kept in the file and not applied; the region is skipped if it could not be built at all. See docs/samples.md "SFZ sets" for what is read today.
AMP5395 SFZ file problem
errorruntimesince 0.3
An .sfz sample set is missing, cannot be parsed (a bad #include, an unclosed header), references a sample or #include that does not exist or that lies outside the SFZ file's own folder, or defines no usable (pitched) region.
AMP5420 Duplicate path in package
errorruntimesince 0.1
A package cannot contain multiple entries with the same project-relative path. Create a package with unique paths.
AMP5421 Package is incomplete
errorruntimesince 0.1
The .ampletx data ended before the complete package was read. Recreate or copy the package again.
AMP5422 Package format is newer than this AMPLE version
errorruntimesince 0.1
This package uses a format version newer than this AMPLE can read. Open it with a compatible newer AMPLE version.
AMP5423 Package file failed its integrity check
errorruntimesince 0.1
A file's size or SHA-256 does not match the package manifest, so the package may be damaged. Obtain a valid copy of the package.
AMP5430 Save the song before exporting a package
errorruntimesince 0.1
Package export uses the saved song. Save the song and retry the export.
AMP5431 Song has no master output to export
errorruntimesince 0.1
Add or connect a master block to define the final output before exporting.
AMP5432 Final output has blocks that failed to build
errorruntimesince 0.1
Every block in the requested final output must build successfully before the song can be exported. Resolve the reported build failures and retry.
AMP5433 Derived export boundary has no audio
errorruntimesince 0.1
The selected boundary does not provide renderable audio and cannot be used to create a derived song.
AMP5434 Resolve derived export conflicts first
errorruntimesince 0.1
The derived export has conflicts that must be reviewed and resolved before the package can be created.
AMP5435 Export package already exists
errorruntimesince 0.1
An existing package is never overwritten. Choose a different package path.
AMP5441 Package not found at the selected path
errorruntimesince 0.1
No package exists at the selected path. Choose an existing .ampletx package.
AMP5442 Package has no master preview
errorruntimesince 0.1
This package does not include a master preview that can be imported as audio.
AMP5443 Import destination is not an AMPLE project
errorruntimesince 0.1
Choose an existing AMPLE project folder as the package import destination.
AMP5444 Package cannot be imported in this environment
errorruntimesince 0.1
The package has one or more compatibility differences that prevent import in this environment. Review the reported differences and use a compatible environment or package.
AMP5445 Package import would overwrite different project content
errorruntimesince 0.1
The destination already contains different content at a package path. Use the conversion flow or import into a new project.
AMP5450 Codex identity is not allowed by this server
errorruntimesince 0.1
This server does not permit the selected Codex identity and does not silently replace it. Choose an allowed identity or ask the administrator to change the server policy.
AMP5451 Acknowledge AI agent risks first
errorruntimesince 0.1
Starting an AI coding agent requires explicit acknowledgement of its risks before the task can begin.
AMP5452 Project Codex identity is not linked
errorruntimesince 0.1
Link a Codex identity to this project in project settings before starting a Codex task.
AMP5453 Codex executable not found
errorruntimesince 0.1
Install Codex or configure its executable path so AMPLE can start it.
AMP5454 Codex request failed
errorruntimesince 0.1
The Codex request failed. Review the reported error and retry the task when the underlying problem is resolved.
AMP5455 Assistant proposal is still being checked
errorruntimesince 0.1
Wait for proposal validation to finish, or explicitly accept it anyway if that is appropriate.
AMP5456 Project changed while Codex was working
errorruntimesince 0.1
The proposal is based on project files that changed during the task. Rebase or review the proposal against the current project before accepting it.
AMP5458 Unknown or expired assistant task token
errorruntimesince 0.1
An assistant tool received a task token that is not active or no longer belongs to a running task. Start or resume the intended task and use the token it provides.
AMP5459 Server setting cannot be widened through the API
errorruntimesince 0.1
A project may turn off capabilities, but it cannot widen server policy or select the Codex program. Ask the administrator to change those settings.
AMP5460 Assistant edit request is invalid or ambiguous
errorruntimesince 0.1
The requested file edit could not be applied safely because its operation is incomplete, its target is missing, or its match is ambiguous. Read the file again and provide a more precise edit, including an occurrence when needed.
AMP5461 FFmpeg is required for FLAC output
errorruntimesince 0.1
Install FFmpeg or set AMPLE_FFMPEG to its executable path before exporting FLAC audio.
AMP5462 Proposal could not be measured
errorruntimesince 0.1
AMPLE could not prepare the proposal for measurement. Review the reported error and retry after resolving the setup problem.
AMP5463 Proposal needs offered audio files
errorruntimesince 0.1
The proposal refers to audio files offered to the assistant. Include those files in the proposal or revise it so the song does not depend on unavailable audio.
AMP5470 File exceeds the 60 MB limit
errorruntimesince 0.1
This operation accepts files up to 60 MB. Choose a smaller file.
AMP5505 MIDI to audio converter has no input
errorruntimesince 0.1
Connect a MIDI sequence or MIDI notes to the converter before building it.
AMP5530 Effect preview range is invalid or too long
errorruntimesince 0.1
Choose a non-negative preview interval no longer than 30 seconds.
AMP5600 Not enough free disk space to store a take
errorruntimesince 0.1
The drive that holds the takebank is full, or has less free space than the render needs. Free space on the drive, clean up takes in the Storage panel (ample storage clean), or move the takebank to another drive (ample storage move).
AMP5601 The takebank cannot be written
errorruntimesince 0.1
A take could not be stored: the folder is read-only, the file is in use or the drive is not reachable. Check the data folder of the project (.ample/data or the configured data path).
AMP5602 Clean-up refused
errorruntimesince 0.1
The clean-up was not run: it needs the exact acceptance text, or the plan changed since the dry run. Run the dry run again and accept it.
AMP5603 The take data folder is not reachable
errorruntimesince 0.1
The configured data path of the takebank is not there (an unplugged drive, a renamed folder). Connect the drive or change the data path; builds render again what is missing.
AMP5604 Not enough disk space for this build
errorruntimesince 0.1
The estimate of this build is larger than the free space of the drive that holds the takebank. Free space, clean up takes, move the takebank, or build a smaller part.
AMP5605 Unknown final
errorruntimesince 0.1
The song has no final with that name. List the finals of the song and use one of them.
AMP5610 A place that can never be written
errorruntimesince 0.1
The assistant asked to write somewhere that is never open (the project own .ample folder or a system folder), or the path is not a full path. Remarks go to ample_findings inside the project.
AMP5611 No permission to write there
errorruntimesince 0.1
The assistant tried ample_write_outside without a permission for that path. It asks first with ample_request_access; the person decides.
Lint rules
Lint rules find things that compile but are probably not what you meant. Set a level per rule with lint { L004 error }; silence a rule for the next block with # ample-ignore L004.
Rule
Name
Default
Finds
L001
unused-output
info
A block has outputs that nothing reads.
L002
missing-input
warning
A required input has no source.
L003
lock-and-refresh
warning
A block is locked and also marked always-refetch.
L004
overlapping-parts
warning
Two parts of the same block overlap in time.
L005
dynamic-not-fixed
off
A dynamic block is neither baked nor locked, so results can change between builds.
L006
pinned-outdated
warning
A pinned block uses an older version than its template.
L007
no-path-to-master
warning
A block that makes audio never reaches the master.
L008
name-case-clash
warning
Two block names differ only by upper or lower case.
L009
custom-seed
info
A block uses custom seeds instead of derived ones.
L010
older-port-name
off
A connection uses an older internal port name (in, out, mix, in3, render_in) instead of the name of the stream it carries (audio, notes, render, audio1).
Command line reference
Ample.Cli
Reproducible song programming.
--help, -h, /h, -?, /?: Show help and usage information
Ample.Cli add
Creates a song, lyrics, chords, MIDI clip, module, sample folder, note or folder with a valid skeleton.
--folder: A project folder instead of the default one of the kind.
--project: The project folder (default: the current folder).
--list: Lists the kinds and their default folders.
<kind>: What to create: song, lyrics, chords, midi, module, samples, note, sequence, folder.
<name>: The name (letters, digits, spaces, _ and -); the extension is added.
Ample.Cli analyze
Analyses a take and keeps the result next to the project (computed once per audio, analyser, version and options; the take never changes).
--with: The analyser: tempo, key, loudness, spectrum, pitch or melody (an analysis plugin offers its own).
--project: The project folder (default: the current folder).
--options: Options as JSON, for example {"tempo":120} for melody.
--plugin: Use this analysis plugin instead of a built-in analyser.
--allow-gpu: Allow a plugin that needs the GPU to run (it is never started without this).
--allow-network: Allow a plugin that needs the network to run (it is never started without this).
--list: List the analysers and exit.
<take>: The take id (or the start of one).
Ample.Cli auth
Sign-in settings for `ample serve --login`.
Ample.Cli auth set-password
Stores the user and password for `ample serve --login` (only a salted hash is kept; the password is asked for without echo).
--user: The user name.
--home: The AMPLE user data folder (default: AMPLE_HOME or the local application data folder).
Ample.Cli backup
Incremental, verifiable backups of a project into a folder you choose.
Ample.Cli backup create
Makes a backup (only what changed since the last one is stored).
--target: The folder that holds the backups.
--project: The project folder (default: the current folder).
--no-takes: Leave out the takes and their data.
Ample.Cli backup list
Lists the backups, newest first.
--target: The folder that holds the backups.
Ample.Cli backup policy
Chooses where backups go and whether one is made before every dangerous act.
--target: The folder that holds the backups.
--auto: on or off: make a backup before every dangerous act.
--project: The project folder (default: the current folder).
Ample.Cli backup prune
Removes old backups (a dangerous act: run it without --accept for the dry run). The last good backup is always kept.
--target: The folder that holds the backups.
--keep: Keep the newest N backups.
--daily: Also keep the newest of each of the last D days.
--project: The project folder (default: the current folder).
--accept: The words the dry run asked you to type; without it nothing is removed.
--reason: Why (kept in the black box).
Ample.Cli backup verify
Rehashes every stored file of a backup; exit code 1 when anything is missing or damaged.
--target: The folder that holds the backups.
<backup>: A backup id (or a unique start of it); default: latest.
Ample.Cli bake
Renders a block at full quality and marks it baked in the song source.
--project: The project folder (default: the folder above the song that has .ample).
--root: The AMPLE folder with spec/ (default: found from the current folder upwards).
Explicitly thins old AMPLE Redux records to hourly summaries and applies the size budget.
--project: The project folder (default: the current folder).
Ample.Cli blackbox export
Writes all records to a file as text.
--project: The project folder (default: the current folder).
<file>: Output file.
Ample.Cli blackbox restore
Restores a file from the pre-image of a record, as a new command (the history stays).
--project: The project folder (default: the current folder).
<seq>: A record number.
Ample.Cli blackbox show
Shows one record in full, including its pre-image.
--project: The project folder (default: the current folder).
<seq>: A record number.
Ample.Cli blackbox simulate-crash
For tests: writes the start of a file write to the black box and stops, as a crash would (the next health check finds it).
--project: The project folder (default: the current folder).
<file>: A project file (relative path).
Ample.Cli blackbox tail
Shows the last records of the black box.
--project: The project folder (default: the current folder).
-n: How many records.
Ample.Cli blackbox verify
Checks the checksums and the hash chain; exit code 1 when a record was changed or damaged.
--project: The project folder (default: the current folder).
Ample.Cli build
Renders the song; blocks whose plan is unchanged come from the cache.
--project: The project folder (default: the folder above the song that has .ample).
--root: The AMPLE folder with spec/ (default: found from the current folder upwards).
--block: Build only this block and what it needs.
--preview: Preview: low quality, optionally a range, for example 5:4 (four bars from bar 5), or just 'all'.
--out: Write the master as a WAV file here (default: out/<song>.wav in the project). With --block: write that block's sound here.
<song>: The .amplet song file.
Ample.Cli check
Validates a song file; exit code 1 when there are errors.
--format: Output format: text or json.
--root: The AMPLE folder (default: found from the current folder upwards).
<file>: The .amplet file.
Ample.Cli check quality
Measures takes (clipping, true peak, loudness, silence, DC offset, sample rate) against the rules of spec/quality-rules.json and the project policy. Advisory: the exit code is 1 only when a rule the policy requires fails.
--project: The project folder (default: the current folder).
--take: A take id (or the start of one). Default: the newest take of every block that has audio.
Ample.Cli checkpoint
Takes a snapshot of the sources (nothing happens when nothing changed).
--project: The project folder (default: the current folder).
-m: Message.
Ample.Cli choose
Selects a take for a block (it is used when the block is baked).
--project: The project folder (default: found above the song).
--root: The AMPLE folder with spec/.
<song>: The .amplet song file.
<block>: The block.
<take>: A take id or unique prefix.
Ample.Cli cleanup
Removes unreachable history content. Without --accept you see the dry run.
--project: The project folder (default: the current folder).
--accept: The exact acceptance text for a dangerous act.
--reason: Why (recorded in the black box).
Ample.Cli credits
Writes the credit sheet of the project: what made each block (from the plans of the takes), the modules and library assets in use with their licences, and the people in .ample/credits.json. Advisory, not legal advice.
--project: The project folder (default: the current folder).
--format: md or json.
--out: Write the sheet to this file instead of the screen.
--write: Also keep the sheet in .ample/credits/ so that backups and exports carry it.
--root: The AMPLE folder (default: found from the current folder upwards).
Ample.Cli docs
Builds and checks the generated help.
Ample.Cli docs build
Generates the offline help.html from the spec, docs and command tree.
--root: The AMPLE folder (default: found from the current folder upwards).
--out: Output file for the help page (default: docs/help/help.html in the AMPLE folder).
Ample.Cli docs check
Fails when the committed help.html is out of date or a source has problems (dead links, failing examples).
--root: The AMPLE folder (default: found from the current folder upwards).
Ample.Cli doctor
Checks a project folder without changing it; exit code 1 when there are errors.
--env: Also check what the tools and the assistant need around the program (libraries, ffmpeg, Python, the sound font, disk space); what is missing goes to the project log with how to fix it.
--sound: Build this song (a path in the project) and report the sound of its blocks: silent, very quiet, clipping, not heard, and the loudness of the master, with suggested gains.
--root: The AMPLE folder with spec/ (default: found from the current folder upwards); used with --sound.
--json: With --sound: write the findings as JSON.
--fix: With --sound: put the suggested effect blocks (normalize for a very quiet block, a lower volume for a clipping one) into the song; the journal keeps the old text.
<folder>: The project folder.
Ample.Cli drop
Drops (archives) a line. Without --accept you see the dry run.
--project: The project folder (default: the current folder).
--accept: The exact acceptance text for a dangerous act.
--reason: Why (recorded in the black box).
<name>: A line, tag or node name.
Ample.Cli encrypt
Optional encryption at rest of the black box and of backups (off by default). The passphrase comes from AMPLE_PASSPHRASE.
Ample.Cli encrypt disable
Switches encryption off again (a dangerous act: run it without --accept for the dry run).
--project: The project folder (default: the current folder).
--accept: The words the dry run asked you to type; without it nothing changes.
--reason: Why (kept in the black box).
Ample.Cli encrypt enable
Switches encryption on (a dangerous act: run it without --accept for the dry run). The new passphrase is read from AMPLE_PASSPHRASE.
--project: The project folder (default: the current folder).
--accept: The words the dry run asked you to type; without it nothing changes.
--reason: Why (kept in the black box).
Ample.Cli encrypt passphrase
Changes the passphrase (nothing is re-encrypted). The old one is read from AMPLE_PASSPHRASE, the new one from AMPLE_NEW_PASSPHRASE.
--project: The project folder (default: the current folder).
Ample.Cli encrypt recovery-kit
Writes the recovery kit of an encrypted project.
--project: The project folder (default: the current folder).
--out: The file to write the recovery kit to (it contains the key: keep it offline).
Ample.Cli encrypt status
Says whether the project is encrypted.
--project: The project folder (default: the current folder).
Ample.Cli explain
Says what each kind of edit does to a block at its freeze level.
--project: The project folder (default: found above the song).
--root: The AMPLE folder with spec/.
<song>: The .amplet song file.
<block>: The block.
Ample.Cli export
Writes the project (without secrets) into one archive that any zip tool can read.
--project: The project folder (default: the current folder).
--no-takes: Leave out the takes and their data.
<file>: The archive to write (for example song.ample).
Ample.Cli fmt
Rewrites a song file into the canonical form.
--check: Do not write; exit code 1 when the file is not formatted.
--root: The AMPLE folder (default: found from the current folder upwards).
<file>: The .amplet file.
Ample.Cli fork
Creates a new line (default: at the current line's head).
--project: The project folder (default: the current folder).
--from: A line, tag or node.
<name>: A line, tag or node name.
Ample.Cli go
Switches the working files to a line (unsaved work is checkpointed first).
--project: The project folder (default: the current folder).
<name>: A line, tag or node name.
Ample.Cli help
Shows help topics in the terminal (the same sources as help.html).
--root: The AMPLE folder (default: found from the current folder upwards).
<topic>: A page id or part of a title; without it the topics are listed.
Ample.Cli history
Shows the lines and the newest snapshots of the current line.
--project: The project folder (default: the current folder).
Ample.Cli import
Imports an archive into a new folder after checking every hash.
--new-id: Give the imported project a new id (default: it keeps its id).
<file>: The archive.
<folder>: A new folder to import into.
Ample.Cli instances
Lists running AMPLE server instances.
--json: Write active instances as JSON.
--clean: Remove stale registry records before listing.
--home: The AMPLE home folder (default: AMPLE_HOME, else the local application data folder).
Ample.Cli keys
The signing keys of the user (ECDSA P-256). The private key is sealed with the passphrase of the vault.
Ample.Cli keys export
Prints the public key (PEM) for someone who verifies your signatures.
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
<key>: The key id.
Ample.Cli keys generate
Makes a signing key.
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
Ample.Cli keys list
Lists the keys.
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
Ample.Cli keys rotate
Retires the active key (it keeps verifying old signatures, but cannot sign any more) and makes a new one.
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
Ample.Cli library
Linked folders (catalogs), the assets in them, and what a project uses.
Ample.Cli library add
Copies a file into a writable linked folder and scans it. A read-only folder is refused.
--into: The linked folder (id or name); it must be read-write.
--as: Path inside the folder (default: the file name).
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
<file>: The file to copy into a writable linked folder.
Ample.Cli library link
Links a folder and scans it.
--name: A name (default: the folder name).
--read-write: Allow AMPLE to write into it (a marker file with the catalog id is written). Default: read-only.
--for-project: Link it for the project only.
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
--project: The project folder (default: the current folder).
<path>: The folder to link (you choose it; nothing is found automatically).
Ample.Cli library list
Lists the linked folders with their state, and the assets.
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
Ample.Cli library relocate
Points a linked folder at its new place (checked by its marker, fingerprint or files). No song changes.
--force: Link it even if it does not look like the same catalog.
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
<folder>: A linked folder (id or name).
<new-path>: Where the folder is now.
Ample.Cli library scan
Scans a linked folder incrementally (unchanged files are not read).
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
<folder>: A linked folder (id or name).
Ample.Cli library verify
Hashes the assets again and compares them with the catalog; exit code 1 when anything is not ok.
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
<folder>: A linked folder (id or name); default: all.
Ample.Cli lint
Shows only the lint rules (L001 and up) for a song file; exit code 1 when a rule is set to error.
--format: Output format: text or json.
--root: The AMPLE folder (default: found from the current folder upwards).
<file>: The .amplet file.
Ample.Cli lock
ample.lock: the assets, packs and plugins a project depends on, pinned by hash.
Ample.Cli lock update
Works out what the songs use and writes ample.lock (accepts changed assets).
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
--project: The project folder (default: the current folder).
--root: The AMPLE folder (default: found from the current folder upwards).
Ample.Cli lock verify
Compares ample.lock with the catalog; exit code 1 when anything is not ok.
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
--project: The project folder (default: the current folder).
Ample.Cli lsp
Runs the language server (Language Server Protocol 3.17) on stdin and stdout. Editors start this; nothing else is printed.
--root: The AMPLE folder with spec/ (default: found from the current folder upwards).
--help-base: Where diagnostic codes link to (default /help).
Ample.Cli mcp
Runs an MCP server on stdio with task-scoped AMPLE context and constrained actions, including approved visible-browser actions (started by an AI agent, not by hand). The token of the task comes from the environment variable AMPLE_CONTEXT_TOKEN.
--url: The address of the running AMPLE server.
Ample.Cli midi
MIDI file conversion commands.
Ample.Cli midi convert
Converts an SMF file into a project module.
--project: Project folder (default: current folder).
--output: Generated module name in modules/ (letters, digits, _ and -).
<file>: Input .mid or .midi file.
Ample.Cli midi soundfont-check
Checks GM preset and percussion coverage in an SF2 bank.
--file: Project-relative SF2 file or pack:musescore-general.
Ample.Cli midi verify-conversion
Checks that a MIDI file parses and converts to a valid module without writing project files.
<file>: Input .mid or .midi file to parse and convert in memory.
Ample.Cli modules
Reusable modules: groups of blocks with parameters, inputs and outputs, used with 'use' in a song.
Ample.Cli modules add
Checks a module file and copies it into the project's modules/ folder.
--project: The project folder (default: the current folder).
<file>: The module file to copy into the project.
Ample.Cli modules expand
Prints the song with every use replaced by the blocks of its module, and the problems found.
--project: The project folder (default: the current folder).
--root: The AMPLE folder (default: found from the current folder upwards).
<file>: The .amplet file.
Ample.Cli modules list
Lists the modules of the project's modules/ folder.
--project: The project folder (default: the current folder).
Ample.Cli mv
Moves or renames a project file or folder and, by default, updates every path in the songs and modules that points at it.
--no-refactor: Move only the file; paths in songs and modules that point at it are not changed (they may break).
--dry-run: Print the changes without making them.
--project: The project folder (default: the current folder).
<from>: The project file or folder to move or rename (project-relative).
<to>: Its new project-relative path.
Ample.Cli new
Creates a project folder with the mandatory .ample structure.
--name: Project name (default: the folder name).
--seed: Master seed (default: derived from the project id).
<folder>: The project folder.
Ample.Cli open
Attaches to or starts the server for a project.
--last: Open the most recently used project.
--visible: Show a console window for the server.
--no-open: Start or attach without opening a browser.
--song: Song path to open when song deep links are supported.
--home: The AMPLE user data folder (default: AMPLE_HOME or the local application data folder).
<target>: A project folder, .amplet song or .module file.
Ample.Cli packs
Lists and links the user-level installed audio packs.
Ample.Cli packs link
Creates a junction to an existing audio-packs folder; it never copies packs.
--home: The AMPLE user folder (default: AMPLE_HOME, else the local application data of the user).
<folder>: Existing audio-packs folder to link into the user-level AMPLE home.
Ample.Cli packs list
Lists the pack folders in lookup order.
--home: The AMPLE user folder (default: AMPLE_HOME, else the local application data of the user).
Ample.Cli packs where
Shows where an installed pack file is found.
--home: The AMPLE user folder (default: AMPLE_HOME, else the local application data of the user).
<asset>: A path below an installed pack (default: the MuseScore General SoundFont).
Ample.Cli peek
Looks at a line, tag or node without changing anything.
--project: The project folder (default: the current folder).
--file: Print this file of the peeked state.
<name>: A line, tag or node name.
Ample.Cli plan
Plan commands.
Ample.Cli plan show
Prints the plan (the complete recipe) a take was made from.
--project: The project folder (default: the current folder).
<take>: A take id or a unique prefix.
Ample.Cli play
Builds a preview and writes the WAV (no player is started).
--project: The project folder (default: the folder above the song that has .ample).
--root: The AMPLE folder with spec/ (default: found from the current folder upwards).
--out: Write the master as a WAV file here (default: out/<song>.wav in the project). With --block: write that block's sound here.
<song>: The .amplet song file.
Ample.Cli plugins
Plugins.
Ample.Cli plugins describe
Shows a plugin's manifest and settings schema.
--root: The AMPLE folder (default: found from the current folder upwards).
--folder: An extra folder with plugins.
<plugin>: A plugin id.
Ample.Cli plugins list
Lists the plugins that are found.
--root: The AMPLE folder (default: found from the current folder upwards).
--folder: An extra folder with plugins.
Ample.Cli plugins run
Starts a plugin, does the handshake and asks it to describe itself (a self-test; no render).
--root: The AMPLE folder (default: found from the current folder upwards).
--folder: An extra folder with plugins.
<plugin>: A plugin id.
Ample.Cli plugins validate
Checks a plugin manifest; exit code 1 when it has problems.
<folder>: A plugin folder (with plugin.json).
Ample.Cli project
Project commands.
Ample.Cli project info
Prints a project summary without taking the writer lock.
<folder>: The project folder.
Ample.Cli project open
Opens a project for writing (takes the instance lock), prints a summary and releases it.
<folder>: The project folder.
Ample.Cli promote
Shows what promoting a line into another would change; --yes does it.
--project: The project folder (default: the current folder).
--source: Source line.
--to: Target line.
--yes: Do it (without it you only see the preview).
Ample.Cli recover
Lists commands that were interrupted (no outcome record) with what they were about to overwrite.
--project: The project folder (default: the current folder).
Ample.Cli regen
Regenerates a block safely: the selected take is kept, the new result is verified, and a difference becomes a decision whose default is to restore the previous take.
--project: The project folder (default: found above the song).
--root: The AMPLE folder with spec/.
--seed: Seed for the regeneration (default: derived).
--decide: Answer an open regeneration decision (its id).
--keep-variant: With --decide: keep the new take as a variant (default: restore the previous take).
<song>: The .amplet song file.
<block>: The block.
Ample.Cli restore
Restores a backup into a new folder, or in place as a dangerous act.
--target: The folder that holds the backups.
--in-place: Put the backup into an existing project (a dangerous act: run it without --accept for the dry run).
--project: The project folder (default: the current folder).
--accept: The words the dry run asked you to type.
--reason: Why (kept in the black box).
<backup>: A backup id (or a unique start of it), or latest.
<folder>: A new folder to restore into (not with --in-place).
Ample.Cli seed
Shows or changes the seed of a block. Seeds are locked by default.
--project: The project folder (default: found above the song).
--root: The AMPLE folder with spec/.
--unlock: Unlock the seed (changing it probably makes reproduction impossible).
--lock: Lock the seed.
--set: Set the seed (needs it to be unlocked).
--randomize: Give the seed a new derived value (needs it to be unlocked).
--reason: Why (recorded in the history).
<song>: The .amplet song file.
<block>: The block.
Ample.Cli serve
Starts the local server with the web UI, the API and the help (127.0.0.1 only).
--project: The project folder (default: the current folder).
--root: The AMPLE folder (default: found from the current folder upwards).
--home: The AMPLE user data folder (default: AMPLE_HOME or the local application data folder).
--port: Port on 127.0.0.1 (default: a free one).
--max-upload-mb: Maximum project file upload size in MiB (1 to 512).
--no-browser: Do not open the browser.
--visible: Keep the server in a visible console window.
--no-token-output: Do not print the private token launch URL.
--allow-lan: Accept other host names. Only on a trusted network: the session token is the only protection.
--login: Also accept a sign-in with the user and password set by `ample auth set-password` (page: /login). The launch token keeps working.
--allow-all: Listen on every address and accept any host name (for a tunnel). Only together with --login.
--log-file: Write server output to this rolling log file.
--started-by: Start source for launcher integration.
--agent-name: Self-reported agent name for launcher integration.
--agent-reason: Reason for an agent-started server.
--openapi: Write the OpenAPI document to this file and exit (no project needed).
Ample.Cli sign
Signs the newest record of the black box (or a file) with your signing key. The passphrase of the key is read from AMPLE_VAULT_PASSPHRASE.
--project: The project folder (default: the current folder).
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
--file: Sign this file (an export) instead of the black box; the signature goes next to it as <file>.sig.
Ample.Cli storage
What the takebank uses, what could be given back, the clean-up and the storage options.
Ample.Cli storage clean
A dry run of a clean-up (what would go, what stays protected); with --accept <fingerprint> it runs.
--project: The project folder (default: the current folder).
--accept: The fingerprint printed by the dry run; with it the clean-up is run.
Ample.Cli storage compress
Compresses the plain WAV takes of the project in place (lossless: the takes and their ids do not change).
--project: The project folder (default: the current folder).
Ample.Cli storage move
Moves the take data to another folder or drive (progress is shown; a stopped move continues when run again).
--project: The project folder (default: the current folder).
<path>: The folder for the take data (for example D:\ample-takes\song), or - to move it back into the project.
Ample.Cli storage settings
Shows or changes the storage options of the project.
--project: The project folder (default: the current folder).
--soft-limit-gb: The size above which a clean-up is suggested (0: off).
--keep-superseded: How many older takes of a block stay besides the current one.
--archive-path: Move reclaimed data here instead of deleting it (empty: delete).
Ample.Cli storage show
Shows the size of the takebank, what is protected and what could be given back.
--project: The project folder (default: the current folder).
Ample.Cli tag
Creates a locked tag.
--project: The project folder (default: the current folder).
--from: A line, tag or node.
--reason: Why (recorded in the black box).
<name>: A line, tag or node name.
Ample.Cli takes
Takebank commands.
Ample.Cli takes list
Lists the takes of a project, newest first.
--project: The project folder (default: the current folder).
Ample.Cli takes show
Shows one take with its plan id and data.
--project: The project folder (default: the current folder).
<take>: A take id or a unique prefix.
Ample.Cli takes verify
Rehashes the data of a take (or of all takes); exit code 1 on missing, truncated or damaged data.
--project: The project folder (default: the current folder).
<take>: A take id or prefix; without it every take is verified.
Ample.Cli variants
Makes several takes of a block to choose from (the same request gives the same variants) and lists them.
--project: The project folder (default: found above the song).
--root: The AMPLE folder with spec/.
--count: How many.
--kind: variant, resing (new voice, same melody) or rewrite (new melody).
<song>: The .amplet song file.
<block>: The block.
Ample.Cli vault
Secrets (API keys, tokens) kept outside every project. Plugins get only the secrets their manifest names.
Ample.Cli vault list
Lists the names of the secrets (never the values).
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
Ample.Cli vault remove
Removes a secret.
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
<name>: The name of the secret.
Ample.Cli vault set
Stores a secret. The value is read from standard input; the passphrase of the vault from AMPLE_VAULT_PASSPHRASE (a new vault is made when there is none).
--home: The library folder (default: AMPLE_HOME, else the local application data of the user).
<name>: The name of the secret (letters, digits, _ - .).
Ample.Cli verify
Checks the hash chain of the black box and every signature of the project (or the signature of a file). Exit code 1 when anything is wrong.
--project: The project folder (default: the current folder).
--file: Check the signature of this file (<file>.sig) instead of the project.
--trust: Only accept signatures of this key id.
Ample.Cli verify-reproduction
Re-renders the selected take's own plan only to check reproduction; never replaces anything.
--project: The project folder (default: found above the song).
--root: The AMPLE folder with spec/.
<song>: The .amplet song file.
<block>: The block.
API reference
The API of ample serve: loopback only, every call carries the session token in X-Ample-Token, every error is RFC 9457 problem details with the AMP code. Generated from docs/api/openapi.json.
acts
Method
Path
What it does
POST
/api/acts/cleanup
Dry run of the history cleanup; returns a token and what to type. Read only.
POST
/api/acts/confirm
Second step: confirm a dry run with the token, the typed acceptance and a reason. Changes state: journaled in AMPLE Redux as a command.
POST
/api/acts/drop-line
Dry run of dropping a line; returns a token and what to type. Read only.
POST
/api/acts/master-seed
Dry run of changing the master seed; returns a token and what to type. Read only.
POST
/api/acts/prune-backups
Dry run of pruning old backups (the last good one stays); returns a token and what to type. Read only.
POST
/api/acts/replace-take
Dry run of replacing the selected take with a regenerated one. Read only.
POST
/api/acts/restore-in-place
Dry run of restoring a backup into this project; returns a token and what to type. Read only.
POST
/api/acts/retag
Dry run of moving or removing a tag. Read only.
POST
/api/acts/unlink-mount
Dry run of unlinking a folder from the library (no file is touched); returns a token and what to type. Read only.
ai
Method
Path
What it does
POST
/api/ai/attachment
Keeps an audio file the user offers to Codex (WAV bytes in the body, or mp3/ogg/flac with ?extension=; name= is the file name). Nothing of it is in the project: it is only offered to a task (offered: [id]) and comes along into the project only when the user chooses so at Accept. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/attachment/{id}/audio
An offered audio file, to listen to before it is given to Codex or taken along. Read only.
GET
/api/ai/context/access
For an AI task: asks the person to allow writing to a place outside the project (path, why, minutes, kind write or passthrough). The call waits for the answer: allow for that long, always for the project, or deny. Read only.
GET
/api/ai/context/analyze
Read-only context for an AI task: the reference analysers (tempo, key, loudness, spectrum, pitch) on the built audio of a block. Read only.
GET
/api/ai/context/arrangement
For an AI task: the arrangement of the song before the task and now (where the blocks sit in bars and seconds, the slice of the master), what changed, and with measure=true the real length, peak and silence of the built master of the proposal next to the current project. Read only.
POST
/api/ai/context/audio-previews
For an AI task: fetches 1-5 MP3 audio URLs observed by its visible browser with one batch approval and presents each as an independent output choice; each file is at most 10 MB and is not added to the project. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/context/block
Read-only context for an AI task: one block with what feeds it and what it feeds. Read only.
GET
/api/ai/context/blocktypes
Read-only context for an AI task: the block types. Read only.
GET
/api/ai/context/browser-audiohero-preview
For an AI task: injects a click into one numbered AudioHero Listen/Preview control so the page itself handles download_auth; returns the resulting audio URL only. Read only.
GET
/api/ai/context/browser-click
For an AI task: clicks a numbered element from the latest browser-read result; purchase actions are blocked and audio previews return a URL only. Read only.
POST
/api/ai/context/browser-js
For an AI task: executes owner-authorized JavaScript in the visible page; may change page state or trigger actions and can bypass the numbered-click purchase block. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/context/browser-media-mute
For an AI task: mutes or unmutes HTML audio and video elements on the current page; does not control browser tab/system audio. Read only.
GET
/api/ai/context/browser-mouse-move
For an AI task: moves the pointer inside the visible page viewport for hovering; does not click. Read only.
GET
/api/ai/context/browser-network-stats
For an AI task: returns aggregate console error and network resource counts without message text, URLs, headers or bodies. Read only.
GET
/api/ai/context/browser-open
For an AI task: after explicit task approval, opens a URL in the visible persistent Chromium window. The owner enters credentials manually. Purchase actions are unavailable. Read only.
GET
/api/ai/context/browser-read
For an AI task: reads the visible page title and text, returning visible links, buttons and text fields with indexes for browser-click and browser-type. Read only.
GET
/api/ai/context/browser-screenshot
For an AI task: captures the visible page screenshot and returns it for visual inspection, without saving it as a download. Read only.
GET
/api/ai/context/browser-show
For an AI task: after explicit browser approval, makes its isolated headless Chromium window visible for owner inspection or manual interaction. Read only.
GET
/api/ai/context/browser-type
For an AI task: fills a numbered non-credential text field from the latest browser-read result; password, username and account fields are never filled. Read only.
GET
/api/ai/context/check
For an AI task: the errors and warnings that the proposal introduced (the ones the song did not have before); none means it can be handed in. Read only.
POST
/api/ai/context/copy-file
Copy a text file within an AI staged proposal to a new path, without overwriting; the source stays. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/context/docs
Read-only context for an AI task: the reference documents (list, one, or a search). Read only.
POST
/api/ai/context/edit
For an AI task: structured edits (replace, insert, delete_lines, set_param, add_block, remove_block, connect, disconnect, create) of a file of the temporary copy, all or nothing; the answer lists the new errors and warnings and the diff. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/context/faer
Read-only context for an AI task: independent project Faer routes, aggregate capability readiness and installed ACE model filenames, roles and sizes, without credentials or job content. Read only.
GET
/api/ai/context/fetch
For an AI task: AMPLE downloads a public web address for Codex (which has no network). A link from the user's request is fetched at once, another link needs the user's approval. Public addresses only, images, text, audio, JSON, XML or PDF, at most 10 MB. Read only.
GET
/api/ai/context/file
Read-only context for an AI task: a text file of the project (a PDF: its text), in a window; an offset reads on past a truncated one. Read only.
POST
/api/ai/context/findings
For an AI task: writes the model's remarks (what is missing, what could be better) to notes/ai-findings/ inside the project; no permission is needed. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/context/history
For an AI task: what the user asked before in this project and what came out (newest first; id= for one request in full with its changes). Read only.
GET
/api/ai/context/hum
For an AI task: the melody found in a hummed recording that the user attached (notes, key, the notes text of a midi-clip), at the tempo of the song or the one asked for. Read only.
POST
/api/ai/context/import-score
For an AI task: turns a MIDI or MusicXML score of the project into a new sequence file in the proposal (one channel per part, patterns of a number of bars, a playlist). Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/context/install-pack
For an AI task: downloads a .zip sample-pack archive from a public web address and installs it into the user's library (packs/<name>@<version>), available to every project afterward. Always needs the user's approval, even when the link was given in the request. At most 200 MB; the archive must contain a pack.json. Read only.
GET
/api/ai/context/listen
For an AI task: builds the proposal in a temporary project and measures a block (or the master) with analysers (default loudness and spectrum), next to the same block of the real project when it is built. Read only.
POST
/api/ai/context/listen
For an AI task: like GET listen, with a trial: edits (the format of the edit tool) that are applied only in the temporary project of this call, to hear a variant without changing the proposal. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/context/lookup
For an AI task: more about units, a parameter (unit, range, what a change reaches), the impact of a block (what is rendered again) or a word (block types, parameters and documents), for the song as the task has it now. Read only.
GET
/api/ai/context/memory
For an AI task: the notes kept for later tasks outside the song, scope=project or scope=song (the song of the task). Read only.
POST
/api/ai/context/memory
For an AI task: adds a dated note to (mode=append, the default) or rewrites (mode=replace) the memory of the project or of the song; it is kept in the .ample folder, never in the song. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/context/move
For an AI task: proposes moving a project file or folder with the references following. With confirm ask the owner is asked now and the call waits for the answer; with propose the move is listed with the proposal and ticked at Accept. Nothing is moved before Accept. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/context/move-file
Move a text file within an AI staged proposal without overwriting; workspace/filename recovers a misplaced root song. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/context/obsolete
For an AI task: marks one previously presented sample obsolete with a reason; it remains in the output until the user dismisses it. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/context/offered
For an AI task: the audio files the user offered (length, level, pitch, the path the song uses for them, whether the proposal uses them). Read only.
GET
/api/ai/context/present
For an AI task: shows the user a piece of sound (the render of a block, whole or a slice, from the proposal or the current project) in the task window. Read only.
POST
/api/ai/context/present
Presents a clip with optional temporary structured trial edits; reports rendered settings and identical audio. Read only.
GET
/api/ai/context/project
Read-only context for an AI task: what the project is, its linked sample-pack catalogs, and a searchable, filterable, paged list of its files. Read only.
GET
/api/ai/context/project-log
For an AI task: the project log, what is missing for the tools of the project and how to put it right. Read only.
GET
/api/ai/context/redux-search
For an AI task: read-only search of the task's AMPLE Redux archive; scope=project expands to this project's journal. Read only.
GET
/api/ai/context/request-asset
For an AI task: asks the user to let Codex read a large or non-text source asset; AMPLE copies it into the working folder of the task when it is allowed. The answer comes when the user has decided. Read only.
POST
/api/ai/context/skip-build
For an AI task: honor the user's explicit no-build or no-render request while still validating source changes. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/context/song
Read-only context for an AI task: a song with its blocks and problems. Read only.
GET
/api/ai/context/thought
For an AI task: shows the user what the AI is thinking about right now (a short note that replaces the previous one). Read only.
GET
/api/ai/context/web-read
For an AI task: reads a public HTML URL supplied by the user or returned by this task's approved search, without cookies or JavaScript and without another approval; redirects are public-address-only and response size is capped at 2 MB. Read only.
GET
/api/ai/context/web-search
For an AI task: calls the selected search provider after task approval; DuckDuckGo is its free official Instant Answer API (not full results), Brave is an API-key search plan. Brave usage is a local estimate and identical results have a brief in-memory cache. No search-result HTML scraping fallback. Read only.
POST
/api/ai/context/write-outside
For an AI task: AMPLE's own pass-through write to a place outside the project; it only works inside a permission the person gave (see access). Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/device/cancel
Stops a device flow that is waiting. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/device/start
Starts the Codex device flow for this project (its own Codex home) and returns the address and the code to enter. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/device/status
The state of the device flow: waiting, linked, failed or cancelled. Read only.
GET
/api/ai/grants
The permissions that last for the project (writing outside it and visible browser access) and the project log. Read only.
POST
/api/ai/grants/revoke
Takes back a permission that lasts for the project. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/history
The earlier AI requests of this project (newest first), optionally for one song (song=) or containing a word (query=), so a new request can continue one (continueFrom). Read only.
POST
/api/ai/hum
Keeps a hummed recording (WAV bytes) so that it can be attached to an AI task or message (hums: [id]); the answer holds its id and the melody found in it (tempo, grid, key and bar as for /api/hum/transcribe). Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/hum/{id}/audio
The kept hummed recording, to play it again. Read only.
GET
/api/ai/models
The models Codex offers and the current account's usage limits. Read only.
GET
/api/ai/policy
What this server allows projects to use of Codex. Read only.
POST
/api/ai/policy
Changes what this server allows projects to use of Codex (capabilities only: they say what a project MAY choose). Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/sessions
Lists the saved assistant sessions for this project. Read only.
POST
/api/ai/sessions
Creates a named assistant session; optional conversation archiving is off by default. Changes state: journaled in AMPLE Redux as a command.
DELETE
/api/ai/sessions/{id}
Deletes an assistant session that has no active or unapplied proposal. Changes state: journaled in AMPLE Redux as a command.
PATCH
/api/ai/sessions/{id}
Renames a saved assistant session or changes its optional conversation archive. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/settings
Changes the AI settings of the project: the assistant (none, codex-server, codex-device) and the execution mode (manual, auto, unsafe). What the server does not allow cannot be chosen; the first use and the first unsafe use need an acknowledgement. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/status
Where the AI assistant stands: what the server allows, what the project chose, whether Codex is installed and (with auth=true) whether it is signed in. Read only.
GET
/api/ai/tasks
The AI tasks of this project, newest first. Read only.
POST
/api/ai/tasks
Asks Codex for a proposal: it works in a temporary copy of the files in scope; the project is not changed. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/tasks/{id}
One AI task: the log (since=N: only the entries after number N; details=1 adds the input and output of every tool call, which is large), the approvals Codex waits for, and the proposal with its diff, diagnostics and build status. Read only.
POST
/api/ai/tasks/{id}/accept
Applies the proposal: a checkpoint first, then the other files; the new text of the song is returned for the editor. Refused when the project changed since the task began, and when it does not build unless anyway is true. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/tasks/{id}/accept-selected
Accepts selected proposal files without starting another Codex turn. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/tasks/{id}/accept-selected-continue
Accepts selected proposal files after checking them, then continues the same Codex conversation with the remaining proposal. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/tasks/{id}/answer
Answers an approval request of Codex: accept, acceptForSession, accept30, acceptForProject, decline or cancel. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/tasks/{id}/assistant
Changes the model and/or the reasoning effort of a running task (as Codex itself can); the next turn uses them and Codex is told. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/tasks/{id}/audio
Proposal audio with byte ranges; peaks=N returns a bounded waveform summary instead of audio. Read only.
POST
/api/ai/tasks/{id}/build
Builds the proposal in its temporary project so it can be listened to: the whole song, or with block one block and what it needs. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/tasks/{id}/cancel
Stops a task. Nothing in the project changes. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/tasks/{id}/clip/{clip}
A clip of sound that Codex presented to the user, to listen to; ?format=wav or ?format=mp3 (encoded by ffmpeg) downloads it as a file. Read only.
POST
/api/ai/tasks/{id}/clips/{clipId}/obsolete
Marks one presented sample obsolete with a reason. It stays visible until the user dismisses it. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/tasks/{id}/compact
Asks Codex to compact its conversation now (between turns); the task shows that it is compacting while it happens. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/tasks/{id}/continue
Continues only unfinished work using delivered clips and original quantity limits; rejects duplicate running requests. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/tasks/{id}/log/{seq}
The input and output of one tool call in the log of a task (the log itself leaves them out because they are large). Read only.
POST
/api/ai/tasks/{id}/message
Goes on with a proposal before it is accepted: while Codex works the message steers it, afterwards it asks for a change on top of the proposal as it is now. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/tasks/{id}/rebase
Brings a proposal up to date with a project that changed in the meantime, where the changes do not overlap. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/tasks/{id}/reject
Throws the proposal away; the project stays as it is. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/tasks/{id}/resume
Resumes an interrupted or ended assistant conversation; with fresh=true a new Codex session starts in the same working copy and is told what was asked and what is already changed. Changes state: journaled in AMPLE Redux as a command.
GET
/api/ai/tasks/{id}/text
The proposed text of a file of the proposal (as it would be after Accept). Read only.
POST
/api/ai/tasks/{id}/undo
Puts back the files (other than the open song) that an accepted proposal changed. Changes state: journaled in AMPLE Redux as a command.
POST
/api/ai/unlink
Removes the Codex identity of this project (its login and its Codex home); the Codex of the user is not touched. Changes state: journaled in AMPLE Redux as a command.
analysis
Method
Path
What it does
POST
/api/audio/transcriptions
Runs a local analysis plugin on bounded WAV audio and keeps its aligned stems and transcription artifacts in the project analysis cache. Changes state: journaled in AMPLE Redux as a command.
GET
/api/audio/transcriptions/{id}
Returns local transcription provenance and URLs for its aligned audio and MIDI artifacts. Read only.
GET
/api/audio/transcriptions/{id}/peaks
Returns a bounded waveform summary for a 16-bit source or stem WAV. Read only.
GET
/api/audio/transcriptions/{id}/{role}
Streams one completed local transcription source, stem or MIDI artifact with byte ranges. Read only.
audio
Method
Path
What it does
POST
/api/hum/transcribe
Finds the melody in a hummed or sung line (WAV bytes in the body, 16 bit PCM): the notes, the key and the notes text of a midi-clip, snapped to tempo (default 120), grid (beats, default 0.25), key (auto or like 'A minor') and start bar. An attempt: listen to it and correct it. Changes state: journaled in AMPLE Redux as a command.
backup
Method
Path
What it does
GET
/api/backup
What the project remembers about its backups (target, last backup, age, whether one is made before dangerous acts) and the backups in that target. Read only.
POST
/api/backup/create
Makes a backup (into the remembered target, or the target given). Changes state: journaled in AMPLE Redux as a command.
POST
/api/backup/policy
Chooses the target and whether a backup is made before every dangerous act (takes effect the next time the server starts). Changes state: journaled in AMPLE Redux as a command.
POST
/api/backup/verify
Rehashes a backup (default: the latest in the remembered target). Changes state: journaled in AMPLE Redux as a command.
POST
/api/export
Writes the project into an archive file (no secrets, a consistent copy of the database). Changes state: journaled in AMPLE Redux as a command.
build
Method
Path
What it does
POST
/api/build
Builds a song (the cache decides what is rendered); progress goes to the event stream. Changes state: journaled in AMPLE Redux as a command.
GET
/api/build/estimate
How big and how long the build of a song would be (an educated guess that learns from earlier builds), and whether it fits on the drive of the takebank. Nothing is rendered or stored. Read only.
GET
/api/build/status
Which blocks of a song already have a take (a dry run of the build: nothing is rendered or stored). A block is cached (with its take), stale (no take yet), waiting (an input has none) or disabled. Read only.
GET
/api/sound-check
The sound of the built takes of a song: silent, very quiet, clipping and unheard blocks, the master loudness and values that are cut, each with a fix when there is one (nothing is rendered). Read only.
POST
/api/sound-check/fix
Puts the fix of a sound finding into the song: a normalize block (fix normalize) or a volume block with a lower gain (fix volume:-3) after the block; what read the block now reads the effect. Changes state: journaled in AMPLE Redux as a command.
control
Method
Path
What it does
GET
/api/control/block
Everything the controls need for one block: freeze level, seed with its history, selected take, variants, the explainer and open regeneration decisions. Read only.
POST
/api/control/decide
Answers an open regeneration decision: restore (default) or keep-variant. Replacing needs the two-step act. Changes state: journaled in AMPLE Redux as a command.
POST
/api/control/regenerate
Regenerates a block safely: the selected take stays; a different result becomes an open decision (default: restore). Changes state: journaled in AMPLE Redux as a command.
GET
/api/control/seed
A seed with its lock state and history. Read only.
POST
/api/control/seed
Unlocks, locks, sets or randomizes a seed (a locked seed refuses changes). Changes state: journaled in AMPLE Redux as a command.
POST
/api/control/select
Selects a take for a block. Changes state: journaled in AMPLE Redux as a command.
POST
/api/control/variants
Makes one or more individually indexable takes of a block. Changes state: journaled in AMPLE Redux as a command.
POST
/api/control/variants/discard
Discards a variant (the take stays in the bank until cleanup). Changes state: journaled in AMPLE Redux as a command.
POST
/api/control/variants/keep
Keeps a variant (it stays selectable). Changes state: journaled in AMPLE Redux as a command.
POST
/api/control/variants/preview
Previews the deterministic field changes for a variant request without rendering. Changes state: journaled in AMPLE Redux as a command.
POST
/api/control/verify-reproduction
Renders with the reference take's seed and current software/settings, compares output bytes and warns on recipe or version changes. Keeps the reference take selected. Changes state: journaled in AMPLE Redux as a command.
documents
Method
Path
What it does
GET
/api/doc
Opens a song: text, version, diagnostics and blocks. Read only.
PUT
/api/doc
Applies new text to the working copy (not saved) with optimistic concurrency by version. Changes state: journaled in AMPLE Redux as a command.
GET
/api/doc/complete
Completions at a position. Read only.
POST
/api/doc/edit
Applies a graph edit (set parameter, add or remove block, connect, disconnect, rename, flag) as a text edit. Changes state: journaled in AMPLE Redux as a command.
GET
/api/doc/expanded
Returns the module-expanded graph for nested module navigation without changing the open song. Read only.
GET
/api/doc/hover
Hover text at a position. Read only.
GET
/api/doc/inputs
The files each block reads while it builds (sample folders, lyrics, MIDI and audio files, SoundFonts, modules) as one stamp per block, a hash of all of them, and the blocks whose seed follows the master seed. The web UI compares it to know that a build is out of date after such a file changed, although the song text did not. Read only.
POST
/api/doc/parse
Parses text that is not open (for example an older state read with peek) and returns the same view as a document, without opening or changing anything. Read only.
POST
/api/doc/revert
Throws the changes of the working copy away: the song goes back to the text of the saved file. Changes state: journaled in AMPLE Redux as a command.
POST
/api/doc/save
Writes the working copy to the file through the command bus (AMPLE Redux keeps the old text). Changes state: journaled in AMPLE Redux as a command.
POST
/api/doc/save-as
Writes the working copy of a song to a new song file of the project (an existing name is refused); the open song stays as it is. Changes state: journaled in AMPLE Redux as a command.
GET
/api/doc/symbols
The outline of a song. Read only.
GET
/api/docs
The song files of the project. Read only.
POST
/api/gm/audition
Renders a bounded temporary GM note preview without creating a take; channel -1 auditions all note channels. Read only.
POST
/api/midi/clip-notes
The notes a midi-clip block plays (from its chords, melody or notes), written as compact notes text, so the piano roll can make a clip with chords or a melody editable; nothing is changed or built. Read only.
GET
/api/midi/download
Re-encodes a project MIDI source as a downloadable Standard MIDI File while retaining its parsed events. Read only.
GET
/api/midi/info
Returns a bounded, payload-free summary of a project MIDI file. Read only.
POST
/api/midi/module-download
Exports edited notes from a generated MIDI module and merges them with retained source events. Read only.
GET
/api/midi/notes
The paired notes of a project MIDI file (all tracks or one), as ticks of the file, for the read-only piano roll and for making an editable copy; bounded. Read only.
POST
/api/midi/upload
Validates and imports bounded MIDI bytes into assets/midi through the project command bus. Changes state: journaled in AMPLE Redux as a command.
GET
/api/templates
The block templates, port types and classes of the language (the palette is generated from this). Read only.
effects
Method
Path
What it does
POST
/api/effects/impulse
Returns the bounded WAV impulse response selected by a convolution block for approximate local preview. Read only.
POST
/api/effects/preview
Applies a built-in integer effect to a bounded input range without creating a take or changing project state. Read only.
The finals of a song (a song without any has one implicit final, full) and what their last render made. Read only.
POST
/api/finals
Adds a final to a song or changes its definition: the first bar, how many bars, a fade in (beats), a fade out (bars) and the format. Changes state: journaled in AMPLE Redux as a command.
POST
/api/finals/delete
Removes a final from a song (an exported file stays). Changes state: journaled in AMPLE Redux as a command.
POST
/api/finals/render
Renders one final on purpose: the estimate and the disk check, the build, then the kept bars with their fades exported to renders/. Changes state: journaled in AMPLE Redux as a command.
health
Method
Path
What it does
GET
/api/blackbox/status
AMPLE Redux journal check and the last records (legacy route name retained for compatibility). Read only.
GET
/api/health
Project health: doctor findings, AMPLE Redux check, interrupted commands, open regeneration decisions. Read only.
POST
/api/health/recover
Deals with an interrupted command: restore what it was about to overwrite (from its pre-image), or accept the file as it is. Changes state: journaled in AMPLE Redux as a command.
Lines, tags and the newest snapshots of the current line. Read only.
POST
/api/history/checkpoint
Takes a snapshot (nothing happens when nothing changed). Changes state: journaled in AMPLE Redux as a command.
GET
/api/history/diff
Which files differ between two states (line, tag or node). Read only.
POST
/api/history/fork
Creates a new line. Changes state: journaled in AMPLE Redux as a command.
POST
/api/history/go
Switches the working files to a line (unsaved work is checkpointed first). Open documents are reloaded. Changes state: journaled in AMPLE Redux as a command.
GET
/api/history/graph
Every snapshot of every line with its parent, plus lines, tags and the current line (the history view draws this). Read only.
GET
/api/history/peek
Reads another state without changing anything. Read only.
POST
/api/history/promote
Replaces studio with garage (the old studio stays in the history). Changes state: journaled in AMPLE Redux as a command.
GET
/api/history/promote-preview
What promoting garage into studio would change. Read only.
POST
/api/history/restore-line
Brings back a line that was dropped (it was only archived). Changes state: journaled in AMPLE Redux as a command.
POST
/api/history/tag
Creates a locked tag. Changes state: journaled in AMPLE Redux as a command.
library
Method
Path
What it does
GET
/api/bundles
The library bundles: blocks saved with their files, seeds and audio, with the slots a user may swap. Read only.
POST
/api/bundles/place
Adds a library bundle to the open song under new names (a prefix or a name per block), copies the files it needs and gives the blocks the audio they were saved with. Changes state: journaled in AMPLE Redux as a command.
POST
/api/bundles/save
Saves blocks of the open song as a library bundle: their text with the seeds they have, the files they read and the audio of their takes. Changes state: journaled in AMPLE Redux as a command.
DELETE
/api/bundles/{id}
Removes a bundle from the library (the songs that used it keep their blocks). Changes state: journaled in AMPLE Redux as a command.
GET
/api/library
The user's library: linked folders with their live state, the assets (up to 5000) and groups of duplicates. Read only.
POST
/api/library/access
Makes a linked folder read-only or read-write. Changes state: journaled in AMPLE Redux as a command.
POST
/api/library/describe
Sets tags, license and credit of an asset. Changes state: journaled in AMPLE Redux as a command.
POST
/api/library/import
Copies an asset into the project (assets/) with a provenance file; the write is journaled in AMPLE Redux. Changes state: journaled in AMPLE Redux as a command.
POST
/api/library/mounts
Links a folder and scans it (read-only unless readOnly is false; scope project makes it visible to this project only). Changes state: journaled in AMPLE Redux as a command.
POST
/api/library/relocate
Points a linked folder at its new place after an identity check (force skips the check). No song changes. Changes state: journaled in AMPLE Redux as a command.
POST
/api/library/scan
Scans a linked folder incrementally. Changes state: journaled in AMPLE Redux as a command.
GET
/api/library/uses
Where the open song uses catalog assets (block and parameter). Read only.
GET
/api/lock
ample.lock checked against the catalog: ok, changed, missing or offline per entry. Read only.
POST
/api/lock/update
Works out what the songs use and writes ample.lock (accepts changed assets). Changes state: journaled in AMPLE Redux as a command.
modules
Method
Path
What it does
POST
/api/midi/module
Converts a project MIDI file into a generated module through the project command bus. Changes state: journaled in AMPLE Redux as a command.
POST
/api/midi/module-activate
Saves an edited MIDI module, exports its active MIDI variant through the command bus and updates the associated MIDI block working copy. Changes state: journaled in AMPLE Redux as a command.
GET
/api/module/doc
Opens the editable block body of a project module. Read only.
PUT
/api/module/doc
Updates a project module body in its working copy with optimistic concurrency. Changes state: journaled in AMPLE Redux as a command.
POST
/api/module/doc/edit
Applies one typed graph edit to a project module body. Changes state: journaled in AMPLE Redux as a command.
POST
/api/module/doc/save
Writes the module body through the project command bus. Changes state: journaled in AMPLE Redux as a command.
GET
/api/module/source
Opens the complete source text of a project module. Read only.
PUT
/api/module/source
Updates complete module source in the working copy with optimistic concurrency. Changes state: journaled in AMPLE Redux as a command.
package
Method
Path
What it does
POST
/api/tx/export
Writes an .ampletx package (Full or Derived) of a saved song into the project; an existing package is never overwritten. With compress the same file is also put in a zip. Changes state: journaled in AMPLE Redux as a command.
GET
/api/tx/export/preview
What an .ampletx export would do (Full or Derived): the files that go in with their hashes, the Derived boundaries and what stays editable, the warnings and the approximate size. Nothing is written. The song must be saved and its final result must build. Read only.
POST
/api/tx/flac/approve
Records that the user listened to the FLAC test and accepts the result; the export with FLAC needs it. Changes state: journaled in AMPLE Redux as a command.
GET
/api/tx/flac/audio
Streams one of the two masters of a FLAC listening test (which=before or after). Read only.
POST
/api/tx/flac/preview
The listening test of the FLAC option of a Derived export: the result built with the original audio inputs (before) and with the audio inputs that went through FLAC (after), and how each audio input came back. Nothing is written to the project. Changes state: journaled in AMPLE Redux as a command.
POST
/api/tx/import/apply
Makes an editable project of a package: into a project (anyway or convert) or as a new project with a new identity. The package is only read; nothing existing is overwritten. Changes state: journaled in AMPLE Redux as a command.
POST
/api/tx/import/plan
What importing a package would do, before anything is written: the differences with this environment and what they can mean, and for every file whether it is already there (same SHA-256), can be relinked, or is extracted (and where). Changes state: journaled in AMPLE Redux as a command.
GET
/api/tx/inspect
Opens an .ampletx package read-only (a plain package or one in a zip, in the project or at an absolute path): what it is, its settings and dependencies, its files with their hashes, the song text and whether a master preview is inside. Nothing is changed. Read only.
POST
/api/tx/mark
Sets or clears the Derived export marker of a block in the song text (bake: use the rendered output of this block as a boundary; as-is: always include this source as it is; none: clear). It changes the working copy of the song like any edit. Changes state: journaled in AMPLE Redux as a command.
GET
/api/tx/master
Streams the master preview of a package (taken out of it into a temporary file, nothing is built). Read only.
plugins
Method
Path
What it does
GET
/api/gpu
The free and total GPU memory (totals only) and the plugin processes that are kept warm. Read only.
POST
/api/gpu/release
Stops the kept-warm plugin processes now so their GPU memory is given back. Changes state: journaled in AMPLE Redux as a command.
GET
/api/plugins
The plugins that are found and any problems with them. Read only.
GET
/api/plugins/settings
The settings schema of a plugin and the values stored in this project (defaults filled in). Read only.
PUT
/api/plugins/settings
Validates and stores the settings of a plugin in the project (secrets only by reference). Changes state: journaled in AMPLE Redux as a command.
project
Method
Path
What it does
GET
/api/faer/status
Machine-local Faer connection, aggregate capability readiness and installed ACE model filenames, roles and sizes, without secrets or job content. Read only.
GET
/api/file
Reads a text file of the project (lyrics, chords, notes, data); other files are refused. Read only.
PUT
/api/file
Writes a text file of the project through the command bus (AMPLE Redux keeps the previous text). Changes state: journaled in AMPLE Redux as a command.
GET
/api/packs/files
The WAV files of the installed audio packs as pack:<pack>/<name> values for a file field. Read only.
GET
/api/project
The open project: id, name, folder, master seed and last Studio song. Read only.
GET
/api/project/audio
Streams a project audio file with range support for the shared player. Read only.
GET
/api/project/auto-checkpoint
The autosave setting (in project.json): whether every save gets checkpoints, and the autosave interval in minutes (with the allowed steps). Read only.
POST
/api/project/auto-checkpoint
Turns the checkpoint-on-save and autosave setting on or off and sets its interval (one of the steps). Changes state: journaled in AMPLE Redux as a command.
POST
/api/project/convert-audio
Decodes an MP3, Ogg or FLAC file of the project to a WAV next to it (ffmpeg, 16-bit PCM, same sample rate) so blocks can play it; an existing WAV of the same name is reused. Changes state: journaled in AMPLE Redux as a command.
POST
/api/project/copy
Copies a project file or folder (with everything in it) to a new path through the command bus; the destination must not exist yet. Changes state: journaled in AMPLE Redux as a command.
GET
/api/project/export
Downloads an original project file without converting it. Read only.
POST
/api/project/export
Starts a cancellable audio conversion or folder ZIP export in temporary storage. Changes state: journaled in AMPLE Redux as a command.
POST
/api/project/export/cancel
Cancels an export job without changing the source files. Changes state: journaled in AMPLE Redux as a command.
GET
/api/project/export/download
Downloads a completed temporary export (available for 30 minutes). Read only.
GET
/api/project/export/status
Progress of a temporary export job. Read only.
DELETE
/api/project/file
Deletes one project file through the command bus with a recoverable pre-image. Changes state: journaled in AMPLE Redux as a command.
GET
/api/project/files
The files of the project (without .ample), for the file tree. Read only.
DELETE
/api/project/folder
Deletes one empty project folder through the command bus with a recoverable pre-image. Changes state: journaled in AMPLE Redux as a command.
POST
/api/project/folder
Creates a project folder through the command bus. Changes state: journaled in AMPLE Redux as a command.
GET
/api/project/hash
Returns the SHA-256 digest of one project file without entering .ample or following links. Read only.
GET
/api/project/kinds
The kinds of file and folder AMPLE can create in a project (the Add menu of the project panel). Read only.
POST
/api/project/last-song
Remembers the last Studio song in the user's recent-project record. Changes state: journaled in AMPLE Redux as a command.
POST
/api/project/move
Moves a project file or folder to an unused path through the command bus; by default every path in the songs and modules that points at it follows (refactor), refactor false moves only the file, dryRun true returns the edits without moving. Changes state: journaled in AMPLE Redux as a command.
POST
/api/project/new
Creates a file or folder of a kind (song, lyrics, chords, MIDI clip, module, sample folder, note, folder) with a valid skeleton through the command bus; the name is checked and an existing path is refused. Changes state: journaled in AMPLE Redux as a command.
GET
/api/project/peaks
Returns a bounded, cached waveform summary for a supported project WAV file. Read only.
GET
/api/project/preview
Returns bounded cached song, lyric or MIDI metadata for project-file thumbnails. Read only.
POST
/api/project/rename
Moves a project file or folder to an unused path through the command bus; by default every path in the songs and modules that points at it follows (refactor), refactor false moves only the file, dryRun true returns the edits without moving. Changes state: journaled in AMPLE Redux as a command.
GET
/api/project/settings
The project settings: AMPLE Redux budget and retention, default engine, Faer routes, linked folders, master seed (as text). Read only.
POST
/api/project/settings
Changes AMPLE Redux retention, the default engine and independent Faer capability routes (the master seed is a dangerous act of its own). Changes state: journaled in AMPLE Redux as a command.
GET
/api/project/tree
Lists one project folder and its immediate contents without following links or entering .ample; names that start with a dot (such as .git) are left out unless hidden=1. Read only.
POST
/api/project/upload
Imports a project file up to the configured byte limit through the command bus with explicit conflict handling. Changes state: journaled in AMPLE Redux as a command.
GET
/api/project/usage
Finds song and module parameters that reference a project file or a file under a project folder. Read only.
GET
/api/samples
The sample sets of the project: folders (up to three levels deep, outside .ample) that hold WAV files, with the base names (lower case, without .wav and without a layer or round robin suffix), how many layers and round robins are on disk (layers, roundRobins, and per voice in voices), the set.json numbers when present (offered), and how many names are notes (notes). Read only.
POST
/api/songs
Creates a new song in the project with a settings line holding the project defaults of the plugins (the project-level plugin settings are only the starting point for new songs). Changes state: journaled in AMPLE Redux as a command.
projects
Method
Path
What it does
GET
/api/projects
The recent projects with a short health summary each (folder reachable, findings, age of the last backup). Read only.
POST
/api/projects/forget
Removes a project from the recent list (the folder is not touched). Changes state: journaled in AMPLE Redux as a command.
POST
/api/projects/new
Creates a new project folder (it is not opened). Changes state: journaled in AMPLE Redux as a command.
POST
/api/projects/open
Opens another project in a server of its own and returns its address (this server keeps its project). Changes state: journaled in AMPLE Redux as a command.
redux
Method
Path
What it does
POST
/api/redux/compact
Explicitly compacts AMPLE Redux after the caller has reviewed the impact preview. Changes state: journaled in AMPLE Redux as a command.
GET
/api/redux/compact-preview
Previews the exact records and bytes an explicit AMPLE Redux compaction would affect. Read only.
GET
/api/redux/edit-history
Read-only exact-hash lookup of saved song or module states in AMPLE Redux, without exposing journal contents. Read only.
GET
/api/redux/records
Read-only, bounded search through AMPLE Redux records. Cursor pages stay pinned to the first page's high-water sequence. Read only.
GET
/api/redux/status
Aggregate AMPLE Redux storage use and budget warning thresholds; no record contents are returned. Read only.
server
Method
Path
What it does
POST
/api/server/shutdown
Stops the server and releases the project. Changes state: journaled in AMPLE Redux as a command.
storage
Method
Path
What it does
GET
/api/storage
What the takebank and the working folders use, what is protected and what could be given back, the options and the state of a clean-up. Read only.
POST
/api/storage/cancel
Stops a running clean-up between items. Read only.
POST
/api/storage/clean
Starts the clean-up of a dry run in the background (its fingerprint must still match); progress goes to the event stream. Changes state: journaled in AMPLE Redux as a command.
POST
/api/storage/compress
Compresses the plain WAV takes of the project in place, losslessly (the takes and their ids do not change), in the background with progress. Changes state: journaled in AMPLE Redux as a command.
POST
/api/storage/move
Moves the take data to another folder or drive in the background, with progress (an empty path moves it back into the project); a stopped move continues when run again. Changes state: journaled in AMPLE Redux as a command.
POST
/api/storage/plan
The dry run of a clean-up: takes and data that would go, per reason and per block, what stays protected, free space before and after. Read only.
POST
/api/storage/settings
Changes the storage options of the project (soft limit, how many older takes stay, keep window, archive folder, intermediates). Changes state: journaled in AMPLE Redux as a command.
GET
/api/storage/status
The state and progress of the clean-up. Read only.
takes
Method
Path
What it does
GET
/api/data/{id}
A stored blob (supports range requests for audio; WAV data is served as audio/wav). Read only.
GET
/api/plans/{id}
A stored plan (the complete recipe). Read only.
GET
/api/takes
Lists takes, newest first (optionally of one block). Read only.
GET
/api/takes/{id}
One take with its plan and any missing data. Read only.
GET
/api/takes/{id}/download
Downloads a take as its original WAV or as a real MP3 encoded by ffmpeg. Read only.
POST
/api/takes/{id}/export
Writes the audio of a take to a file in the project (WAV as it is, or MP3 encoded by ffmpeg) through the command bus; an existing name is refused. Changes state: journaled in AMPLE Redux as a command.
GET
/api/takes/{id}/peaks
Min and max pairs of the audio of a take (points pairs, values -1 to 1) for drawing a waveform; computed in one pass over the file and cached. Read only.
GET
/api/takes/{id}/verify
Rehashes the data of a take. Read only.
AMPLE overview
Status (2026-10-11): this is AMPLE's design and knowledge base, not a statement that every described idea is implemented. Each feature page carries its own built/planned status; use the build plan for chunk status. The rationale below is historical. Mock examples and proposed CLI sessions are labelled on their pages.
Origin: two favourite songs, "Always" and "Serendipity", could not be reproduced because their seeds were never stored; AMPLE is built so that this cannot happen again (the "Always rule" in Decisions).
The original goal was a clean, standalone tool (C#) for writing songs as text: instrument tracks (textual MIDI) and AI voices/samples on one timeline, reproducible by default, with a web UI (plain HTML/JS/CSS on a local server, also on Windows). It does not depend on the prompt bridge or prompt-music; those (and the ACE-Step findings in Prompt bridge) are only background knowledge.
Building it: the build plan is in ../plan/ (chunks prefixed with their priority, lowest first). The language specification is in language/ (normative, kept maintained); the generated help.html comes from chunk 12.
What a project folder contains and how it is protected: .ample/, format versions and upgrades, the one-writer lock, atomic writes, safe paths, and the new/open/doctor commands.
Immutable takes and the plans they were made from: the Always rule enforced in the API, the database and the files, canonical plan ids, content-addressed data, attempts, verification, orphans.
ample lsp and the /lsp WebSocket: capabilities, transports, measured budgets, decisions and what is not done; the VS Code extension and the generated grammar.
How a coding agent works when asked to conserve usage: concise narration, safe batching of reads and edits, guarded edit scripts, focused verification.
What the web app does today (layout, palette, canvas, ports and links, groups, properties, undo, build), what is not yet there, honest differences from the sketch, how to run the browser tests.
The plugin manifest, the JSON-RPC protocol version 1 and what the host guarantees: clean environment, job folder, timeouts, crash containment, one GPU job at a time.
Optional per-project routing for ACE song and voice generation, voice cloning and stem separation to the authenticated Trooper.ai service; GPU acceptance checks remain.
The plugin for an acestep.cpp server: the assumed server API (unverified), the rules it follows, the stand-in server and the manual test for the real server.
The warning that AI regeneration is only guaranteed locally, and the safe regen flow: old take set aside, result verified, default "restore previous", crash-safe pending decision.
What the producer can pin, tweak and expect from an AI block: the freeze ladder (seeds, melody plan, take) and the "if I change X" table, with measured versus expected marked.
Everything a song uses (catalogs, packs, voices, plugins, templates, data), where it is used, replace with impact analysis, folders, global catalogs as linked folders.
In detail: project folder and .ample/ layout, lifecycle (create, open, duplicate, archive, delete), templates, settings scopes, health checks, backup and restore, migration, the dangerous-act pattern.
What is open after the long session of 2026-10-10: gaps, failures that were already there, voice clone and GPU, the AI assistant, and the design for moves proposed by Codex.
The learning help with pictures and a dictionary (open it with the Guide link in the app, or at /guide/); how it is made and kept up to date; the doc style holds the house style rules.
One HTML file (plain JS, open it in a browser) that is also a knowledge base: ComfyUI-like canvas with groups and minimap, history and structure browser, variants, build with cache and preview playback, linked folders. Machine-readable notes inside (see its README). Mock only.
What the commands could look like (invented output).
Example files (examples/)
17-splitter-branches duplicates mono drums into independent EQ and delay/reverb branches, with volume/channel controls and a stereo mix.
25-piano-roll-tour is a sample-free editable clip for exploring piano-roll tools and per-note properties.
01-hello instruments only · 02-hybrid instruments + AI voices + overlap + booth directions · 04-remix importing audio, MIDI, old recipes, stems · 05-lyrics timed words, checks, queries, retry policy · 16-audio-vs-render compares full effect chains on materialized audio and render recipes · 21-fades a crossfade between two drum patterns, a fade out as a formula and a fade block in seconds · 22-sections named drum patterns put in order · 23-sequencer a sequencer block with channels, patterns, a playlist and lanes · 24-tutorial-song a full band song (sequencer, sample kit, guitar, a General MIDI choir and effect chains), built step by step in the guide · packs/ soundset manifests · plugins/acestep/plugin.json · ample.lock.
How the blocks make a song
Status (2026-10-02): design sketch from a brainstorm. Nothing here is built; the clickable sketch only simulates it (its preview plays a stand-in synth, but it does follow the chords you type for piano and bass).
1. The idea in one sentence
The graph is a dependency graph of typed values on one shared timeline. A link does not carry sound first; it says "this block needs the result of that block". Building walks the graph from the sources to the master and each block produces its typed output, which is cached by hash.
2. Four kinds of values (the port colours)
Type
What flows
Example
midi (notes)
events with a position on the timeline: bar.beat, length, pitch or chord, velocity
chords produces Am F C G, one chord per bar, as note events
text
words with timing hints
lyrics: each line or word has a target time (bar, seconds, first 5 s checks)
data
recorded facts, usually locked
weather fetched once, frozen with lock (for example "rainy, 11 degrees")
audio
a rendered signal placed on the timeline
drum kit output, piano, a vocal take, the mix
3. The shared timeline is what keeps everything in sync
There is one tempo map (tempo, time signature, changes) and one bar grid. Bars and beats convert to seconds through that map.
Every block that makes something is anchored on the timeline: at bar 9 for 8 bars. The arrangement view shows exactly these anchors; the block graph shows how the blocks depend on each other. Same project, two views of the same text.
Notes and drum triggers are exact: they are events at known positions, so two blocks that read the same chords are in sync by construction, to the sample.
4. Walking through the example graph
chords (stable, textual MIDI): Am F C G, 8 bars. Output notes = chord events (bar 1 Am, bar 2 F, ...). This is the harmonic spine.
piano reads notes and plays voicings of each chord with its soundset (a sampler, pinned by hash). Output: audio, exact and repeatable.
bass reads notes too and plays a pattern (for example the root on beats 1 and 3, a fill before a chord change). Same events, same timing, so it locks with the piano.
drums have a trigger input that is optional: by default they play their pattern against the bar grid; connecting chords lets a pattern accent chord changes. Output: audio.
lyrics produce timed words. weather produces data (mood words, tempo hint, season).
chorus_vox (dynamic, an AI voice) takes lyrics, melody (the chords, which give key and progression, and optionally a melody line) and style (the weather data) and builds a recipe for the voice engine: caption/tags, lyrics, tempo, key, length, a seed derived from the master seed and the block name. The engine returns a take (audio) that is stored in the take bank by hash.
mixer sums the audio inputs on the timeline at their anchors with gains and pans. master (EQ, compressor, limiter) shapes the sum; its output is the master audio you play or release.
5. Where "sync" is exact and where it is measured
Part
How it syncs
Honest note
Piano, bass, drums, any MIDI-driven sampler
by construction: events on the shared grid
exact, deterministic
Audio imports (a recorded loop)
placed on the grid; optionally warped or trimmed with a stated policy
exact placement, warping is a choice
AI voice
measured, then checked: the take has its own length and timing
the model does not obey a bar grid exactly; AMPLE places the take, measures word timing (opt-in plugin), compares with the lyric hints, and reports the error; variants let you pick the take that sits best; policies: warn (default), retry within a budget, pick best, nudge
Harmony between voice and band
the voice is conditioned on key, tempo and chord progression
following a given melody exactly is not proven yet; chords constrain the style, not every note
6. What a build does
Order the needed blocks (sources first).
For each block compute its cache key from template, version, parameters, node seed, baked flag and the keys of everything upstream. A matching cache means "will only be checked" (greyish green); no match means "must run" (grey).
Run block by block: MIDI and sampler blocks are cheap and exact; dynamic blocks call plugins asynchronously with progress and store takes; each finished block lights up.
Change the chords: the chords block, piano, bass, voice, mixer and master turn grey again, drums and lyrics stay cached.
Preview of any block with an audio output builds what is missing first, then plays that block (for the mixer or master: everything upstream mixed together).
7. Where the song actually lives
The text (.amplet) is the song; the graph and the arrangement are views of it, and visual edits become patches to the text. A tiny excerpt of what the text could look like:
project "my-song" { tempo 92 master-seed 424242 }
midi chords { chords "Am F C G" bars 8 }
sampler piano using felt-piano { from chords.notes }
sampler bass using analog-808 { from chords.notes pattern root-1-3 }
sampler drums using kit/tape { pattern beat-A }
voice chorus_vox using anna { lyrics chorus melody chords.notes style weather at bar 9 for 8 bars }
mixer mix { drums, bass, piano, chorus_vox gains 0 0 -2 0 }
master { limiter -1 dB target -14 LUFS }
8. What the sketch does and does not do
It follows the dependency graph, the cache colours and the progress per block.
Its preview sound is a stand-in synthesizer: piano and bass follow the chords you type in the chords block (one chord per bar, major or minor by name), drums play a fixed pattern, the voice is a seeded tone line. It is not the real engine and the voice is not a real take.
It does not measure word timing, place takes with warping, or apply the voice-engine recipe; those are designed in Variants, Bake, Modules and Tech.
One song, three views: node editor, arrangement, source
Status (2026-10-02): design sketch from a brainstorm. The syntax below is the MOCK syntax used by the clickable sketch (sketch/index.html); it is not a final language design, but it is real enough that the sketch parses, validates and applies it.
The song is text (song.amplet). The node editor, the arrangement and the source editor are three views of that text. Edit in any of them and the other two follow.
1. The three views
View
Good for
Writes to the text
Node editor (blocks)
structure: what feeds what, parameters, groups, templates
blocks, parameters, from ... lines, layout
Arrangement (timeline below)
time: where each block sounds
the at <bar> for <bars> [as <name>] part of the block header
Source (text)
precision, bulk edits, copy and paste, reading
everything
2. The arrangement is editable
Drag a bar to move it, drag its edges to resize (snaps to whole bars). Several parts per block are allowed (a piano with verse and chorus).
Double-click a bar: go to that block's source line. Double-click empty space on a track: a new 4-bar part. Right-click: go to source, select in the node editor, rename the part, duplicate, split, delete.
Blocks without a placement can be added with the dropdown above the timeline.
An edit changes the text (and the cache keys: a moved block is a changed block, so it and what depends on it turn grey for the next build) and shows the working copy as unsaved.
3. The source view
Switch the canvas area from Blocks to Source.
┌ files ────────────┬ song.amplet [Apply] [Revert] [Show block in node editor] status ┐
│ ✎ song.amplet ● │ 1 # song.amplet ... │
│ ✎ lyrics/chorus │ 2 project "my-song" { tempo 92 master-seed 424242 } │
│ ✎ chords/main │ ... editor with line numbers; ● error and ▲ warning markers in the gutter │
│ 🔒 ample.lock ├────────────────────────────────────────────────────────────────────────────────────────┤
│ │ problems: ● line 43: Unknown parameter "patern" for drums. Did you mean "pattern"? │
└───────────────────┴────────────────────────────────────────────────────────────────────────────────────────┘
Files on the left:song.amplet (the graph), plain text files the blocks refer to (lyrics, chords), and generated read-only files (ample.lock). Only song.amplet changes the node editor.
Live validation while typing (a short pause), with a problems list; click a problem to jump to the line.
A valid song edit syncs to the working copy (graph, arrangement and other views) after a short pause. Save makes the revision; Ctrl+Enter confirms the current buffer as applied.
Revert drops invalid or not-yet-synced edits and returns to the current working copy.
While the text cursor is inside a block in a valid song, the Properties panel shows that block. Source and property edits sync both ways; while the code has errors, the properties panel explains that sync is paused until the code is valid again.
Show block in the node editor jumps to the block under the cursor (needs a valid song).
Leaving the source view (to Blocks or the Branch manager) applies first; if there are errors the switch is refused with a message. A valid song switches back and every other view updates.
A project that opens with mistakes starts in the source view and stays there until the errors are fixed (or reverted). In the sketch, open always-remix in the Projects manager to see it.
Dirty non-song source files can be closed by saving, keeping a recovery draft, discarding or cancelling. Recovery drafts are offered when each file is next opened; a manual checkpoint saves pending source drafts first and stops if a file changed since its draft was made.
When safe saves are enabled, recovery saves in that manual checkpoint batch skip their individual auto-checkpoints; the final manual checkpoint remains the single snapshot for the batch.
File writes and recovery records are journaled in AMPLE Redux. Its current on-disk folder remains .ample/blackbox for compatibility; compaction is explicit and uses the project budget.
4. Mock syntax
project "my-song" { tempo 92 master-seed 424242 }
features { quality off credits off signing off secrets on lyrics on git-mirror off assist diff }
drums drums stable@1.2 at 1 for 16 as "beat A" {
kit kit/tape
pattern beat-A
bars 16
}
piano piano pinned@2.0 at 1 for 8 as verse, 9 for 8 as chorus {
set felt-piano
from chords.notes -> notes
}
ai-voice chorus_vox live@0.9 at 9 for 8 as "chorus voice" {
voice anna
from lyrics.words -> lyrics
from chords.notes -> melody
from weather.data -> style
}
layout drums 280 20
Header: <block type> <name> <class>@<version> [at <bar> for <bars> [as <name>] (, ...)] [bake] [lock] [refresh] {. Body: <parameter> <value> lines and from <block>.<output> -> <input> lines. Lines starting with # are comments. layout lines (at the end) keep node positions. Names with spaces or odd characters are written in quotes. The inner graph of a group or of a block like the AI voice is edited in the node editor; the text shows a comment line for it.
5. What validation checks
Check
Message idea
Blocks leaving?
Project line present, tempo 20 to 300, seed an integer
"Tempo must be between 20 and 300."
yes
Unknown block type, class or version malformed
"Unknown block type 'drumz'. Did you mean 'drums'?"
yes
Unknown or duplicate parameter
"Unknown parameter 'patern' for drums. Did you mean 'pattern'?"
yes
Duplicate block names
"Two blocks are called 'bass'."
yes
from points to a missing block, output or input
"There is no block named 'chordz'. Did you mean 'chords'?"
yes
Type mismatch between a connected output and input
"Type mismatch: x.vocal is audio but y.lyrics wants text."
yes
An input with two sources, a block reading itself
"Input 'in2' of 'mixer' already has a source."
yes
Cycles
"Cycle: a → b → a"
yes
Unclosed block, bad header, bar numbers below 1
"Block 'x' is not closed (missing '}')."
yes
A block needs a plugin that is not installed
"Block 'x' needs the plugin 'timing', which is not installed (opt-in)."
no (warning)
A part reaches past bar 1024
warning
no
6. How Apply maps text to the graph
Blocks are matched by name: the same name and type keeps its identity, its inside (groups, templates) and its cache state; a new name creates a block from the template; a block that is gone from the text is removed (the number is reported).
Parameters, class, version, parts and flags come from the text; missing parameters get the template default; links are rebuilt from the from lines.
Positions come from the layout lines, or a free spot for new blocks.
Applying text that equals the current graph changes nothing (cache keys stay equal).
7. Interaction with history
Apply and arrangement edits change the working copy; the unsaved badge compares the text with the last saved text. Save makes a revision (and clears the badge). Going to another revision with unsaved work takes an automatic checkpoint first (see History management, "Unsaved work").
Errors never reach a revision: a revision is only made from a valid song.
8. Open questions
Should text edits made in another editor (VS Code) while AMPLE is open be picked up (file watcher) and validated the same way?
Should the inner graphs of groups and templates be editable in the same text (nested blocks), or stay node-editor-only?
Should comments and formatting be preserved when the text is regenerated from the graph (a lossless round trip), or is a normalised form acceptable?
Should the source view offer completion and inline docs (block types, ports, parameters) from the templates?
9. Dedicated editors (lyrics)
Some blocks deserve a real editor of their own. The first one in the sketch is the lyrics editor.
Open it with the … button next to the file parameter, Edit lyrics in the block menu, the … Edit lyrics button in the properties, or a double-click on the block.
It takes over the canvas area (full screen in the editor area) instead of showing the node view. The file the block points to (lyrics/chorus.lyr) is the data: one line per row, [bar 9] text.
Rows: bar or seconds (switch above), text, the other unit as a read-only column, move up or down, insert after, delete. A bar can be fractional (9.5).
Checks, live: first line within 5 s (the block's first5s setting), lines in order, lines outside the block's placement, empty lines, long lines, too many words for the gap. A timing strip shows where the lines sit inside the placement and colours the problems.
Import / export: paste .lrc or .srt (times become bars at the project tempo), export .lrc.
Undo and Redo, one step at a time (Ctrl+Z, Ctrl+Y outside text fields; the buttons show how many steps are left) work for edits, moves, inserts, deletes, imports and sorting.
Sort by time: a button orders the lines by bar (stable: lines on the same bar keep their order) and is only enabled while the lines are out of order. The check panel offers the same fix next to an order error, and auto-sort on apply (on by default) sorts silently when you press Apply and back.
Linking the views: hovering a mark in the timing strip highlights the matching row (and scrolls it into view); hovering a row or a check message highlights its mark in the strip, and editing a row (focusing its bar or text field) keeps its mark highlighted in a stronger colour while you type, so the mark moves live when you change the bar.
Two ways back:Apply and back (keeps the changes in the working copy, makes no revision, refused while there are errors) and Undo and back (throws the session's changes away, with a confirmation if anything changed).
The same pattern is meant for other blocks later (chords, drum patterns, a mixer, an automation lane): a block can offer a dedicated editor, always with Apply and back / Undo and back.
10. Ctrl+click navigation and read-only
Hold Ctrl (Cmd): in the source editor the elements that lead somewhere light up: block names (in headers and in from lines), both ends of a connection, a placement (at ...), a lyrics file, a block type. The cursor becomes a pointer over them and a label says what a click does.
Ctrl+click opens the node editor on that element and selects it. On the -> arrow of a from line, it selects and highlights the represented connection, then opens that connection's context menu. A placement also shows the arrangement, a lyrics file opens the lyrics editor, and a type opens its page in the language reference.
With errors in the text a popup asks: Fix first (stay, jump to the first error) or Show read-only. Read-only shows the last valid version of the node editor, with the element selected if it exists there ("does not exist in the last valid version" otherwise). A banner says so and offers Back to the editor.
Read-only means: no moving, connecting, adding, deleting, grouping, parameter or arrangement edits; selecting, zooming, opening groups and going to the source still work. It stays read-only until you go back to the source editor; entering the source view ends it, and getting back to the node editor then needs a valid song again (or the same popup).
The plain Blocks button uses the same popup when the text has errors.
11. The lock file (ample.lock)
ample.lock is generated and read-only. It pins everything the song depends on that is not written in the song text, so the song reproduces later: plugin versions with their toolprints, soundset and sample packs by hash, templates and pinned blocks by hash, locked external data (the recorded weather value, when it was fetched, its hash) and model files. It plays the role of package-lock.json: the text says what you want, the lock says exactly what you got. It is part of every revision, changes only when you update a plugin, a pack or a pin, or lock external data, and verify and upgrade-check compare the present setup with it. See Determinism and Tech.
Recording your voice
The arrangement panel has a Record... button. It opens a small recorder that works on your own computer and only sends something to the AMPLE server when you say so.
Steps
Choose the microphone. The level meter moves when the browser hears sound. The first time, the browser asks for permission; if it was blocked, allow the microphone in the address bar and open the recorder again. The page must be localhost or HTTPS.
Record. Optional: a count-in of four clicks at the tempo of the song, and playing the built song while you sing (put on headphones, or the microphone hears the music). The recording is a raw take: the browser's noise suppression, echo cancellation and automatic gain are off unless you switch them on.
Listen, trim, retake. Every recording is a take in the list. Select one to see its waveform, move the start and end sliders to trim, tick normalise to bring the peak to 90 percent, and press Listen. Nothing has left your browser yet.
Latency. Monitoring through the browser adds a small delay, so a voice recorded to the song lands slightly late. Type the delay in milliseconds, or press Calibrate latency: it plays a click through the speakers and listens for it, so keep the microphone near the speaker. The number is subtracted from the start of the take.
Use in project. The chosen take (with trim, latency and normalise applied) is uploaded as a 16-bit mono WAV into the project folder recordings/ (an existing name gets a new one) and an import-audio block called recording points at it. The upload goes through the normal project upload of the server, so it also works when the server runs on another computer.
After recording
Clean the voice with blocks: a gate removes the noise between phrases, a deesser tames harsh s sounds, then a compressor, eq and reverb. Everything after the upload is ordinary AMPLE, so the take is stored in the project and reproducible.
Limits
The count-in clicks are scheduled on the audio clock and the take is cut at the frame of the downbeat (bar 1 of the song), so the count-in is not in the take and the start is exact on the clock of the microphone. The built song is started with a plain play call at that moment (a browser audio element cannot be scheduled), so what is left is the delay between the play call and the sound coming out of your speakers; the latency number (or Calibrate latency, which now measures from the frame the click was scheduled at) covers that.
Only one take is uploaded at a time.
Determinism in AMPLE
Status (2026-10-02): design sketch from a brainstorm. Nothing here is built or tested.
Default: strict
require reproducible is on. A build fails if a node is not reproducible and has not been recorded.
Four levels (declared by every plugin)
Level
Meaning
Examples
exact
same input = same bytes on the declared platform
rendering notes, mixing, alignment math
machine-exact
same on the same machine/build/driver; different elsewhere
A check has a severity: warn only reports, error fails the node.
A retry policy must be explicit and bounded: retry up to N takes until checks pass. Each attempt gets a fixed, derivable seed
(name + attempt number), so the sequence of attempts is reproducible; the chosen take is frozen.
On failure the policy says what happens (keep best, fail build), never an endless loop.
How it stays true
ample verify runs a plugin's self-test twice and compares hashes; a plugin that lies about its level is downgraded.
A platform fingerprint (os, gpu, driver, plugin build) is stored with machine-exact results; on another machine AMPLE uses the frozen audio and warns before any re-render that would differ.
Seeds per node are derived from the node's name, not its position: adding a section shifts nothing.
Internally always lossless (WAV/FLAC, 24-bit); mp3 only on export.
Honest limit: bit-exact floating-point results across different CPUs cannot be guaranteed in general; "exact" means "same hash on the declared platform class".
What is guaranteed (three layers): (1) a stored take plus the deterministic mix is bit-identical, verified by hash; (2) everything AMPLE renders itself (built-in blocks, mixer, PRNG) is identical on every supported platform, proven by golden hashes; (3) regenerating an AI block is only guaranteed on the same machine and toolprint. For (3) the app shows a permanent warning and runs the safe regeneration flow (old take set aside, result verified, default answer "restore previous"): see Regeneration safety.
Generation recipe audit (2026-10-17)
Seeds alone are insufficient. The CFG investigation exposed missing effective defaults and discarded plugin provenance; see the incident and mandatory end-to-end checklist. Preserve the full recipe and immutable reference audio. A comparison with changed models/software may proceed with warnings; byte identity must be measured rather than inferred from a seed.
Regeneration safety
Status (2026-10-02): built in chunk 58 (core service and CLI: ample regen, verify-reproduction, explain); the dialog and the health page entries come with chunks 76 and 77.
The producer controls now offer Check reproduction… using the reference seed and current song/settings/software. The reference stays selected; configuration and provenance drift produce warnings. This is a comparison, not automatic replay of an older recipe. Recipe metadata can create a distinct take even when its audio bytes match. See the recipe audit and review checklist.
The rule in one line: regenerating an AI block is only guaranteed to give the same result on the same machine and the same toolprint, so AMPLE treats every regeneration as a risky act: the old take is set aside first, the result is verified against it, and if it differs the default answer is restore the previous take.
The warning (always visible)
On every AI block, on the regenerate button and in the freeze explainer:
Careful: regeneration is only guaranteed to give the same result on this machine with the same plugin, model and GPU setup. On another machine, driver or version it may sound different. Your current take is kept safe either way.
The text lives in the spec (diagnostic/notice code AMP5101), so the UI, CLI and help show the same words. The block's toolprint (plugin, version, model hash, coarse hardware class such as GPU model and driver) is shown next to it, and the warning is stronger (AMP5102) when the current machine's toolprint differs from the one that made the take, or when the plugin's determinism is unverified.
The flow when the user regenerates ("regen")
Plan first (Always rule). A new plan record is stored with parentPlanId = the plan of the current take, and a journal record regen.started is written first (write-ahead, chunk 45).
Set the old version aside. The current take is not touched (takes are immutable); it is pinned (protected from cleanup, included in the next backup, and a backup is suggested right now if the last good backup is older than the take) and marked fallback for this block. The block keeps playing the old take while the new one renders.
Render the new take (preview of progress as usual). Failure or cancel: the old take stays selected, a failed/cancelled plan remains, nothing else changes.
Verify automatically. Compare the new result with the old one:
identical content hash: "Same result. Nothing changed." The duplicate collapses (same content address), regen counts as a successful reproduction check and is recorded in the take's reproduction history.
not identical: measure and show how different (duration, loudness, spectral fingerprint, and for melody-plan blocks the note differences), with A/B listening.
If different, ask what to do. Options:
Restore previous take (default, pre-selected, also the result of closing or ignoring the dialog)
Keep the new take as a variant (old one stays selectable)
Replace with the new take (a dangerous act: typed acceptance, tombstone; the old take stays in history anyway)
Crash safety. The question is a pending decision stored in the journal and the history database, not just a dialog. If the app crashes, loses power or is closed while the question is open, the old take is still the active one (nothing was replaced), and on next start the health page and a dialog ask again. The default stays "restore previous". The new take is never promoted without an explicit answer.
What counts as "regen"
Re-rendering a block whose seeds are unlocked or changed, the "regenerate" and "re-sing"/"re-write" buttons, and the explicit Verify reproduction command (re-render the same plan only to check, never replaces anything: it shows "same" or "different" and records the result). Changing a locked seed or a setting first triggers the seed warning (see controllability), then this flow.
Reproduction history
Every take keeps a list of reproduction checks: when, on which toolprint, result (same / different / failed). That is how "verified on my machine" becomes a fact instead of a belief, and it feeds the determinism flag of the plugin (unverified, verified-local, verified-cross-machine).
Where it is built
Chunk 55 (render path and fallback pinning), chunk 58 (flow, verification, reproduction history, pending decision), chunk 45 (journal records regen.started/decided, recovery of pending decisions), chunks 76/77 (dialog, warning text, A/B), chunk 10 (notice codes and wording in the spec).
Controllability: what can the producer pin, tweak and expect?
Status (2026-10-02): design sketch from a brainstorm. The facts marked MEASURED come from earlier ACE-Step experiments recorded in Prompt bridge; everything else is expected behaviour that has to be measured before the producer can rely on it. The clickable sketch shows the panel (properties of an AI block, "Controls").
AI blocks are not perfectly controllable and never will be: change the words or one of the seeds and you get something else. That is not a bug to hide, it is the material. The goal is that the producer knows exactly what is pinned, what a tweak will do, and what it costs, so they can work with it instead of being surprised.
1. What the producer is really after
Not "follow this exact melody", but: get a melody I like, freeze it, then change around it as far as the model allows, and be told honestly where the limits are.
1b. The Always rule: capture first, freeze later
The freeze ladder below is about what you keep fixed on purpose. Underneath it sits a rule that needs no choice: every take stores its plan automatically, at the time it is made (seeds, audio codes, settings, words, toolprint). The reason is the lost seeds of the songs "Always" and "Serendipity": a take you love and cannot get back. With automatic capture, "Freeze the melody" is just a label on something that was already saved, and a take without a plan can only come from an import (marked "no plan").
2. The freeze ladder (for a dynamic block such as the AI voice)
| Level | What is stored | What stays the same | What you can still change |
| --- | --- | --- | --- |
| Free | nothing | nothing: every build can differ | everything |
| Seeds | the two seeds (melody seed and performance seed) | the same take, only if words, settings, tool and computer are identical | nothing safely: any input change gives another take |
| Melody (plan) | the melody codes (the language model's output) plus both seeds and the settings | the melody and the structure | re-sing (new performance seed), restyle, voice, quality settings, tweak words at your own risk |
| Take | the rendered audio (baked) | everything, bit for bit | nothing until you unbake |
Why a "plan" level exists: in ACE-Step the language model first writes audio codes (melody, structure, timing) and a second stage (the diffusion synthesis) turns the codes plus text into sound. Storing the codes freezes the melody while the sound can still be varied. See Bake for the take level and Determinism for reproducibility.
3. What happens if I change X (the table)
Columns: melody, how the words fit (do they land where they should), sound (timbre and performance). = unchanged, ≈ close, ≠ different, ? unpredictable. Expected unless marked MEASURED.
| If I change... | Seeds frozen | Melody frozen (plan) | Take frozen |
| --- | --- | --- | --- |
| the words | ≠ ≠ ≠ (new input, new melody) | = ? ≈ (words follow the stored melody only roughly: check timing, make variants) | = = = |
| style or mood tags | ≠ ≠ ≠ | = = ≈ | = = = |
| tempo or key | ≠ ≠ ≠ | ? ? ? (the codes carry their own timing: re-write instead) | = = = |
| the voice | ? = ≠ | = = ≠ | = = = |
| steps or guidance | = = ≈ | = = ≈ | = = = |
| the melody seed | ≠ ? ≠ (a new melody: re-write) | ignored: = = = | = = = |
| the performance seed | = ≈ ≠ (re-sing) | = ≈ ≠ (the safe way to get alternatives) | = = = |
| model or tool version | ≠ ≠ ≠ (reproducibility lost, new toolprint recorded) | = ≈ ≈ (stored melody re-rendered) | = = = |
| another computer | ≠ ≠ ≠MEASURED: the same seeds gave another take | = ≈ ≈ expected: re-rendered, not bit-exact | = = = |
Also MEASURED: a fixed melody seed gave a byte-identical take on the same computer; audio codes recovered from finished audio are lossy (only about a third of the digits matched), so a take cannot be turned back into its exact plan afterwards, which is why the plan must be stored at the time of the take.
4. What the producer sees (UI)
In the properties of an AI block: Frozen as (Free, Seeds, Melody plan, Take) with a one-line meaning, the table above for the current level, a legend, and buttons: Freeze the melody now, Bake the take, Re-sing..., Re-write....
Re-sing opens the variants with the melody frozen (only the performance seed varies); Re-write varies the melody seed (new melodies). The variant cards say which of the two they vary.
Changing a parameter of a frozen block is not blocked; a message says what to expect ("Changing steps while frozen as Melody: melody =, words fit =, sound ≈").
In the text it is one word in the block header: freeze seeds or freeze plan (a baked take is bake). The linter treats a frozen block as fixed (rule L005 stays quiet).
The cache key includes the freeze level, so changing it rebuilds the block and what depends on it.
5. What is stored in a plan
The plan is the "recipe plus the part you cannot recompute": the audio codes, the melody seed, the performance seed, all settings (steps, guidance, voice, tags, tempo, key, duration), the words and the toolprint (tool and model versions, platform class). With it, a re-render on the same toolprint reproduces the take; on a newer toolprint the melody stays and the sound is re-made. It lives in the take bank next to the take it came from.
6. Experiments that turn "expected" into "measured"
A small repeatable suite (run when a model or tool version changes, results stored with the toolprint):
Same seeds, same machine twice: identical? (already measured: yes.)
Same plan, different performance seed: does the melody stay (compare the codes and a melody extraction)? How different is the sound?
Same plan, changed words: how often do the words land within tolerance (word timing plugin)? How often is the melody still recognisable?
Same plan, changed tags, voice, steps: effect sizes on melody and timbre.
Same plan, other computer or newer model: how close (melody correlation, loudness, spectral distance)?
The numbers fill the table, and the symbols in the UI come from them (for example ≈ only when the measured melody correlation is above a stated threshold).
7. Relation to other parts
Variants: the variant tool gains "re-sing" and "re-write" as two ways of varying; picking a variant can freeze it as a plan or a take. See Variants.
Bake: the take level is bake; the plan level is a lighter bake that keeps the sound re-makeable. See Bake.
Language support:freeze is part of the header syntax and the diagnostics. See Language support.
History: the freeze level and the plan are part of a revision; going back to a revision restores the same freeze state.
8. CLI twins
ample freeze <block> --melody|--seeds|--take ample unfreeze <block> ample variants <block> --re-sing|--re-write
ample plan show <block> ample plan export <block> > plan.json
9. Seeds: easy to change, locked by default
Seeds are the one control the producer will touch most, so it has to be both easy and safe.
Two seeds per AI block: the melody seed (lm_seed) and the performance seed (seed). By default both are derived from the master seed and the block name, so the same song always asks for the same takes. The text can also hold a typed number (lm_seed 3297468772) or the word derived.
Locked by default. The Seeds section shows the values with a lock; the plain parameter fields for them are disabled so a stray edit cannot change them. Unlock to edit... is one click and states the consequence for the current freeze level first:
Free or Seeds: "The current take can no longer be reproduced from the new seed. It stays in the take bank and in the seed history; bake it to keep it for sure."
Melody (plan): a new melody seed has no effect until the melody is unfrozen; a new performance seed re-sings the same melody.
Take: seeds are ignored until the take is unbaked.
Editing is easy once unlocked: type a number, 🎲 for a random seed, derived to go back, Lock the seeds to lock again.
Nothing is lost: every change pushes the previous seeds onto a seed history per block, with use (restore) buttons; after a build the panel compares the current seeds with the ones the last take was made with and shows a warning when they differ ("that take cannot be reproduced from these seeds").
Several tests for one block:Try several... makes 4, 8 or 20 takes (variants) and the producer chooses. A variant can vary both seeds, re-sing (performance seed only, melody frozen) or re-write (melody seeds only). Each card shows its melody and performance seed. Pick puts the chosen seeds into the block and freezes it as Seeds; freezing the melody or baking the take makes it permanent.
In the text and the linter: custom seeds are validated (the word derived or an unsigned 64-bit whole number from 0 to 18446744073709551615, error AMP2011) and raise the info-level lint rule L009 custom-seed ("fine and recorded; changing a seed later breaks reproduction of takes made with the old one"). A node with custom seeds shows a custom seed tag.
Why a warning and not a block: a producer experimenting should never be stopped, only told. The only hard rule is that a baked take or a stored plan is never silently replaced.
10. Open questions
Is a melody extraction (notes from the take) shown next to the plan, so the producer can see and edit the melody and not only freeze codes?
Can edited words be re-aligned to a frozen melody automatically (syllable-aware), or is variant-and-check always needed?
Should the table also be shown per model version (different models, different rules), and how are results shared between users?
How is the same plan on another computer announced to the producer (a warning with the measured closeness)?
As built (chunk 96)
The analysers and the melody plan are described in Analysis: ample analyze <take> --with melody turns a take's pitch contour into the notes of a plan (ample-melody/1), which can be stored as the melody data of a take and pinned with freeze plan. Tolerances are from synthetic signals only; the numbers in the table above stay expectations until measured on real material.
Variants: make many, pick one, keep a few
Updated 2026-10-17: the field-selection, deterministic proposal and plan-recording flow is implemented; focused verification has not been run yet. The sections below retain the broader workflow design.
Current implementation
In Studio, Make variants first offers the output-affecting fields supported by the selected block: numeric fields, fixed choices and seeds. Select one or more, then review the exact deterministic values for each take before rendering. Explicit seed parameters such as seed and lm_seed accept the full unsigned 64-bit range and are stored exactly in the take plan. The synthetic Renderer seed field varies the renderer's normal seed; explicit seed parameters are separate choices. Free text, file paths and execution-policy fields are not proposed.
Variant values are temporary build overrides while rendering, and each take's immutable render plan records the changed field names and exact before/after values. Apply & pick writes every listed block parameter (including numeric controls and full-width seeds) into the working .amplet source, then selects the rendered take. The synthetic renderer seed stays attached to the take because it is not a block parameter. The selected values can therefore be inspected, saved and built again from Properties or Source. Repeating a request can reuse existing takes. If no field is selected, no render starts.
Takes render sequentially. Each completed take appears in the review grid before the next starts. Cancel current take aborts the active render; completed takes remain in the take bank and visible in the grid. Each request carries the original song version and variant index, so editing the song stops the remaining batch and stopping/retrying preserves the same proposed values.
This support is intentionally based on each template's declared parameter metadata. A field is offered only when its metadata says it affects output and its kind is currently supported. Tests cover proposal repeatability and that chosen values are recorded in the take plan; actual sound quality remains for the producer to judge.
Generating several takes of a dynamic block (for example an ACE-Step vocal) and choosing is the everyday workflow, so it needs to be fast. A variant set is a batch of takes of one block, a pick locks one, and a shortlist keeps a few alternatives for later.
Make
$ ample variants chorus --count 20 # 20 takes of the block "chorus"
$ ample variants chorus --count 20 --vary seed,voice=[anna,bo] # also vary other things
Seeds come from AMPLE's stable PRNG (master seed plus block name plus variant number), so "variant 7 of chorus" is the same take next time on the same declared platform; nothing is lost when a run is repeated.
Only the block and what depends on it is rendered. A budget (--max-minutes, --max-gpu-hours) and cancel apply, because 20 renders cost GPU time.
Every take lands in the take bank (immutable, hashed, with its full recipe) and in a numbered variant set, chorus/v-2026-10-02-1.
Review (UI and CLI)
A grid of the variants with waveform, length and any measured scores (timing, quality module); play, play A/B against the current pick or another variant, loop, solo against the rest of the song ("in context").
A score of 0 to 100 per variant, set by the user, plus stars/flags (reject, maybe, good), a short note and tags. These human fields are kept apart from measured scores, which are labelled with the scorer that made them (so a human 80 and a measured 63 never get mixed up).
Quick keys: arrows to move, space to play, 0 to 9 to score (0-90), r to reject, k to shortlist, Enter to pick.
Sort and filter by score, flag, measured timing error, similarity to the pick.
Pick and lock
$ ample pick chorus 7 # use variant 7 and lock it (freeze to its take hash)
$ ample pick chorus 7 --keep 3,12,15 # lock 7, keep 3, 12 and 15 as alternatives for later
$ ample pick chorus --best # take the highest user score
Pick = lock: the block now refers to that take by hash, so later renders and releases are stable.
Shortlist (--keep) stays attached to the block as options: they remain in the take bank and are listed with the block (ample options chorus), so you can switch (ample pick chorus option 12) or open the review again with only the shortlist. Switching is a normal revision, so it can be undone.
Everything not picked and not kept stays in the take bank as unreferenced takes until a deliberate cleanup (see History v1: purge is dangerous, with impact and backup suggestion). Unpicked takes of a sealed release are never touched.
Picking, scoring and keeping are recorded in history (who, when), so "why did we choose 7" can be answered later.
In the file
voice chorus_vox using anna { lyrics chorus }
variants { count 20 vary seed } # recipe: how the set is made
pick 7 # the locked choice (take hash recorded in ample.lock)
keep 3, 12, 15 # shortlist
score 7 = 92, 3 = 85, 12 = 80 # the user's own 0-100 scores (optional)
Decided details (2026-10-02)
UI first. This workflow is a per-block tool in the web interface (right-click a block: "Make variants...", review grid, pick, keep). Most users will not use the CLI, so the CLI examples above are the scriptable twin of the same actions, not the main way in.
Round two (refine) is a good idea but later: variants close to a chosen take (same voice and prompt, small change, with a small/medium/large distance setting), offered per block in the UI, with a CLI twin afterwards.
Automatic scorers to start with (all four, as plugins on a variant set): lyric timing error, key/pitch fit, loudness and dynamics, similarity to the chosen take (finds duplicates or deliberately different ones).
Several blocks together (--together verse,chorus): per block separately by default; with --together, whole-song variants are made (variant 7 = verse 7 + chorus 7) and judged as a whole. In the UI: a "vary together" toggle when several blocks are selected.
Scores: a free number from 0 to 100, optional, with stars and flags as shortcuts that translate to a number; everything stays optional and apart from measured scores.
Scorers are opt-in. Each automatic scorer is a plugin that may need its own model; a project enables only what it needs, and a scorer that is not installed shows as "not available" in the review grid instead of a blank score.
Bake: dynamic results that become data
Status (2026-10-02): design sketch from a brainstorm. Nothing here is built or tested.
A dynamic node is anything whose result is not a pure function of the text: an AI voice, a weather lookup, a "make master" step, an analysis of an imported file. Normally AMPLE records its recipe (inputs, seeds, toolprint) and can render it again. Baking turns a node's result into a data block that is part of the project and of every backup.
Why
The recipe may stop working (a tool disappears, a model is updated, a web source changes, a different machine gives a different take; this was measured with ACE-Step).
The result is what you liked. The recipe is only the way you got it.
What a baked block is
The result bytes plus metadata: hash, size, which node and recipe made it, the toolprint and platform, the time, who baked it.
Stored in the take bank / data store (hash-addressed, immutable) and included in backups and exports (according to profile; a partial export says when baked data is missing).
Referenced by hash from the node in ample.lock; the project then renders from the data instead of calling the tool.
voice chorus_vox using anna { lyrics chorus }
bake sha256:MOCK... # data-exact from now on; the recipe is kept as provenance
Recipe-exact versus data-exact
A release states which one it is:
Recipe-exact: the recipe plus pinned tools reproduces the same bytes on a declared platform class; no baked data is needed (but may exist).
Data-exact: the result rests on baked data. Reproducible by definition, because the data is the project, but the recipe may no longer work.
ample rederive <node> re-runs the recipe and compares: if the hash matches, the node is proven recipe-exact and says so; if not, the difference is shown and the baked data stays authoritative.
Commands (sketch)
ample bake chorus_vox # freeze the current result of this node into data
ample bake --all-dynamic # every dynamic node of the current cut
ample unbake chorus_vox # back to recipe only (the data stays in the bank until cleanup)
ample rederive chorus_vox # recipe-exact test (needs the tools)
ample bake --list # what is baked, size, recipe-exact status
In the UI: right-click a block, "Bake" / "Reset recorded values"; baked blocks show a small lock-and-disk marker.
Interaction with other parts
Variants: picking a variant locks it by take hash; baking is the same idea for any dynamic node, and a pick is a bake of that variant.
History: baked blocks come back with a revision. A baked block is cleaned up (ample gc) only when no revision, tag, checkpoint or archive refers to it; the real removal is a purge (dangerous act, impact shown, backup suggested).
External data (weather, remote sources): lock records it once, refresh fetches again on purpose; a baked block is a lock with provenance and a hash.
Storage: big baked audio may live outside git (LFS or AMPLE's own store) with the usual warning that can be silenced inline (accept external until ...).
Backups: profile minimal leaves baked data out (and says what that costs), standard and complete include it.
Open details
Granularity: bake a node, a segment, a whole cut?
Whether a bake should warn when the tool that made it is no longer present (suggested: show it in verify --present, not as an error).
Compression/deduplication of baked audio in the store.
The .ampletx package
Status (2026-10-09): built. Export (Full and Derived), the read-only viewer, the comparison with this environment, the import (anyway, convert, new project) and the optional zip are in; format version 1.
An .ampletx is a portable, single-file snapshot of a saved song and everything it needs. It is a text file: binary files are Base64 lines, so the head, the manifest and the song can be read in any editor. Opening one is read-only; to work on it you import it, which makes an editable project and leaves the package untouched.
Layout
#AMPLETX 1 the format tag (a number to bump when the format changes)
#kind full full or derived
#manifest {...} one JSON line (see below)
#entry {"path":..., "role":..., "encoding":"text|base64", "sha256":..., "size":..., "bodyBytes":...}
|a text line text files: every line starts with |
QUJDRA==... binary files: Base64 lines of 76 characters
#end
...
#endpackage
Every entry names its SHA-256 and size; reading it back checks both. The same project gives the same bytes (entries are sorted by role and path; there are no timestamps except created in the manifest). bodyBytes lets a reader skip a body, so opening a package of hundreds of MB only reads the head.
The manifest
format (ampletx, amplet, spec), application (version and the SHA-256 of the program that made it; nothing reads it back yet), created, mode, project (id, song, the SHA-256 of the saved song and of the whole state), settings (the song line, features, lint, plugin settings, the master seed and default engine of the project, render settings: sample rate, bit depth, resampler), settingsFingerprint, dependencies (every block with its template version, the renderer and toolprint of its take, plugins with version and fingerprint, models with their hash, every asset with hash and size), master (when included) and warnings. Interface preferences are not part of it.
Full and Derived
Full: the song as it was saved, and every file its blocks read. By default only what the build really uses goes in (see below).
Derived: parts of the graph are rendered to audio and become new import-audio blocks (derived/*.wav); what is upstream of such a boundary is not in the package. Per audio path:
Automatic boundary: the output of the first block after a source that really transforms the sound (an effect). A path that reaches a mixer without any transformation is rendered straight from the source and the export warns that it is untransformed.
Explicit boundary: right click a block > Bake to here for Derived export (# export bake <block> in the song). It beats the automatic one and bakes as much as it covers.
Always include as-is: right click a source > Always include as-is in Derived exports (# export as-is <block> or <block>.<port>): the source stays live and its files stay in. An as-is source and a boundary on the same path is a conflict that must be resolved.
Paths are analysed per output; paths that share the exact same rendered result share one file. Disabled blocks and blocks that do not contribute to the final output are not exported by a Derived export (Full keeps them).
The export dialog shows all of this first: the files with hashes, the paths and boundaries, what is baked, what stays editable, whether the original source is embedded, the warnings.
Comments are never removed. A Derived song keeps every comment line of the original, including the comments above and inside blocks that are left out. A # export … marker or a # view … line that names a block that is no longer there stays as a plain comment (# (Derived) export …), so it cannot act on a block that is gone.
Module instances. A Derived export of a song with use … as … first writes every instance out as ordinary blocks (named like intro/bass) and cuts that text, so the package needs no module files and the baked parts can include blocks of a module. The export says so in its warnings. (Verified on a small song with one module: import and cold rebuild give the same SHA-256. A cut through a module with sample files was not tried.) A Full package keeps the use lines and the module files.
FLAC for the audio inputs (optional, Derived only)
Store the audio inputs as FLAC makes the package smaller. FLAC is lossless in principle, but there is no guarantee that the decoded audio equals the original file (ffmpeg's encoder and decoder, the header of the WAV, the sample format). So the export does not just do it:
AMPLE sends every audio input through FLAC and back (ffmpeg, 16 or 24 bit integer PCM; other formats stay WAV).
It builds the final result twice in a temporary project: before (the original WAV inputs) and after (the inputs that came back), and tells you per input whether it came back byte for byte, only with a different header, or different.
You listen to both and press I listened: use FLAC and write the package; only then the package is written. The approval belongs to exactly that saved state and those options; a changed song needs a new listening test.
The package stores the .flac with the SHA-256 of the original WAV and of its samples. At import the audio is decoded again, compared with the original SHA-256, and the import result lists any input that differs. Import needs ffmpeg for a package with FLAC (it checks before it writes anything; AMPLE_FFMPEG points to it).
Only what the build needs
The export runs one traced build and records what is used: which presets of a SoundFont and which samples of a sample set were really played. The package then carries a smaller, valid SoundFont with just those presets (zones, modulators, loops, sample headers and stereo links copied as they are, indexes renumbered; the package notes the original file, its SHA-256, the SHA-256 of the subset and the presets) and the samples that were played with all variants of their note or voice name. Imported projects render bit for bit the same. The option Include the whole sets puts everything in.
Eligibility
Only a saved song can be exported: the export never saves for you. Unsaved changes in any other song or module of the project also stop the export (the message names them), because the package is made from saved files only. Every block that contributes to the final output must build; disconnected, disabled or unused blocks never block an export. The master preview (default on) is the render of exactly that saved state and is read straight from the package, without building.
Import
Every file is matched by SHA-256: identical at the same place (nothing written), identical elsewhere in the project or in an installed pack (relinked), or extracted to a free place (the place can be changed first; nothing is overwritten, a taken place moves the file to imported/<package>/...).
The comparison with this environment lists differences (format, language version, block versions, renderers, plugins, sample rate, models) with their impact; nothing is normalised silently.
Import anyway uses this environment as it is and records the differences. Try to convert applies only the listed deterministic changes (relinks, moving a reference to an extracted file). Create new project from package makes a new project with a new identity; if differences remain a best-effort confirmation lists them. Do not import writes nothing.
Every import writes a record to .ample/imports/ (source package, its SHA-256, the differences, what was written, relinked and changed).
A Derived package starts at its audio inputs; the blocks before them are not made up.
A package can also be compressed: name.ampletx.zip holds the same file; opening and importing handle it transparently.
API
GET /api/tx/export/preview, POST /api/tx/export, POST /api/tx/flac/preview, GET /api/tx/flac/audio, POST /api/tx/flac/approve, POST /api/tx/mark, GET /api/tx/inspect, GET /api/tx/master, POST /api/tx/import/plan, POST /api/tx/import/apply (see /api/openapi).
The AI assistant (Codex)
Playback and request responsiveness (2026-10-17)
Sample cards request serialized, small server-side waveform summaries instead of downloading and decoding each full song in the browser. Stop releases the audio source. Task indicators do not poll behind an open conversation and never overlap their own requests; closing a conversation aborts its pending poll. Read requests have a 15-second timeout. Hidden tabs release their event stream and reconnect when visible, leaving browser connections available for controls. The source context menu does not wait for AI status before showing ordinary editing actions. These safeguards address verified unbounded requests and full-song waveform downloads; actual delays still need the browser's Timing view to distinguish connection queueing from server work.
Long tasks and recovery (2026-10-17)
Codex turns have no fixed twenty-minute wall-clock cutoff. Explicit Stop and cancellation remain available; the existing inactivity probe checks silent turns. Failed tasks, including older twenty-minute timeouts, can resume their saved Codex thread once the previous worker has ended, provided the task workspace still exists. Resume retains the working copy and delivered samples; it does not restart the original request from scratch.
Accept and bounded Continue (2026-10-10)
Accept selected applies only the checked files and starts no Codex turn. Accept selected & continue remaining is a separate explicit action. Continue remaining work, including after a samples-only answer, resumes the same conversation and supplies a checkpoint of the original request, user requirements, accepted files, existing clips and last answer. It does not reject the task and reopen the original prompt. Closing the samples-only window leaves the conversation intact.
Continue preserves delivered work and quantity requirements. For an unambiguous request containing one numeric sample/clip/variant count (or five/vijf), the server counts proposal clips delivered for that request; current-project reference clips are excluded. A clear later sample request in user messages supersedes the earlier quantity. If that count is met, Continue starts no turn. Otherwise the presentation endpoint enforces the remaining count for that continuation, including concurrent calls. Multiple quantities or other wording are retained in the checkpoint but are not inferred as a machine-enforced numeric quota. A new explicit sample request via Send (for example “give me 5 more samples”) clears the continuation quota; ordinary feedback does not.
Buttons suppress repeat clicks, and the server serializes review actions and rejects Continue when a turn is already running. The quota and presented clips persist with the task. Source/build checks are separate from real-Codex/browser acceptance, which remains pending.
Temporary listening trials (2026-10-10)
AI clip waveforms show the maximum magnitude across all audio channels. Quiet waveforms are scaled for visibility (up to 1000× visual magnification); this does not change playback gain, and waveform height should not be used to compare absolute loudness. Silent audio stays a flat line.
POST context routes for edit, listen, present, and staged moves (move, move-file) authenticate with a registered X-Ample-Context task token. They do not require the browser's session token in addition; unknown/missing task tokens do not grant access. Host/origin checks still apply. When adding a new POST context tool, update both its route/MCP call and the exact authorization allowlist in AmpleServer.cs.
ample_present accepts optional path and edits, using the structured ample_edit format. For example, present the master with source=proposal, from="0", to="30", and edits=[{"op":"set_param","block":"wonderwall-guitar","name":"program","value":"24"}]. Repeat with programs 25 and 26 for real instrument comparisons. The edits apply only in the temporary render project; the proposal and live project remain unchanged. source=current rejects trial edits. Apply the winning settings with ample_edit afterwards.
The tool returns parsed source settings, the applied trial summary, audioHash and identicalTo when another presented clip contains the same canonical WAV bytes. Exact equality proves matching audio; different hashes do not prove a perceptible difference. The card exposes these source settings and warns about identical audio. Clips and metadata are saved with the task and restored after restart. Audio is captured inside the render lock, avoiding another listen call replacing its temporary take before presentation. Labels and descriptions never change audio. Bars use the rendered source tempo.
Activity and sample lists forward excess wheel scrolling to the outer task page at their top and bottom boundaries. Browser and real instrument comparison acceptance remains pending.
Status (2026-10-09): built, optional, off by default. Codex proposes, AMPLE validates, the user decides.
Inspect tool activity
AI dialog headers expose an icon-only Stop music button just before Close while project or proposal playback is active. It stops both players without cancelling a build or the AI task, then disappears. Its active styling matches the master Stop control.
Starting a full-song build stops project and proposal playback automatically, including a full proposal build. Build-and-play/build-and-listen still starts the resulting audio after building.
Each tool or command activity row has a small ↗ button opening a full-screen detail dialog. It shows the timestamp, input, output, errors and original MCP/command metadata as plain text. New operation payloads are saved with the session; older summary-only entries explicitly say their payload was not captured. Batch calls are expandable individually, with ordered input/result pairs and error flags; the original combined response remains available. The MCP batch result preserves its text response and also exposes structured results (at most 12 executed calls). No extra tool execution occurs when inspecting a call.
Samples in Output can be marked obsolete by the user with a reason, or by Codex with ample_obsolete_sample when later edits make a prior render unrepresentative. Obsolete cards remain in place, dimmed, and show the reason; only the × dismisses them from the view.
The updated server must be started before new payloads can be captured. This does not recover details already discarded by an older server.
Two kinds of approval
New staged songs and blocks can be read before acceptance. When editing an existing project file not initially copied into the proposal, the editor uses its current disk text as the baseline; replacements are not applied to an empty string. Explicit owner workflow exceptions apply within the scope authorized in the conversation, subject to actual tool/sandbox permissions.
Codex action approval (what Codex may do while it works): the project setting execution mode.
Proposal acceptance (whether the result becomes part of the project): always the user, in the proposal wizard. Even Unsafe / Auto-all never accepts anything for you.
Server and project
The server says what a project MAY choose (%LOCALAPPDATA%\ample\ai-policy.json, or the AI settings dialog > Server capabilities): Codex enabled, the server-user account, project Device ID identities, inheriting the system-user configuration, and each of Manual, Auto and Unsafe. All are on by default. If something is turned off, a project that uses it shows as unavailable; nothing falls back to another identity or mode. Optional fields model, effort and codexPath (and the environment variables AMPLE_CODEX_MODEL, AMPLE_CODEX_EFFORT) choose the Codex model and reasoning effort.
The project chooses (project.json, settings.ai): the assistant (No AI assistant, Codex - Server account, Codex - Project Device ID) and the execution mode. The default of a new assistant is Auto. The first use needs an acknowledgement of the risks, and the first time for Unsafe another one. The settings never contain a secret.
Mode
Codex's own approval and sandbox
What it means
Manual
approvalPolicy untrusted, workspace-write
reads freely, asks before it writes or changes anything; each request is shown to you
Auto
on-request, workspace-write
Codex decides what is routine and asks for the rest
Unsafe / Auto-all
never, workspace-write
nothing is asked: what the sandbox allows runs at once, what it does not allow is refused
(any mode, a request to explain)
never, read-only
Codex cannot write at all
Codex only ever needs to write in its own temporary copy, so the sandbox stays on that copy in every mode. Only if the server administrator turns on unsafeNoSandbox (off by default, AI settings > Server capabilities) does Unsafe / Auto-all use danger-full-access.
A denied request is respected; modes are never raised by themselves. Lowering the mode applies at once; raising it is an explicit choice.
Identities (what Codex really supports, checked on Codex 0.160)
Server account: the Codex login of the user the server runs as (its CODEX_HOME). Its configuration and global AGENTS.md live in the same home as the login and cannot be separated from it; with inheritance switched off on the server AMPLE only switches off AGENTS.md files of the project (project_doc_max_bytes=0) and says so.
Project Device ID: its own CODEX_HOME (%LOCALAPPDATA%\ample\codex-homes\<project id>, outside the project), linked with the real device flow (account/login/start of the Codex app server: an address and a code). Nothing of the user's Codex is read or changed. Link, Re-authenticate and Unlink are in the settings. The login is never part of the song, a package or Git.
One Codex account is used by one task at a time (its refresh token works once). Further tasks wait in a queue (the wizard says how many are ahead; a queued task can be cancelled) behind a lock file per account in %LOCALAPPDATA%amplecodex-locks, so it holds across projects and servers.
A task
AMPLE copies the files in scope (text files only) into .ample/ai/work/<task>/project with the reference, a TASK.md, an AGENTS.md and the skill ample (.agents/skills/ample), starts the Codex app server there (JSON-RPC over stdio), and gives Codex a read-only MCP server ample (the command ample mcp, with a token of this task that can only read): ample_project, ample_faer_status, ample_docs, ample_block_types, ample_song, ample_block, ample_file, ample_analyze (tempo, key, loudness, spectrum, pitch of a built block) and ample_request_asset. ample_faer_status reports the project's independent song/voice/separation routes, live capability readiness and the current ACE .gguf filenames, roles and sizes; it returns no credentials or job content. The ACE model catalog explains model roles and recommendations. Codex keeps AMPLE's hardcoded family choices, combines them with live server offers, and can use the exact-file field for a server-listed unfamiliar variant. When relevant, the final proposal summary names the model choice so it is visible on Output, Changes and Checks.
The ample_project response lists task-work files separately under taskWorkFiles. On path-based read calls such as ample_file, ample_song and ample_block, pass location=work to read one of those files; location=project (the default) reads the normal project file. The path stays a plain relative filename. Hidden internal files and rpc.log are excluded, and choosing location=work is read-only. The task composer has an unchecked Strict mode option. Off by default, Codex can ask for approval before accessing files or running commands outside its task work folder; the request is surfaced in the task UI and waits for the user's decision. When checked, the current strict policy applies. This is separate from the manual/auto/unsafe interaction choice: non-strict always uses sandboxed, on-request permissions so an external request cannot be silently refused or silently granted.
For every .amplet containing ace-music or ai-voice, Codex must write settings { acestep.backend server } in that song, including baselines and every standalone variant. A project default is insufficient; missing it produces AMP2013 and prevents the ACE block from building with AMP5370. Apply the setting identically across seed-comparison files before rendering. This selects the ACE server backend; the project's separate local/Faer capability route decides where the request is sent. Resolve this preflight error before investigating Faer readiness, model loading or VRAM. Submit Faer renders sequentially so one GPU job owns the model load/render/release cycle at a time, and call ample_present immediately after each successful render before starting the next. This makes each playable sample available while later variants are still rendering; do not wait for the whole batch before presenting the first one.
When Codex is done, AMPLE compares the copy with the baseline and shows: what Codex says, the changed files with a diff, the diagnostics, the build result (the proposal is built in a staged copy of the project, so you can listen to it) and warnings. The fixed tab row stays above each tab's scrollable content: the written summary appears in Output, Changes and Checks, while the newest presented sample is first in Output. The preview target follows the song Codex actually rendered or measured, including when a proposal adds multiple songs; the proposal window names that target, and Look at it in the block view opens that proposed song read-only while the project keeps its active song. A failed or refreshed proposal clears the prior build audio, and a lost task connection pauses playback and hides output until the newest task result is confirmed. If the user explicitly asks for no build or render, Codex calls ample_skip_build: AMPLE still checks source errors and warnings, labels the proposal Build not run (as requested) with the reason, and allows Accept without waiting for a build. The user can still choose Build and test again. Accept is refused when source errors exist or a build fails; Accept anyway applies a failed build (unsaved, undoable) after a clear warning. Accepting makes a checkpoint first, writes other files through the journal (undoable from the wizard) and applies the song as one undo step in the editor. If the project changed while Codex worked, the proposal is not applied blindly: Try to rebase (a three-way merge, conflicts are reported), Regenerate, or Review anyway (view only).
Sampling ACE/Faer seeds
When the user wants to compare generated voices or performances across seeds, treat it as an audition: Codex presents the candidates and the user chooses; Codex must not pick a winner on the user's behalf. Keep lyrics, prompts, model, inference settings, arrangement and all other song settings fixed so the seed is the only intentional difference.
When a genuinely random master or block seed is needed, call the MCP tool ample_random_seed. It uses the operating system's cryptographic random generator and returns a positive 64-bit value accepted by AMPLE; do not ask the language model to invent a supposedly random number.
An unverified baseline is not a reason to refuse a requested song or render batch. Create the requested first version, render it with AMPLE's build/listen tools, then continue through the requested number of candidates and present the resulting samples. A missing baseline limits claims about comparative quality, not the ability to generate. Distinguish source checks and measured audio facts from subjective musical quality; do not claim a render succeeded until AMPLE confirms it. Shell builds remain out of scope: use the integrated AMPLE tools, and skip rendering only when the user explicitly asks for no build or render.
Lyrics files in generated songs
Choose the lyric format for the request. For exact, untimed lyrics supplied for ACE music generation, preserve them in .txt and feed them through the lyrics block; .txt is an accepted lyrics source and avoids inventing bar timings. Use .lyr when the owner wants AMPLE's timed-lyrics workflow or Lyrics editor: one [bar N] text row per lyric line, stored in lyrics/<name>.lyr, referenced by a lyrics block whose words output connects to ACE music or AI voice. Preserve the user's words exactly; do not convert untimed lyrics to .lyr unless timing is requested or clearly useful and can be added without changing the words.
There are two valid ways to do this:
Separate files: derive each standalone .amplet from the same baseline, change only the relevant seed value(s), and build/render each file. Use this when the user wants reproducible variants they can keep and revisit.
Sequential samples: keep one song and its settings fixed, set one candidate seed, request a preview/output sample, then change only the seed and request the next sample. Keep each presented sample available and identify it by its seed value(s), so the user can compare them and choose. After the user chooses, put the chosen seed value(s) in the retained song.
Do not claim that a candidate was generated or rendered until its build or preview actually succeeds. If the current tools, project route or server cannot render the requested candidates, explain that limitation and offer the other route; never fabricate sample results or imply that changing a seed alone rendered audio.
Large or non-text assets (a sample, a recording) are not copied up front. Codex asks with ample_request_asset (path and reason); the wizard shows the request (kind asset, with the size) and in Manual and Auto the user allows or denies it; in Unsafe it is allowed at once. When allowed, AMPLE copies the file into assets/<path> of the task's copy and tells Codex where it is. Assets are outside project/, so they never show up in the proposal.
Autocomplete (Ctrl+Space, the grey suggestion) stays local and never calls Codex.
ample_redux_search is a read-only MCP tool and starts in the current task's AMPLE Redux archive. Codex can explicitly select its current session or expand to project history. Results are paged; payloads stay hidden unless one AR record is requested in detail.
Where
AI ▾ in the source editor and the sequencer, AI in the right-click menu of a block, Project settings > AI assistant. Right-clicking a block, selected blocks, or a group can start a fresh Codex session with those block IDs as its initial context. Empty canvas offers a session with no block focus. Codex is asked to stay focused where practical and to explain when the requested outcome needs broader edits. API: /api/ai/* (see /api/openapi).
Server policy cannot be widened by a project
/api/ai/policy is reachable with the token of a project, and the token of a project is not the administrator of the machine. So over the API the policy can only be tightened: a capability can be switched off, never on, unsafeNoSandbox can be switched off but never on, and the program that is started as Codex (codexPath) cannot be changed at all (error AMP5459). Allowing more is done by editing the policy file %LOCALAPPDATA%ampleai-policy.json (or setting AMPLE_CODEX), which requires access to the machine. The settings page shows a switch that is off as disabled, with that explanation.
"Changeset only": what it is and why it is not a separate mode
The idea (owner, 2026-10-09): Codex starts with no ability to write anywhere and its output is a changeset (a list of file changes) that AMPLE shows, checks and applies after approval.
What exists instead, and why it comes to the same thing for the project:
Codex runs in a temporary copy (.ample/ai/work/<task>/project), with its sandbox on that copy in every mode (workspace-write; read-only for a request that only asks to explain). It cannot write to the real project. AMPLE then computes the changeset itself by diffing that copy against the baseline taken at the start (the Changes tab), checks it (parse, staged build) and applies it only when you press Accept (one undo step for the song, the journal and a checkpoint for other files).
So the effect is the one asked for: nothing in the project changes until Accept, and what Accept applies is exactly the shown changeset.
What is not the same: Codex does write files, but in the throw-away copy. A true "changeset only" mode would need Codex to run with a read-only workspace and return its edits as structured output (a patch) instead of editing files. Codex's own tools for editing are file based and its sandbox modes are read-only, workspace-write and danger-full-access, so a patch-returning flow means asking the model to output patches as text and parsing them, which is less reliable for multi-file work and gives up Codex's own run-and-check loop. It is therefore not built; the only read-only use is the explain request.
If it is ever built, it should be a fourth choice next to Manual, Auto and Unsafe (not a replacement), with the same checks and the same Accept step, and the changeset format should be a unified diff per file (the format AMPLE already shows and applies).
What the user sees while Codex works
Tool use is shown with its arguments and elapsed time, for example ample_build (block=mix): completed · 12.4 s; a failed call shows Codex's own error text. The proposal's automatic build check also reports its elapsed time.
Build, listen and arrangement-measure tools follow the project's configured routes, including Faer. Avoiding Faer-specific tool calls does not force local rendering. There is no per-call local-only override yet; if a user asks to avoid Faer while a route is set to Faer, Codex must say that it cannot guarantee local rendering.
A thinking bubble. Codex can tell you what it is thinking about with the MCP tool ample_thinking, and every other tool takes an optional thinking argument that does the same. The latest thought is shown as a speech bubble with the two before it faded, beside an animated badge; thoughts are also kept in the Activity log. The title bar also shows a blinking activity dot and the current action while Codex works or AMPLE checks the proposal. Activity visually distinguishes what the user sent, Codex's notes and its answer, while tool details are quieter. Changed files appear in a highlighted, clickable group, and ample_edit can include an optional per-file why shown beside its diff. Audio cards can be hidden one at a time and restored with Ctrl+Z until the proposal window is closed. These are notes written by the model, not a view into its reasoning.
A document that does not exist is not an error:ample_docs accepts the name with or without docs/ and .md, and for an unknown name it answers with the closest document names (didYouMean). Earlier, a wrong guess showed up as a failed tool call.
The task window keeps its scroll position while it updates and the log follows its end.
In an active task, the proposal header identifies the task's actual Codex account and mode; it does not reuse a possibly stale project-settings badge. Sample playback continues while task updates redraw the conversation. Temporary refresh errors show a reconnecting notice and retry while preserving the current view.
Newly proposed files are absent from the live baseline until accepted. Stale checks treat those paths as absent instead of trying to open them as existing song documents.
Look at a proposal in the real views and build it
AI proposal builds can use the active project's takebank as an additional read-only audio cache. This is on by default in Project settings > AI assistant and can be turned off there. Only exact plan-id matches are reused, so source, effective settings, seeds, inputs and renderer identity must all match. Matching blobs and any required parent-plan records are copied into the temporary proposal takebank for downstream rendering; the active project's takebank is never changed.
If a current-project sample sounds inconsistent with the current song, or a frozen selected take could hide the requested localized change, Codex should diagnose it autonomously with ample_present using source=current and rebuildCurrent=true. AMPLE rebuilds from current song text in a temporary project while bypassing selected-take choices and preserving pinned melody choices. When the exact plan already exists in the active project's read-only takebank cache, AMPLE reuses that audio; otherwise it renders the current plan. Codex compares the returned take id, plan id, audio identity and audible scope with the ordinary current sample, then uses that evidence to correct its proposal without asking the user to find cache or take ids. The clip is labeled rebuilt from current song text; the active project and its takebank remain unchanged.
Look at it in the block view (button at the bottom of the proposal window): the proposed song is shown read-only in the real Blocks view (the same canvas, panels and properties as for your own song), with a yellow banner and the badge peek: AI proposal. Nothing is saved or changed; clicking the banner goes back to the proposal. It is the same read-only peek that the history uses for an old state. The build marks you see there (cache hit, waveforms) are those of your current project, not of the proposal.
In that preview the normal controls work on the proposal, never on your project: the Build button and the Play button of the header build the whole proposal in its temporary project and play the master, every audio block has a small ▶ that builds that block (and what it needs) and plays it, and the blocks show their build result (built, failed). Your own takes are not shown there and nothing of your project is built, overwritten or saved. A built block gets a normal audio player (play, scrub, repeat) that reads from the temporary project of the proposal, so listening again needs no new build; the temporary project is kept while the proposal does not change.
Build and listen to one block: right-click a block in that preview > Build and listen to this block (AI proposal). AMPLE builds that block and what it needs in the temporary project of the proposal and plays the result.
Build and test the whole proposal: the Checks tab has Build and test again, which builds the proposal in the temporary project and plays the master. The first check still runs when the proposal is ready; a build you ask for waits for it. API: `POST /api/ai/tasks/{id}/build` (optional `block`), `GET /api/ai/tasks/{id}/text` and `/audio`.
Which model Codex uses
The server chooses the default. It is GPT-6-Luna (gpt-6-luna, fast and affordable), and it can be changed in AI assistant > Server capabilities (the list comes from Codex itself), together with reasoning effort. The request window can override the model for one request; Use server default inherits this setting, while Let Codex choose its default defers to Codex. The policy file (model, effort) or AMPLE_CODEX_MODEL / AMPLE_CODEX_EFFORT configure the server default. GET /api/ai/models lists available models and, for a ChatGPT account, current rate-limit windows and reset times. A model appearing in the list does not guarantee account access; Codex checks access when a task runs. See the OpenAI app-server documentation for the model list and account rate-limit data.
Finding the way back, and hearing the proposal
A pill (bottom right) shows an AI task that is working, waiting for you, or ready, whenever its window is not open; clicking it opens the window again. Closing the window never loses the task.
In the read-only preview a bar under the yellow banner holds Back to the proposal, Build and listen, and a player with the waveform of the proposal (play, stop, a playhead, click to seek). It stays there in every view, the code view included. The header Stop button also stops the proposal, and starting one sound stops the other.
Before accepting, the proposal window shows the same waveform player on top, so the built result can be heard (and seen) first.
Going on before you accept: quick iteration
The Codex session stays open after the first answer, until you accept or reject (or when the server stops). It ends after 30 minutes without a message (the proposal itself stays). You then choose: Resume the conversation (the same Codex thread, with everything it remembers) or Start a new session (a new thread in the same working copy, told what was asked, what is already changed and how many clips were delivered). The idle time is 30 minutes; for tests AMPLE_AI_SESSION_IDLE_S shortens it. So you can adjust without starting over: type in the message row of the proposal window, or in the bar of the read-only preview ("too loud" > listen > "make it 20% quieter"). Each message is a new turn on top of the proposal as it is now; the proposal (changes, checks, waveform) is updated and the preview builds and plays it again at once.
Steering while it works: a message sent while Codex is working goes to it at once (turn/steer), it does not wait for the end.
Fast by instruction: Codex is told to work quickly and focused, to read with one ample_batch call instead of many, to change as little as needed, and to answer follow-up messages quickly. The default model is the fast one (GPT-6-Luna).
ample_batch: many read tools (project, docs, block types, song, block, file, analyse, check) in one request, in order, with their answers together (at most 12 calls).
Delivery without new problems: Codex has ample_check, which lists errors and warnings that the song did not have before, and is told it is not finished until that is empty. AMPLE checks too: if the result has new errors or warnings it sends Codex back to fix them (at most twice) before you see the proposal; the proposal window says when that happened. Problems the song already had are not held against the proposal.
The thinking bubble is fed by the tool ample_thinking and also by the summary of the model's reasoning when Codex gives one, so it moves even when Codex does not call the tool.
API: POST /api/ai/tasks/{id}/message, GET /api/ai/context/check.
Links the user gives, and a preview that waits
Codex has no network (its sandbox is closed on purpose), so a link in the request (an image, an SVG, a page) used to fail with "the download was refused". AMPLE now fetches it for Codex with the tool ample_fetch_url: a link that is in your request or in one of your messages is downloaded at once; any other link first asks you ("Codex asks AMPLE to download a web address for it"). The file lands in assets/web/ of the temporary copy (never in the proposal); text, SVG and JSON also come back as text. Only the public internet (never this computer or your local network, also not after a redirect), images, text, audio, JSON, XML and PDF, at most 10 MB. AudioHero also has ample_fetch_audio_previews: one approval for up to 10 MP3 preview choices by default (up to 50 only when explicitly requested), already observed one-by-one in the task's visible browser; each appears independently in Codex Output and can be chosen before it is copied into a proposal. This is non-commercial exploratory use only.
ample_web_search lets Codex choose between DuckDuckGo's free Instant Answer API and Brave's full web-search API. AMPLE persists only an API-key hash, UTC month and its own Brave request count under the library home, estimating remaining out of 1,000 calls; this is not the live Brave credit balance and does not include calls made elsewhere. Brave is capped at 10 actual calls per Codex turn and spaced by at least one second. Identical Brave query/count/offset calls can reuse a five-minute memory-only cache; results are not persistently cached. Brave's headers are not treated as a credit balance. ample_web_read can read a user-supplied or approved-search-result page without another approval, cookies or JavaScript; if that fails, the browser remains the fallback. AMPLE asks before each search, never silently switches provider, and does not scrape a search results page when an API returns nothing or fails.
The tools of AMPLE are accepted when Codex asks before calling them (in Manual mode Codex asks for every tool that is not read-only); other MCP servers stay declined.
While Codex works again or a new build runs, whatever was playing stops, the preview (the player, the ▶ of the blocks, Build and listen) is disabled, and the bar says why ("a new build of the proposal is in progress"). It comes back, with the new proposal, when the build is ready. Messages can still be sent in that time: they steer Codex.
Editing with a tool instead of shell hacks
Codex used to change songs with PowerShell text replacements and regular expressions (fragile, and the first attempt often failed). It now has ample_edit: structured edits of a file of the temporary copy, all or nothing. The ops: replace (exact text; when it occurs more than once, use occurrence to select one match or all to replace every match), insert (after / before a line, or at a line number), delete_lines, set_param (sets or adds a parameter line of a block, for example gains), add_block, remove_block (it says which blocks still use it), connect and disconnect (a from line), and create (a new file). The first edit that does not fit stops the call and nothing is written; the answer lists the new errors and warnings and a diff, so Codex can fix them with the next call. Files with Windows line endings keep them; only files of the proposal can be edited (never .ample). The instructions tell Codex to use it instead of shell text replacement.
Build progress of a proposal
While the build check runs (first check, a rebuild after a message, or a build you ask for) the proposal window and the preview bar show the same steps as the real build: Checking the build with a spinner, the number of blocks done of the total, a progress bar, and the blocks being made right now with their steps and how far they are (`build.progress` in the task).
Checking its own work
When Codex thinks it is done it is told to check itself before it answers: read back what it changed and compare it with every part of the request, run `ample_check` (it must say ok), and, when the change can be heard (levels, gains, effects, notes, tempo, which sounds are used), run `ample_listen`. Codex cannot hear, so this builds its proposal in a temporary project and measures a block (or the master) with the analysers (loudness and spectrum by default; tempo, key and pitch too), next to the same block of the current project, so "20% quieter" can be seen as about -2 dB. It says what it measured in its answer. Changes that only touch comments or text need no listening. The reading tools (`ample_file`, `ample_song`, `ample_block`) show Codex its own proposal, not the unchanged project, so a read-back after an edit shows the edit (before, it showed the old text and Codex thought the edit had failed). `ample_analyze` measures the current project, not the proposal.
When the proposal does not build, and a valid format first
Why it does not build, in detail. For every block that failed, Codex gets the message and its code, a hint (from the diagnostics documentation when the code is explained there, else where to look: the block type and the block itself), what feeds the block, and the text of the block. The tool `ample_build` (optionally for one block) builds the proposal and answers with exactly that; `ample_listen` does the same when it cannot build. Errors in the song itself come back as lines with the hints of the parser. The same details are in the task (`build.details`).
A last gate before you see it: after the format check, AMPLE builds the proposal; if it does not build, Codex is sent back once with those details and told to fix it. Only then the proposal is shown (with the result of its own build).
The format is checked first, for every file Codex changed: `ample_check` (and AMPLE after the answer) checks the song with the parser (new errors and warnings, with their hints), a sequence file (`.seq`, `.ampleseq`) with the sequence parser, and JSON files for valid JSON. A file that was already invalid before the task is not held against the proposal. Codex is told to run it before it delivers.
More information when Codex needs it (units, ranges, impact)
Every parameter says what it is. `ample_block_types type=` gives each parameter with its unit, minimum, maximum, step, default, how to write a value (for example `-3 dB`, `25%`, one of a list) and what it does. The list of all block types shows what each type takes and gives (`in`, `out` with their kinds), so Codex sees what can be connected.
`ample_lookup` for more, on request: `units` (what dB, %, ms, s, Hz, bars, LUFS mean, how time and seeds work, and how "x% quieter" converts to dB: 20% is about -1.9 dB), `parameter` with `type.parameter` (unit, range, how to write it, what a change reaches), `impact` with a block of the song (what feeds it, what is rendered again when it changes, the files it reads, whether it reaches the master) and `search` with a word (block types, parameters and documents that mention it). It also works inside `ample_batch`.
Checking the goal: arrangement and real length
`ample_arrangement` gives Codex the arrangement of the song before the task and now: tempo and bar length in seconds, where every block that reaches the master sits (bars and seconds), blocks that are not placed or do not reach the master, the slice of the master, the length of the arrangement from the text, and a list of what changed (added, left, moved or resized, tempo, slice). Without `measure=true`, it calculates planned duration without rendering. With `measure=true` it also builds the proposal and returns the real facts of the built master next to those of the current project: length in seconds, peak (dBFS) and the silence at the start and the end (below -60 dBFS counts as silence). `ample_listen` and `ample_build` return the same audio facts. `ample_build` accepts `forceRender=true` for one named ACE song/voice block when the user explicitly wants a genuine fresh render; it skips both AMPLE's exact-plan take cache and Faer's remote render cache. Ordinary matching ACE requests use Faer's bounded render cache automatically. When the user requests an output duration or range, Codex must use the non-rendering calculation first, adjust the master slice for a time-limited sample or the arrangement when the song itself should be shorter, and verify the planned seconds are in range before the first expensive render. `measure=true` is for checking actual rendered duration after this preflight, since it builds the proposal. This measures output duration, not render wall-clock time.
Hum a melody
Say it with your voice when notes are hard to describe. ● Hum a melody… is in the AI prompt (and in the message row of a proposal, and in the bar of the preview), in the piano roll (● Hum…: the notes go into the clip) and in the recorder of the arrangement (→ melody: a new midi-clip with the notes).
A count-in at the tempo of the song, then you hum, sing or whistle one line on one vowel, close to the microphone.
AMPLE finds the notes in it (the pitch of every moment, cut into notes) and shows them on a small roll, with the key that fits best. It is an attempt: a hummed line is never exactly in tune or in time. You hear the notes next to your own hum and can change the tempo, how fine the notes snap in time (sixteenth, eighth, beat, as sung) and whether they snap to a key (the best fitting one, or one you choose).
Only when you approve it, it is handed on. For Codex: "Approve and give it to Codex" keeps the recording on the server, shows it as a chip (♪ 6 notes, D minor) with your message, and Codex gets the raw audio (`assets/hum/.wav` in its working folder) and the tool `ample_hum`: the notes with pitch names, the key and the `notes` text of a midi-clip at the tempo of the song (bar 1 or the start bar you gave). Codex can ask again with another `tempo`, `grid`, `key` or `bar`, and is told to check the attempt against your request and say how sure it is.
API: `POST /api/hum/transcribe` (WAV bytes; `tempo`, `grid`, `key`, `bar`), `POST /api/ai/hum` (keeps a recording, answers with its id), `hums: [id]` on a task or a message. The finder is built in (the YIN pitch method, no model or network needed): 16-bit WAV, mono or stereo, up to two minutes. Not covered: chords, several voices at once, or a noisy room.
Offering sounds to Codex (drag, pick or record)
Give Codex material without putting it in your project first. In the AI prompt, the message row of a proposal and the bar of the preview you can drag a sound onto the window, + Add audio… (wav, mp3, ogg, flac) or ● Record a sound… (the microphone). Each one shows as a chip you can listen to (▶) or remove (×). The files are only kept for this task (`.ample/ai/attachments`, removed after three days).
What Codex gets: every offered file exists in its copy at `samples/offered/.wav`, so a block can use it like any sample (`file samples/offered/kick.wav`), and the checks, the build check and `ample_listen` find it; the raw file is also in `./assets/offered/`. The tool `ample_offered` says what each one is (length, sample rate, peak and average level, silence at the ends, the pitch when there is one clear note) and whether the proposal already uses it. Codex is told to say in its answer which ones it used.
At Accept you decide what comes along. The proposal window lists Audio you offered Codex: the files the proposal uses are ticked and have to come along (otherwise the song would not find them, and Accept refuses with AMP5463), the others are unticked and optional; every one can be heard first. Only the ticked ones are copied into the project (`samples/offered/`); Undo takes them away again. Nothing offered is in your project before you accept.
API: `POST /api/ai/attachment?name=` (WAV bytes, or `&extension=.mp3` etc.), `offered: [id]` on a task or message, `include: [id]` on accept.
Trying a variant, and reading without repeating
Hear a variant without changing the proposal. `ample_listen` and `ample_build` take an optional `edits` list (the format of `ample_edit`, plus `path`): they are applied only in the temporary project of that call, so Codex can compare "-9 dB or -15 dB?" and then apply the one it chooses. (A change that does not move the measured number, as the kick channel did in the test, shows that the metric of the whole block is blunt for a single channel.)
Re-reading is answered shortly while nothing changed. Codex re-reads blocks and files a lot after every round. When the same song, block or file is asked for again and nothing in its working folder (or the offered audio) changed since the last read, the answer is a short "unchanged since your last read"; once it changed something (a file is newer, bigger or new) the next read is fetched again with the change included.
Memory of earlier requests, and how-tos
The AI remembers what you asked before. Every request is written down in `.ample/ai/history.jsonl` when it is ready, accepted, rejected or failed (the request, what Codex said, the files it changed and, as far as kept, the changes; at most the last few hundred), so it survives a restart of the server. Codex gets the three latest requests on the same song with its first message and has the tool `ample_history` (newest first; `id=` gives one in full with its changes) for "like before", "continue", "undo that last thing".
Continue an earlier request. The AI prompt has a list of earlier requests of this song: pick one and its prompt comes back to tweak, and Codex is given that request, its answer and its changes (and told to check what is in the song now). A result without changes has a button Continue this request…. API: `GET /api/ai/history`, `continueFrom` on a task.
Project and song notes for later tasks. `ample_memory_read` reads the project notes or the notes for the current song; `ample_memory_write` appends a dated note by default or replaces the whole note with `mode=replace` (empty text clears it). A single write is limited to 6,000 characters and each memory to 24,000. They are stored under `.ample/ai/memory/`, outside the working copy and song exports.
How-tos for what is not a plain parameter. `ample_lookup topic=howto query=` gives, in code-accurate form, how to change a value over the song (`auto(bar:value,…)`, `lfo`, `env`, `fadein`/`fadeout`, which parameters accept it: volume gain, the mix of delay, reverb, chorus, flanger, phaser and bitcrusher, compressor threshold, filter cutoff, pan; bars to seconds), fades, speed and tempo (the tempo is constant and not automatable; stretch.speed accepts auto(bar:percent, ...) for gradual ramps; project tempo remains constant), placing and cutting, sidechain and adding an effect, and says what is NOT possible with the closest workable alternative. `topic=diagnostic` explains an error code with a wrong and a right example, and `search` also finds how-tos. `ample_edit` also has targeted transpose, velocity and quantize operations for compact MIDI notes.
Letting the user hear a block (clips)
Codex can present sound with `ample_present`: the render of a block (empty: the master; `block.port` for one output such as a sequencer lane), either a musically useful slice (`from`/`to` in seconds, or in bars with `unit=bars`) or the full render when requested or useful for comparison, as the proposal has it (the default) or as the current project is. The requested range is preserved without a 90-second cap; a full-song request is not silently truncated. A few milliseconds of fade are applied at both ends of a slice. It appears in the task window, also while Codex is still working, as a card Codex wants you to hear: label, which block, whether it is the proposal or the project as it is now, the range, why it is shown, a waveform player, and quick answers (Yes, that is it, No, another one, Play me more of it) that fill the message box. Ctrl+click (or Meta+click) on one of those choices sends its action immediately and includes the sample and block references in the message. Codex is told to use it when it is not sure which instrument or part you mean (present the candidates and say which it chose), to let you compare before and after, and to let you check a result.
Codex alone marks samples obsolete and supplies the reason; the user can hide a card with × and undo that hide with Ctrl+Z. When Output is open, its content is the only vertical scroller; the task body and tabs stay fixed.
What was presented is remembered: each request in the history keeps the label, block, source, range and reason of its clips (`ample_history`), so later you can say "I think you are modifying the wrong instrument, play me what you think I mean" and Codex can find what it had presented and present it again. The audio files are kept for the session (`.ample/ai/clips/`), the information stays in the history. API: `GET /api/ai/tasks/{id}/clip/{clip}`.
Requests for a Flemish sound
When a request asks for music to sound more Flemish, Codex is guided to treat that as an open direction rather than a single genre or a caricature. It should follow details the user gives, avoid inventing dialect, and consider whether the request concerns Flemish Dutch wording/prosody, a local or traditional feel, contemporary Flemish music, or another quality. When the intended direction is open to interpretation, it can offer a few musically distinct short variants with ample_present, explain their differences, and let the user choose. Codex may also offer extra iterations as an option—for example, explore contrasting directions, compare them, then refine the chosen one. Extra rounds are optional and should help resolve an open direction; they are not added to narrow requests and do not guarantee a more Flemish result.
In a finished proposal, Codex's written summary is part of the scrollable Output content, below the fixed tab row, so opening Output does not shift the tabs vertically.
Assistant tab sessions
The bottom Assistant tab stores named conversations in the open project's .ample/ai/assistant-sessions/ folder. The index keeps each session title and task references; task snapshots and the bounded activity log are stored beside it. Project switching therefore loads a different session index. Create, rename and delete are available in the tab; a session with a live task or an unapplied proposal cannot be deleted.
Assistant task outcomes are also written to AMPLE Redux, including redacted request/summary metadata, proposal change counts and hashes for content-addressed before/after text, plus presented audio clip hashes and render settings. Full conversation capture is off by default. When creating a session or from its archive control, it can be enabled for new messages; an additional explicit choice can import earlier messages still available for that session. Captured text is redacted for common credential patterns before it enters Redux. Existing task-log files may contain earlier conversation text, but the Assistant view hides those messages while capture is off.
The Health page shows aggregate AMPLE Redux storage use. The default 10 GiB budget warns at one fifth (2 GiB); changing the project budget changes both thresholds proportionally. Preview compaction shows the exact segment, record, summary and byte impact; compaction then requires a separate confirmation. Records are never pruned automatically.
Starting from the tab opens the same full prompt and proposal interface as other Codex entry points. The task keeps the session ID, while existing task APIs remain usable; callers that omit it receive an automatically named session. The session list opens its newest task and exposes earlier requests in the same conversation.
Codex threads are started as persistent app-server threads. After a server restart, the task snapshot and temporary project copy are retained. The task appears as interrupted and offers Resume conversation, which reconnects with thread/resume and registers the AMPLE MCP server at the current server URL. A task stopped with Stop now can also resume when it already has a saved thread; it reconnects to the same conversation and leaves the proposal unaccepted until you continue or accept it. Tasks cancelled before Codex starts a thread offer Ask again instead. The proposal remains unaccepted if the thread cannot be resumed. Runtime recovery still depends on Codex retaining that thread and the temporary work directory remaining available.
Accept selected & continue is available for one or more checked proposal files when the task is ready, not stale, and its complete proposal build succeeded. AMPLE validates the selected text, checkpoints the project, journals non-song writes, updates the accepted baseline, and then sends the supplied continuation instruction into the same Codex thread. Remaining files stay proposed in the temporary project. If the follow-up fails, already accepted files remain in the project and the task shows the failure; no remaining files are accepted automatically. API: GET/POST /api/ai/sessions, PATCH/DELETE /api/ai/sessions/{id}, POST /api/ai/tasks/{id}/resume, and POST /api/ai/tasks/{id}/accept-selected-continue.
Moves that Codex proposes (2026-10-10)
Codex can propose moving or renaming a project file or folder (a sample, a module, a song, a whole folder) with the tool ample_move. A move is never made in the temporary copy (it holds only the files in scope, not your audio or your other songs): it is made at Accept, after the file changes, one by one, with every path in the songs and modules that points at it following (the same refactor as Move in the project panel). Codex chooses how:
confirm: "ask": you get an approval card now (Codex asks to move a file or folder) and Codex waits for your answer. Approve the move records it as approved, Deny tells Codex, which goes on without it. Codex uses this when the rest of its work depends on the move. In Unsafe / Auto-all mode there is no question.
confirm: "propose" (the default): the move is listed under Moves Codex proposes in the proposal with a checkbox, the number of lines in your songs that will follow, and the reason. Ticked moves are made at Accept; nothing moves before.
Refused when it is proposed: a source that does not exist, a destination that exists (a move never overwrites), a folder into itself, .ample, the song of the task itself, or a path that is already part of another proposed move. A move is skipped at Accept (with a note in the log) when a song that would have to change has unsaved changes, or when the source is gone or the destination exists by then. Undo puts accepted moves back, the references following again. Accept selected (and Accept selected & continue remaining) make the approved and the ticked moves too, after the checked files; a move that is not ticked stays in the proposal.
The separate tool ample_move_file moves a text file inside the staged proposal without overwriting. Its source and destination are project-relative; workspace/<filename> can recover a misplaced text file from the working-copy root. It changes no live project file before Accept. Use ample_move for a project file or folder whose references need to follow the move.
The tool ample_copy_file (2026-10-14) copies a text file inside the staged proposal to a new, not-yet-existing path; unlike ample_move_file the source stays in place. Use it to duplicate a sequence or module before changing the copy, so other songs that still point at the original are unaffected — for example giving one song its own patched copy of a sequence that other songs also use. A workspace/<path> source instead stages a file ample_fetch_url downloaded into assets/web/ of the working folder (an image, a PDF, a fetched score) into the project, since nothing else can move a fetched asset out of the working folder and into the proposal.
ample_import_score (2026-10-15) also accepts a staged PDF or image of printed music, recognized with Audiveris (optical music recognition) when it is installed, and a MuseScore/Guitar Pro/Capella/Overture file or a zipped .mxl, converted with MuseScore (or unzipped directly for .mxl) — see docs/score-import.md. The typical flow for a score found on the web: ample_fetch_url to download it, ample_copy_file with a workspace/ source to stage it into the proposal, then ample_import_score on the staged path. OMR can misread a source, so the tool's answer says to check the note count before trusting it; a MuseScore/.mxl conversion is a faithful machine conversion, not recognition.
API: POST /api/ai/context/move (for the tool), moves: [id] (the ticked ones) on POST /api/ai/tasks/{id}/accept, accept-selected and accept-selected-continue, moves in the task JSON. ample_copy_file posts to POST /api/ai/context/copy-file.
Access outside the project: what the assistant may do and how it asks
Status (2026-10-11): built. For the assistant and for people.
The assistant (Codex) works in a copy of the project. Inside that copy and in its ./scratch folder it needs no permission. Everything else needs the person's say-so.
For the assistant
Remarks about AMPLE (what is missing, what could be better, how to fix it): ample_findings { text, name? }. It writes to notes/ai-findings/ inside the project. No permission needed. Say where it is in your last message.
A file somewhere else (a full path such as C:\temp\x.txt): first ample_request_access { path, why, minutes, kind? }. The person sees what, why and for how long, and answers allow (for that long), always for this project or deny. After an allow use ample_write_outside { path, text }. Never try another way to write there.
If you try anyway (a command or a file change outside the working folder) AMPLE stops the run at that request and asks the person: allow once, always for this project, or ask it to do it correctly; in the last case you are told to use ample_findings or ample_request_access.
What is missing: when a tool answers with a hint (a PDF reader, the voice worker, ffmpeg), tell the person and recommend the hint. ample_project_log lists them with how to fix.
For people
The question appears in the task window with the path, the reason and the time. Always for this project is kept in .ample/ai/grants.json and listed (with Revoke) under Projects, Health.
ample doctor --env checks what the tools need around the program (libraries, ffmpeg, Python, the sound font, free disk space) and writes what is missing, with how to fix it, to the project log (.ample/logs/project-log.jsonl, shown on the Health page).
A grant of kind passthrough only allows AMPLE's own ample_write_outside, never a direct write.
Never open for writing: the project's own .ample folder and the system folders.
Codes
AMP5610 (a path that can never be allowed or is not a full path), AMP5611 (no permission for that path), AMP5458 (the task is no longer running: AMPLE was restarted or the task ended; start a new task, nothing is lost in the project).
History v1: branches and promote (mock)
Status (2026-10-02): design sketch from a brainstorm. Nothing here is built or tested.
Keep it simple for now: you can branch, but you never merge content. You promote one branch to be the main line and continue from there.
Model
Revisions form a graph with parents[] (so multiple parents are representable, e.g. when importing from git), but the tool only creates single-parent revisions.
main is the main line. Other branches are experiments.
Promote = choose: main now points at the branch tip. No content is merged, so there are no conflicts.
The old main tip is kept as archive/main-<date> (read-only, can be branched from again). Nothing is deleted.
Sealed releases stay valid forever, also those made on an archived line; ample lineage shows them under the archive.
Every promote writes an entry in the hash-chained ledger: which branch, which old tip, who, when.
Commands
ample branch <name> # new branch from the current revision
ample branches # list with revisions ahead of main
ample promote <name> --dry-run # show what changes and what drops off main
ample promote <name> # main -> <name>, old main tip archived
ample branch --from archive/<name> <new> # continue an archived line
Rules for promote
Clean working tree; static checks (syntax, cycles, hash references) pass on the target.
If main has moved on since the branch point, the dry run lists the revisions and releases that drop off main (they stay in the archive).
Other branches stay as they are; promoting one of them later replaces main again. Never a merge.
Git mirror
Promote moves main of the mirror; the old tip is kept as a hidden ref refs/ample/archive/*, so ordinary git clients do not see branches vanish. The mirror is derived data and may be force-updated.
Listening A/B between branches (cheap, fits v1)
ample ab <branch1> <branch2> --block chorus previews the same block from both branches.
Navigating history: go, promote, peek, undo
Every revision ever made stays visible and reachable; moving around never rewrites history, only pointers move.
Two pointers
here (current): the revision you are on and working from.
the real deal (main): the revision that counts as the official one.
Double-clicking any revision moves here. "Make this the real deal" (promote) moves main. They are independent, so you can look and work without changing what is official.
Example. Go 5 revisions back (double-click, or ample go ~5). Make a commit: because you are not on a tip, it starts a new line automatically (named after the fork point, renamable; there is no loose "detached HEAD" where work can get lost, every tip stays reachable). Then change your mind: "1 back from the original line, then 5 up" is another double-click, or ample go ~1 followed by ample go +5. +N follows the path you walked before (like a browser's forward button), also through forks. In the graph: left/right along a line, up/down to switch line at a fork.
The graph (like a git client): all revisions including other lines and the archive (faded), with markers for here, the real deal, releases and bookmarks. Context menu: Go here · Look only · Make this the real deal · Fork here (name a line) · Compare with now or with the real deal · Listen A/B · Bookmark · Take just this block · Copy id. Search by block, message or release.
Commands
ample log # the graph: every revision, all lines (hashes shown here)
ample leaves # the branches/leaves from where you stand (the natural way to navigate)
ample go <line|leaf|~N|+N> # move "here" by line name, leaf number or steps (--peek: read-only)
ample go <hash> # allowed, but the exception; normally you pick from `leaves`/`log`
ample promote <rev> # move the real deal; old tip kept as archive/main-<date>
ample undo ample redo # step back and forth in the operation log
ample mark "before-the-remix" # bookmark
ample purge <rev> --reason "…" # real deletion: deliberate, logged, rare
rewind is simply go + promote; restore is not needed: a commit after go forks by itself.
Operation log (like Jujutsu's): every go and promote is recorded, so undo and redo always take you back to where you were.
Safeguards
Moving with unsaved work makes an automatic checkpoint (auto/...), so nothing is lost. Automatic checkpoints are also made before promote, block updates and migrations.
What comes back with a revision: songs and editable text sources (.amplet, lyrics, chords, .txt, .md, .csv, .json, .seq and .ampleseq), lock, cuts, baked data blocks and the modules enabled in the project. Takes stay in the shared take bank (immutable). Secrets live outside history and do not change.
Sealed releases stay where they are; going back never un-releases. Lineage keeps showing them.
A baked block is only cleaned up (ample gc) when no revision or archive refers to it.
Tools from back then may be missing: verify --present warns ("acestep@b7ba6d9 not available; baked data is present").
Revisions may also be addressed by a date (@2026-10-05) or relative (@5d), but the primary way is the revision itself.
Credentials get their own timeline
Secrets (API keys, tokens, signing key, passphrases) are not part of the project history. They live in a separate, protected store with their own timeline: every change (added, rotated, revoked, who/when) is a numbered entry, but the values are sealed (master passphrase, optional recovery key). Consequences:
Going back in the project history never brings back or exposes an old secret, and never overwrites the current one.
A revision only records which credential it needed (name and version), not the value. verify can say "needs weather_api v2, current is v3".
Credentials have their own log, undo (restore a rotated value) and expiry; a revoked value can be purged on its own.
The credential timeline is included in a backup only as sealed data, see Backup.
Purge is a dangerous act
Removing data that is no longer relevant (old takes, baked blocks, archived lines, old credential versions) must be possible, but it is the one operation in AMPLE that cannot be undone, so it is treated as dangerous:
Never implicit. Nothing purges as a side effect of another command; gc only removes what nothing refers to and says so, anything else needs purge.
Explicit acceptance.ample purge first shows a dry run: what is removed, how much space, which revisions, releases, lineage links and baked blocks lose their data, which releases would drop from data-exact to recipe-exact (or become unreproducible), and what stays untouched.
Backup suggestion. Before anything is deleted it says: "This cannot be undone. Make a backup first?" and offers ample backup right there (profile suggested from what is affected). Skipping needs an explicit choice.
Acceptance by typing, not a bare yes: the user types the scope (for example the number of revisions or the line name), or passes --accept "<what>" with a reason in scripts. A UI needs the same: a red dialog listing the impact, a backup button, and a confirm that stays disabled until the scope is typed.
Leaves a tombstone: the operation log keeps who, when, what scope and the reason; revisions stay in the graph as "purged (data removed)" so lineage remains understandable. Sealed releases are never purged without separate, stronger confirmation.
Navigating by leaves, not hashes
Hashes are poor handles for people, so go is aimed at lines and leaves (the tips you can actually continue from); a hash is accepted but is the exception. ample leaves shows, from where you are now, what is reachable and how far:
$ ample leaves
here ● chorus-rework (3 revisions after the split from main)
[1] ★ main +2 ahead of the split "mastered v2" release: v2
[2] verse-redo +4 "new voice for verse"
[3] archive/main-2026-10-02 +1 (archived) faded
[4] auto/before-promote +0 (automatic checkpoint)
$ ample go 2 # or: ample go verse-redo
Each entry shows the line name, distance from here (back to the split, then forward), the last message, markers (the real deal, releases, archived, automatic) and a play/listen hint. --all includes archived and automatic ones; --tree draws the graph. The UI graph is the same view: leaves are the highlighted ends, double-click one to go there.
Dropping a leaf: archive first, clean up later
Experiments that went nowhere are common, so dropping a dead-end leaf must be easy, but deleting stays dangerous. Two steps:
ample drop <leaf> = mark as archived (the usual, safe action). Nothing is deleted: the leaf (and the revisions only it contains) fade out of leaves and log, stay in log --all and the graph's archive filter, and ample unarchive <leaf> or undo brings them back. Revisions shared with another line are never affected; only the part reachable from no other line counts as the leaf's own.
ample cleanup = the real removal, later, on purpose. It targets archived leaves (never live ones) and is a purge: dry run with impact (revisions, space, baked blocks, releases that lose data), a backup suggestion, acceptance by typing the scope, a tombstone in the log. It can also be limited by age (--archived-older-than 30d) and shows which archived leaves it would take so single ones can be skipped.
Rules: the real deal and the revision you are on cannot be dropped; a leaf holding a sealed release is not dropped without a stronger confirmation; drop on a leaf with children offers to drop the whole branch (listing the children) or just that tip. Dropping a line with a name asks for that name.
$ ample drop 2 # verse-redo: marked archived (4 revisions), undo available
$ ample cleanup # later: lists archived leaves, shows impact, suggests a backup, asks you to type the scope
The name of the real deal, and tags
Name of the main line. It is called studio and a new project starts with studio > garage (see "Studio and garage" below). The names are defaults and are not meant to change; they can be renamed if someone really wants to. Importing from git maps main/master to studio.
Tags are locked names for a revision. Meant for releases ("v1.0", "radio-edit"), but any revision can be tagged.
ample tag v1.0 [<rev>] creates a tag (default: here). A tag never moves by accident: tagging an existing name fails.
ample tag v1.0 <rev> --force --reason "..." re-tags. It is allowed but dangerous-lite: the old target stays in the tag's own history (ample tag --history v1.0) and in the operation log, so a re-tag can be undone and nothing is lost silently.
Removing a tag needs --force too and is logged; tag history is kept.
ample release can tag automatically (release v2 creates tag v2 on the sealed revision). A sealed release's tag cannot be force-moved to a different revision; a different revision means a new release and a new tag.
A tagged revision is protected: drop warns and cleanup skips it unless the tag is removed first on purpose.
Tags show in leaves, log and the graph as labels; go v1.0 works like any other target. Git mirror: tags map to git tags (annotated, with the hash of the release).
Checkpoints: a quick lookup table of named moments
A checkpoint is a light, named pointer to a revision, a quick way to find a moment again ("before the remix", "chorus works"). It replaces the earlier mark bookmark.
ample checkpoint ["name"] (UI: a button next to save). Without a name the default is a timestamp, e.g. cp-2026-10-02-1415. It records the revision you are on, and makes one first if there is unsaved work.
Unlike tags, checkpoints are not locked: they can be renamed, moved and removed freely (removing one deletes only the label, never the revision). That is what makes them cheap.
Tags stay for what must not move (releases, see above). A checkpoint can be promoted to a tag: ample tag v1.0 --from-checkpoint "chorus works".
ample checkpoints lists them (name, time, line, message), newest first, searchable; ample go "chorus works" jumps to one, and the graph shows them as small labels with a filter. They count as markers in leaves.
Automatic ones (auto/..., made before moves, promote, updates, migrations) are checkpoints too, but kept apart: hidden by default, grouped, and thinned out after a while (never the ones a revision or tag points at).
A checkpoint protects nothing from cleanup by itself (it is only a label); a dropped leaf's checkpoints show as archived with it. Tags do protect.
Protecting tags from tampering
Principle: moving a tag must be harder than making a revision, and never silent. Layers (1 to 4 default, 5 and 6 opt-in):
No silent overwrite: an existing tag name cannot be reused by plain tag.
Typing to re-tag:--force alone is not enough; the user types the tag name and a mandatory reason after seeing an impact summary (old and new target, whether a release or sealed hash hangs on it, who set it before). Same pattern as purge. Scripts need --accept "<tag>" --reason "..."; the UI has no shortcut.
Tag types:release tags are created by release and can never move (a different revision means a new release and a new tag); normal tags can be re-tagged under layer 2; a tag can be frozen (ample tag --lock) so only unlock (same confirmation) releases it.
Tag history: every create, move and delete is in the operation log (who, when, why) and reversible with undo. Backups and the git mirror carry the tag history.
Signed tags (module signing, opt-in): a tag signed with the author's key; verify reports a move made without that key ("not set by the owner"), which also catches edits made outside AMPLE.
Tags inside the sealed release: a release records its tag names and target hashes; verify flags a tag that no longer points at the sealed revision, also in a copy of the project elsewhere.
Studio and garage
A new project has two lines: studio (the top line, the real version) and below it garage, where you work; saves land on garage.
Promote:garage promoted to studio sets studio to that revision. The old studio tip stays in history.
Fork from studio: after a promote you can fork studio again, giving a new garage, and promote that one later too.
No merge: when the second garage is promoted, the first garage is no longer reachable in the daily view (only through commit history), the same rule as archived leaves.
Going back to a commit rebuilds the tree as it was. Every promote and fork records where each line stood (a snapshot of the pointers, kept with the operation log). ample go <rev> then asks: "Should studio be replaced too?"
No (default): only garage (and any other children of studio) is restored, as a child of the current studio. studio is not overwritten.
Yes:studio also returns to its state back then; the old top is kept as an archive and undo reverses it.
Releases are tags (locked, see above) on any line, recommended from studio. There is no separate top level for releases.
Any revision, tag or release can be opened temporarily and read-only, for example to rebuild an older release. This is ample go <rev|tag> --peek (UI: "Open read-only"); it is a mode, not a move of here.
Nothing in history can change: saves, checkpoints and promotes are refused while peeking. The banner says what you are on ("read-only: v1.0 (tag), 2026-09-12").
Rebuild is allowed:render/master and verify --repro run on the checked-out sources, lock and baked data. Results go to a scratch area (.ample/scratch/<rev>/), never into the project's takes or releases, so the sealed release stays untouched. Compare the rebuilt hash with the sealed one: same hash = reproduced; different = reported with the toolprint differences (a tool or model from back then may be missing: verify --present says what).
Leaving is free:ample go --back (or closing the peek) returns to where you were, with your unsaved work exactly as it was. The scratch area is removed on leave, or kept on request (--keep-scratch) and listed for cleanup.
Turning it into work: "work from here" (ample go <rev> without --peek) forks as described above. A rebuild you want to keep can be saved as a new take or release only from a normal (non-peek) state.
History management (detail)
Status (2026-10-02): design sketch from a brainstorm. Nothing here is built or tested. History v1 holds the decisions (lines, studio and garage, promote, tags, checkpoints, drop and cleanup); this page works them out into a data model, operations, screens and scenarios. The clickable sketch in sketch/ shows the screens.
1. Principles
Nothing is lost silently. Every move is a pointer move recorded in the operation log; only cleanup and purge remove data, and both are dangerous acts with a dry run, a backup suggestion and typed acceptance.
Everything stays reachable until it is deliberately removed: any revision, any line (also archived ones), any earlier arrangement of the lines.
Revisions are immutable and tamper-evident (their id is a hash of content and parent).
No merges in v1. Lines diverge; one is promoted. "Adopt this block" and structure-aware merge are tracked in Future ideas.
Natural navigation is by line, leaf, checkpoint and tag; hashes are the exception.
Two audiences, one model: a simple view that shows what matters, an advanced view that shows everything.
2. Data model (SQLite in .ample/history.db)
revision id (hash of content+parent), parent, line_at_creation, author, time, message,
kind (save | auto | adopt | migration), content_ref, summary_json
content id (hash), sources_tree_hash, lock_hash, settings_hash, cuts_json, baked_refs_json, modules_json
line name, tip, forked_from (revision), role (studio | garage | fork), state (live | archived), created, renamed_from
tag name, target, type (normal | release | frozen), created_by, created_at + tag_history(name, target, op, by, at, reason)
checkpoint name, target, auto (0|1), created_at
arrangement id, taken_at, studio_tip, lines_json one row per promote and fork: where every line stood (so "restore the tree as it was" works)
operation id, time, by, kind, args_json, before_json, after_json, danger (0..3), reason the operation log (undo and redo walk this)
tombstone op_id, scope, what_was_removed_json, size, backup_ref what cleanup/purge removed
release id, tag, revision, sealed_hash, kind (recipe-exact | data-exact), lineage_json
A revision id is hash(parent id + content id + author + time + message), so history cannot be rewritten without changing ids; with the signing module the id is also signed.
Content is separate from the revision so identical states share rows; sources are stored as a tree of file hashes (a snapshot), not as diffs.
Takes and baked data are not in the database; revisions reference them by hash (take bank, see Bake).
Secrets are never in history; a revision records only which credential name and version it needed (see section 9).
3. What a revision contains
| In the revision | Not in the revision |
| --- | --- |
| Sources (.amplet, lyrics, MIDI, chords, packs list), ample.lock, cuts, which baked blocks are used, the enabled modules, project settings that affect results, the layout of the block graph | Credentials, machine-local settings, the cache and scratch, the take bank files themselves (referenced by hash), unsaved drafts |
Layout-only changes (moving a block on the canvas) are saved but marked layout in the summary, so they do not look like musical changes in the history and can be hidden by a filter.
4. Operations (what each does, how risky)
| Operation | Effect on pointers | Undo | Danger |
| --- | --- | --- | --- |
| save | new revision on the line you are on (forks by itself if you are not on a tip) | undo | 0 |
| checkpoint | adds a light label (renamable, movable, removable) | undo | 0 |
| tag | adds a locked label | undo | 0 |
| go | moves here; if the target studio differs from now, asks "replace studio too?" (default no) | undo | 0 |
| go --peek | read-only checkout; rebuilds go to scratch/ | leave | 0 |
| fork | new line from a revision | undo | 0 |
| promote | moves studio; old tip kept as archive/studio-N; no merge | undo | 1 |
| drop | marks a leaf archived | unarchive / undo | 1 |
| rename line | renames, keeps renamed_from | undo | 1 |
| re-tag / unlock tag | moves a locked tag; old target in tag_history | undo | 2 (typed name and reason) |
| cleanup | removes archived leaves and unreferenced data for real | no (only from a backup) | 2 (dry run, backup suggestion, typed scope, tombstone) |
| purge | removes chosen revisions or credential versions for real | no | 3 (like cleanup, stronger for anything holding a sealed release) |
| undo / redo | steps through the operation log (does not undo cleanup/purge) | redo | 0 |
Unsaved work is never a revision. When you move away from unsaved changes, AMPLE takes an automatic checkpoint (a draft revision of kind auto, hidden by default) first, so nothing is lost; a crash leaves a draft that is offered back on the next open.
5. The History screen (anatomy)
┌ toolbar: [Save] [Checkpoint] [Promote] [Fork] search [___________] filters ▾ view: (•) Simple ( ) Advanced [Compare] [Export] [Cleanup]
├ graph (lanes: studio, garage, forks) ┬ detail of the selected revision
│ ★ = studio ◯ = here ⚑ checkpoint 🏷 tag │ message, author, time, line, tags, checkpoints
│ faded = archived │ changed blocks, layout-only flag, takes used
│ │ buttons: Go · Look only · Make studio · Fork · Checkpoint · Tag · Compare
├ leaves list (ample leaves) ┴
└ status line: here r12 (garage, tip) · studio r9 · 3 unsaved changes · last backup 2 days ago
Simple view: the recent revisions plus everything that matters (tips, studio, here, tags, user checkpoints), compressed with "... 14 older" markers. Archived lines and automatic checkpoints are hidden.
Advanced view: every revision, archived lines (faded), automatic checkpoints, ids and parent links, tools and baked counts.
Filters: text (message, block name), author, line, "touched block ...", tag or checkpoint only, date range, kind (save, auto), layout-only on/off.
Keyboard: arrows move along a line and between lines at forks, Enter = go, Space = look only, P = promote, F = fork, C = checkpoint, T = tag, / = search.
Everywhere else: the header always shows the line you are on and the studio revision; the Structure browser (left) is the same data as a tree.
The branch tree and the "branch laid out" view
Structure panel (left), simple view: only the path from the root to the branch you are on plus its direct leaves (branches forked from it), and a + New leaf under button that forks from where you are. Recent revisions of the active branch sit in a collapsed list. This replaces long revision lists in the tree.
Structure panel, advanced view: the whole branch tree (parent to children), archived branches faded.
Clicking a branch name opens the Branches tab: one row laid out left to right: the root (studio) on the left, an arrow to each branch on the path, the focused branch highlighted as THIS BRANCH, and its direct leaves stacked on the right. The row scrolls sideways and starts centred on the focused branch. Each branch is a box with its tip message, where it forked from, a strip of revision dots (the dot where the next branch forks is outlined, the dot you are on is ringed) and buttons Go, Look, + Leaf, Drop or Unarchive. Clicking another box refocuses the row.
Branch manager (dedicated view): a switch above the canvas replaces the block view with the whole hierarchy as a scrollable tree (root left, siblings stacked, any number of leaves per branch and leaves of leaves). Focusing a branch there also centres it in the Branches tab; you can fold subtrees, search, show archived, rename, go, look, add a leaf and drop. The Branches tab under the canvas deliberately shows only the direct line.
Parent of a branch is derived, not stored: the line of the revision its first revision was forked from. Promote only moves studio; it does not change who is whose parent.
6. Compare
Pick two revisions (or a revision and "now"). The result is structural, not a text diff:
Blocks: added, removed, renamed (detected by stable block id), moved (layout only), changed.
Parameters: old to new per block, with the node seed unchanged or changed.
Links: added and removed connections.
Takes: same take hash, different take hash, or baked versus recipe. A changed dynamic block offers Listen A/B (plays the two takes, aligned).
Text: the .amplet diff as a secondary tab.
Verdict line: "audio probably changed in: chorus_vox, mixer, master" (derived from which blocks and their downstream changed), "nothing audible changed: layout only".
7. Search
An index over messages, authors, touched block ids and parameter names (kept in SQLite). Queries like "where did chorus_vox's seed change", "all revisions by tinne in October", "everything tagged release". Results open in the graph with the matches highlighted.
8. Going back, forking and restoring the tree (worked scenarios)
Go back 5, save, then 1 back and 5 up.go ~5 moves here. save from a non-tip forks a new line automatically (named from the fork point). go ~1 then go +5 follow the path you walked (like a browser's back and forward); at a fork the arrow keys choose a line.
Promote. garage tip r12 becomes studio. The old studio tip r9 is kept as archive/studio-r9. A second garage forked from the new studio and promoted later makes the first garage unreachable in the daily view (still in the graph through "show archived" and in advanced).
Restore the tree as it was.go r7 asks: "Should studio be replaced too?" No (default): only garage (and other children of studio) come back, as children of the current studio. Yes: studio also moves back to what it was at r7, the old studio goes to the archive, undo reverses it. This uses the arrangement rows.
Experiment that went nowhere.drop the leaf (archived, undoable). Weeks later cleanup lists archived leaves with their size and impact; you choose which to remove for real, after the backup suggestion.
Rebuild an old release.go v1.0 --peek opens it read-only; Build runs into scratch/; compare the rebuilt hash with the sealed release; leave and everything is as before.
9. Credentials have their own timeline
Credentials are not part of revisions. The vault has its own numbered timeline (added, rotated, revoked, by whom) with sealed values. A revision records which credential and version it needed; verify reports "needs weather_api v2, current is v3". Going back in the project never restores or exposes an old secret. Old credential versions can be purged on their own (dangerous act). See Backup for how the vault travels.
10. Releases in the history
A release is a locked tag plus a sealed release record (hash, recipe-exact or data-exact, lineage based on / remix of). It can sit on any line, recommended on studio. The history screen marks releases, and the lineage view shows how releases derive from each other (also across projects, by uuid). A sealed release's tag cannot be force-moved; a new revision means a new release and a new tag. Tag protection layers are in History v1.
11. Storage and size
Revisions are small (hashes and JSON); the size is the take bank and baked data.
ample size reports by line and by revision: how much each line uniquely holds, so cleanup choices are informed ("verse-redo holds 1.2 GB that nothing else uses").
Cleanup candidates: archived leaves, takes referenced by no revision, tag or checkpoint, old auto checkpoints beyond a policy (manual for now), scratch and cache.
Never a candidate without extra confirmation: anything a sealed release, a tag or a user checkpoint refers to.
12. Integrity
Single writer per project (instance lock); SQLite WAL; every operation is one transaction.
ample verify-history recomputes revision ids and checks parents; ample doctor includes it (quick: recent revisions; full: all).
With signing, tags and releases are signed; a move made outside AMPLE (editing the database) shows up as "not set by the owner".
A rolling daily copy of history.db protects against corruption.
13. Export and mirror
Export a slice (ample export --scope line:garage or tag:v1.0 or checkpoint:"chorus works") as a .ample file with chosen contents; opening it creates a new project or, later, imports the line into an existing one (see Future ideas: pull and push).
Git mirror (module): one-way mirror of lines and tags with the release hash in annotated tags; conversions both ways keep raw git objects for a lossless round trip.
Should a timeline scrubber (drag through revisions with a quick audio preview of the master) be part of v1 or a later nicety?
How long are auto drafts kept, and when are they thinned (manual for now; propose a gentle default later)?
Do layout-only changes get their own revisions, or are they folded into the next real change?
Should compare show sound differences measured on the rendered takes (loudness, spectral change) next to the structural ones?
Is the operation log shown to the user as a full panel, or only through undo and redo and the Log tab?
Project management
Status (2026-10-11): partial. The CLI supports ample new, ample open, ample doctor [--env], ample instances and project inspection; ample open attaches to or starts the server for a project. The lifecycle, template and management sections below include planned behavior and are not a claim that every listed UI, CLI or API operation is implemented. The clickable sketch in sketch/ is a historical reference.
Project management and history management (History management) are the two things the whole tool leans on, so this page goes into detail. It covers what a project is, where its data lives, how it is created, opened, copied, archived, moved and deleted, how settings are scoped, how health is checked and how format changes are migrated.
1. Vocabulary
Term
Meaning
Project
A folder that contains a mandatory .ample/ folder. Everything the project needs to reproduce its songs is inside it or reachable through its links.
Home
The per-user AMPLE folder (for example %APPDATA%\ample or ~/.config/ample): global links, recent projects, plugin and model catalog, user templates, global settings. Never part of a project or a backup of a project.
Engine
The running ample serve process. One engine can serve several projects at once (each in its own browser tab).
Link
A named folder outside the project that is exposed in the folder browser (see Tech, "Linked folders"). Global or per project, read-only or read-write.
Take bank
The immutable, hash-addressed store of rendered audio and baked data of a project.
2. What is in a project folder
my-song/ the project (any name; it can be renamed or moved)
song.amplet lyrics/ midi/ chords/ packs/ ... user content (sources)
ample.lock pinned tools, packs, external data
.ample/ mandatory; AMPLE's own data
project.json id (uuid), name, format version, created, master seed, modules, main line names
history.db SQLite: revisions, lines, tags, checkpoints, operation log, tombstones
takes/ the take bank (hash-addressed, immutable)
data/ baked data blocks (hash-addressed)
links.json project links (uuid, name, read-only flag, relative path hint); global links live in Home with their uuid
assets.json uuids of project assets (assigned the first time AMPLE sees them; survive rename and move)
vault/ sealed credential store and its own timeline (no plain secrets)
plugins.lock pinned plugin versions and toolprints
cache/ build cache (disposable, safe to delete)
scratch/ read-only checkouts and rebuilds (disposable)
lock instance lock (pid, host, heartbeat)
logs/ engine logs (no prompts or lyrics: see privacy below)
.ampleignore what the source scan and snapshots skip
Rules:
Disposable means safe to delete at any time without losing information: cache/, scratch/, logs/.
Precious means loss is permanent: history.db, takes/, data/, vault/, the sources and ample.lock. Backups cover these (profiles decide how much, see Backup).
project.json holds a uuid, not the path, so a project can be moved, renamed or opened from a backup restore and still be recognised in Recent and in the lineage records of releases.
A folder with sources but without .ample/ is not a project; the app offers "Make this an AMPLE project" (creates .ample/, first revision "adopted folder", scans the sources).
3. Lifecycle
Create
A wizard (UI) or ample new <folder>:
Name and location. The folder name defaults to a slug of the name; the name itself is free text.
Template (see section 4).
Modules to enable (all off except core by default, see Modules).
Master seed: random (default) or typed. It is shown once and stored in project.json; changing it later is a deliberate, logged act, because it changes every derived seed (and therefore every dynamic result not baked).
Links: pick global links to use here, add project links.
First revision: the project starts with the lines studio and garage, one revision "new project" on studio, you are on garage (see History v1).
The engine writes .ample/, takes the instance lock and opens the project.
Open
Recent list in the Projects manager (name, path, last opened, line and revision you were on, health badge, size).
Open folder: a folder picker served by the engine (it only shows what the engine may see: the user's chosen roots and links). A folder with .ample/ opens; a folder without offers adoption.
Drag and drop a folder or a .ample export file onto the Projects manager; a .ample file is never opened in place: it is restored or imported into a new folder first (see section 7).
URL:http://127.0.0.1:5174/?project=<uuid> opens a known project (tabs can be bookmarked).
On open the engine takes the instance lock, checks the format version, runs a quick health check (a few seconds, never a full scrub) and restores your last view (line, revision, open tab). If you left unsaved work it is offered back (see History management, "Unsaved work").
Switch and close
Each browser tab is bound to one project. Closing the tab releases nothing by itself; the engine releases the instance lock when the last tab of a project closes (with a short grace period) or on shutdown. Unsaved work is kept as a draft, not lost.
Rename, move, duplicate
Rename: changes the name in project.json (and optionally the folder name). The uuid stays.
Move: moving the folder is fine while it is closed; the Recent entry then says "moved? locate" and relinks by uuid when you point at it.
Duplicate offers three explicit choices: fork with history (new uuid, history and takes copied or hard-linked, the lineage records "copy of"), fresh start from the current state (new uuid, no history, takes referenced by baked data only), template from this (strips takes and history, keeps structure).
Archive and delete
Archive marks a project as archived in Home (hidden from the default list, nothing changes on disk). It can offer to compress takes/ and cache/ later.
Delete project is a dangerous act (see section 9): it shows what would be lost (revisions, takes in MB, releases that exist nowhere else), offers a backup first and asks for the project name to be typed. The default action is move to the system trash, not a hard delete.
4. Project templates
New-project templates are folders with an .ample/template.json and starter content:
| Template | Gives you |
| --- | --- |
| Empty | Core only, one tempo, no tracks. |
| Instrumental | Drums, bass, chords and a mixer block wired to a master chain. |
| Song with AI voice | The above plus lyrics, an AI voice block (plugin required) and a variants set. |
| Remix | Import audio or MIDI blocks, an overlap track, based on and remix of ready. |
Templates live in Home (yours), in a project (shared with its collaborators later) or in a pack. A template can pin plugin versions and links. "Save as template" strips history, takes, vault and secrets.
5. Settings and their scopes
| Scope | Where | Shared in exports/backups | Examples |
| --- | --- | --- | --- |
| Global (user) | Home | no | author name, default view (simple or advanced), global links, plugin catalog, default templates, theme, engine port |
| Project | .ample/project.json | yes | name, modules, assist level, tempo default, output folder, project links (without machine paths, see below), storage policy, backup profile and reminder interval, history view default |
| Machine-local | .ample/local.json (git-ignored, never exported) | no | absolute paths of links on this machine, plugin install paths, GPU/CPU choices |
Precedence: machine-local > project > global > built-in default. A project link records a logical name and, where portable, a project-relative path; the machine-local file maps names to real paths on this computer, so a restored project asks once "where is Samples on this machine?" instead of breaking.
Project settings that matter most:
Modules: quality, credits, signing, git-mirror, secrets, lyrics (opt-in; turning one on is a normal revision).
Assist: off, diff, button or auto.
Output folder: a read-write link or the project folder; a read-only link can never be chosen as output.
Storage policy: where big audio lives (inside, LFS, AMPLE store, external with accept-until), and the warnings that go with it.
Backups: profile (minimal, standard, complete), reminder after N days or N revisions, last backup time shown in the Projects manager.
History: default view (simple or advanced), cleanup is on request only (for now), automatic checkpoints are manual (for now).
6. Identity
Every revision records an author (from the global setting, default the OS user). It is a label for credits and history, not authentication: a local tool has no accounts. With the signing module the author also has a key (the private key is not in backups by default).
7. Import, export, backup and restore
Backup (ample backup) writes one .ample file: a zip with manifest.json (project uuid, name, format version, profile, what is inside and what is deliberately left out, tool and plugin versions), the history, takes and data per profile, and optionally the sealed vault. See Backup.
Partial export (ample export) takes a scope (project, line, tag, release or checkpoint) and a choice of contents (sources only, plus baked data, plus takes and audio, plus history, plus credentials sealed and off by default). It says plainly what is missing so the result can still be opened.
Restore always goes into a new folder and never overwrites. It verifies hashes, reports what is missing (and why), asks once for the machine-local things (link locations, plugin availability) and then opens like any project. A restored project keeps its uuid unless a project with that uuid is already known, in which case you choose "restore as copy" (new uuid, lineage "restored from").
Import handles: an .ample file (restore as above), a git repository (via the git mirror in reverse), an old recipe or .ggufmusic, audio and MIDI into blocks.
Round trip guarantee (goal): backup, then restore on another machine, then verify --hash passes; verify --repro passes for everything recipe-exact on a declared platform class.
8. Health and maintenance
ample doctor (UI: Health panel with traffic lights and a "fix" button per item):
| Check | Depth | Fix offered |
| --- | --- | --- |
| Instance lock is ours / stale lock | quick | take over (typed, shows pid, host and last heartbeat) |
| history.db integrity (PRAGMA integrity_check) and hash chain | quick / full | restore the last good copy (a rolling history.db.bak per day) |
| Format version vs this AMPLE | quick | migrate (with automatic backup and checkpoint) or open read-only |
| Links present and reachable; read-only flag respected | quick | relocate, remove |
| Tools and plugins present with the pinned toolprints | quick | install, or accept the difference (reported in verify) |
| Take bank scrub (recompute hashes) | full (slow, on request) | re-fetch from backup, mark take as lost |
| Unreferenced takes and baked data (size) | quick | cleanup (dangerous act, see history management) |
| cache/ and scratch/ size | quick | clear |
| Free disk space vs take bank growth | quick | warn |
| Cloud-synced or network folder detected | quick | warn (SQLite and sync tools do not mix) |
| Last backup age | quick | back up now |
ample doctor --fix only does safe fixes (clear cache and scratch, rotate logs); anything that removes precious data goes through the dangerous-act pattern.
9. The dangerous-act pattern (shared by project and history)
Delete project, purge, cleanup, re-tag, unlock a frozen tag, change the master seed, take over a stale lock, restore over a known uuid. All of them:
Show a dry run: exactly what changes, how much space, which releases or lineage links lose data, what stays untouched.
Suggest a backup and offer to make it right there.
Ask for typed acceptance (the project name, the tag name or the scope), never a bare yes; in scripts --accept "<what>" --reason "...".
Leave a tombstone in the operation log (who, when, what, why).
The UI uses one red dialog for all of them so the shape is always recognisable.
10. Concurrency, safety and privacy
One writer per project. The instance lock (pid, host, heartbeat) stops two engines writing to the same .ample/. A second engine can open the project read-only (peek mode for the whole project) or take over a stale lock after the typed confirmation.
Crash safety: SQLite in WAL mode; the take bank only ever adds files (write to temp, fsync, rename); a rolling daily copy of history.db.
Path safety: the engine serves only paths inside the project and its links; .. never escapes; read-only links are never written to, also not by plugins.
Privacy: logs and the health report never contain lyrics, prompts or file names of takes; secrets live only in the sealed vault and never in project.json, exports (unless sealed on purpose) or logs.
Network: the server binds to 127.0.0.1; remote use is explicit, with a token and an SSH tunnel (see Tech).
11. Format versions and migration
project.json carries format (an integer). On open:
Same format: open.
Older format: the app offers to migrate. A migration takes an automatic backup and checkpoint first, runs in a transaction and is recorded in the operation log. A failed migration leaves the project as it was.
Newer format: open read-only with a clear message (never try to write); unknown fields are always preserved on write by this version.
Should Recent and project tags/colours live only in Home, or should a project be able to carry a colour for its tab?
Duplicate with history: copy the take bank or hard-link it (fast, but ties the two projects to the same files)?
Is a project allowed to be opened from inside a cloud-synced folder at all (warn, or refuse)?
How is the master seed change handled: a new project (fork) instead of an in-place change?
Do project templates travel in the .ample export when asked ("export as template")?
Project layout and safety (as built)
Status (2026-10-02): implemented in chunk 40 (Windows verified; Linux runs when CI first runs).
A project is a folder with a mandatory .ample/ folder. Everything the user writes (songs, lyrics, chords) lives next to it; everything AMPLE manages lives inside .ample/. The design background is in Project management; this page describes what the code does.
<project>/
*.amplet, lyrics/, chords/ ... the user's sources
.ample/
project.json id, name, created, format version, master seed, settings
history.db history database (chunk 50)
takes/ data/ the takebank (chunk 42)
blackbox/ AMPLE Redux journal (legacy storage path; chunk 45)
locks/instance.lock held by the one writer (operating-system lock)
locks/instance.json heartbeat: process, machine, started, last seen
tmp/ scratch, safe to delete while the project is closed
backups/ copies of project.json made before a format upgrade
project.json
Canonical JSON (fixed property order, indented, LF). The master seed is stored as a decimal string. A new project's master seed is derived from its id unless given. The id never changes when the folder is moved or renamed. Settings: AMPLE Redux (budget 10 GiB, 90 days full detail, a year thinned), cleanup (no automatic age limit), mounts (filled by the library, chunk 85), default engine.
Format versions and upgrades
The current format is 1. Opening a project for writing upgrades an older format: the old project.json is first copied to .ample/backups/project.v<N>.<time>.json, then the upgrade steps run and the result replaces the file atomically. Opening read-only never upgrades (AMP5209). A project made by a newer AMPLE is refused (AMP5206). Fault tests prove that an error at any step leaves either the old or the new file complete, and the backup intact.
One writer
The writer holds an operating-system lock on .ample/locks/instance.lock and writes the heartbeat. A second writer is refused with AMP5204 that names the holder; read-only opening always works. A held lock is never broken automatically; taking over is a dangerous act that comes with the history work (chunks 50 and 77). ample doctor reports whether the lock is held or only an old heartbeat remains.
Atomic writes
Project files are written to a temporary file (*.ample-tmp) in the same folder, flushed to disk, renamed over the target, and the folder is flushed where the platform allows it. A crash leaves the old file, the new file, or a harmless leftover that the doctor reports (AMP5213).
Safe paths
All project paths go through ProjectPath: relative only, no .. escape (AMP5201), no invalid characters, trailing dot or space or Windows reserved name (AMP5202), and no symbolic link or junction that leads outside the project or a registered link (AMP5203).
Seeds are stored per block and locked by default (derived from the master seed); changing one needs an unlock first (AMP5260: it probably makes reproduction impossible), and every change is kept in the seed history. The freeze ladder is read from the block header: nothing (free), freeze seeds, freeze plan (the melody output of the selected take is passed back to the renderer as a pinned input and becomes part of the plan), or bake with a selected take (that exact take is used and nothing is rendered; if its data is missing the build says so with AMP5221 and does not render). Variants: ample variants makes several takes of one block, each with its own seed derived from the master seed; asking again gives the same ones; you choose one, keep others and discard the rest (a tombstone). Safe regeneration (ample regen, see Regeneration safety): the selected take is never touched; the new result is verified against it; identical means "same result"; different becomes an open decision, stored in the database, whose default is to restore (keep) the previous take, so a crash or a closed dialog leaves the old take in place. Replacing the take is a dangerous act. ample verify-reproduction re-renders the take's own plan only to check, and the result goes into the take's reproduction history. ample explain says what each kind of edit does at the block's freeze level.
Build (as built, chunk 55)
ample build <song> orders the blocks by their connections and, for each block, builds a plan from its source (type, name, class, parts, parameters and connections; not the bake, lock or refresh flags), the takes of its inputs, its derived seeds, the renderer and the settings. If a take for that plan exists it is used (a cache hit); otherwise the plan is stored first (the Always rule), the block is rendered and the take committed. Changing one block therefore re-renders it and what depends on it, and nothing else. --preview 5:4 renders four bars from bar 5 at a low sample rate (a separate plan from the full build); ample bake <song> <block> renders at full quality and adds bake to the block header; ample play writes a preview WAV. The built-in renderers (midi-clip, drums, bass, piano, mixer, master, group) use integer-only synthesis (fixed-point oscillators, tables computed with decimal arithmetic), so the same song gives the same bytes on every machine. Blocks without a renderer (AI voices and other plugin blocks) are skipped with AMP5251 until plugins exist.
History (as built, chunk 50)
ample history|checkpoint|tag|fork|go|peek|promote|drop|cleanup. The snapshot of a state is the set of source files (.amplet, .amplelrc, .lrc, .chordpro, .lyr, .chords), each stored once in the takebank; a node is immutable and stored in history.db. A new project has the lines studio (the real version) and garage (work happens here). Checkpoint snapshots only when something changed. Go first checkpoints unsaved work automatically (kind auto), so switching never loses anything, then restores the target's files through the command bus. Peek reads another state without changing anything. Promote shows the changes first (--yes does it) and replaces studio's head with garage's; the old state stays in the history. Tags are locked; moving or removing one, dropping a line (archive, recoverable) and cleanup are dangerous acts with dry run, typed acceptance, reason and tombstones. Cleanup only removes file contents that no active line and no tag can reach, and never content used by a take.
Command path and AMPLE Redux (as built, chunk 45)
Every state change is an ICommand run by the CommandBus: the pre-image (what is about to be overwritten) is captured, a start record is written to the journal and made durable, the change is applied, then an ok or failed record follows. If the journal cannot be written the command is not run (AMP5230). AMPLE Redux currently uses the compatible .ample/blackbox/ storage path and *.seg files (JSON lines, each with a CRC32C and the SHA-256 of the previous record, so a changed record is detected). A torn last line (a crash while writing) is dropped on open; a damaged record in the middle is never repaired away: the file stays as evidence and new records go to a new segment. A command with a start but no outcome record is interrupted (ample recover); the legacy ample blackbox restore <seq> command restores a file from a pre-image as a new command. A copy of the journal tail is kept in the user's home (%LOCALAPPDATA%/Ample/blackbox-mirror/<project id>/). Retention: full detail for 90 days, then hourly summaries for a year, within a 10 GiB budget (the legacy ample blackbox compact command; the newest segment is never thinned). The journal is not encrypted (optional encryption is chunk 92). Dangerous acts share one contract: dry run, backup suggestion, typed acceptance, reason, tombstone record (AMP5232).
Commands
ample new <folder> [--name N] [--seed N] creates a project. ample open <folder> attaches to or starts its local server; ample project open <folder> opens it for writing, prints a summary and releases the lock. ample project info <folder> prints the summary read-only. ample doctor <folder> checks structure, project file, lock state, leftovers, unknown entries and free disk space against the AMPLE Redux budget, and changes nothing.
Library (chunk 85)
The catalog is per user (library.db in the home folder), not per project. A project keeps ample.lock in its root (assets, packs and plugins pinned by hash), an assets/ folder for imported files (each with a .provenance.json), and the ids of its project links in project.json settings (mounts). A writable linked folder carries .ample-catalog.json. See the library manager.
Takebank and plans (as built)
Status (2026-10-02): implemented in chunk 42 (Windows verified; Linux runs when CI first runs).
The takebank stores everything AMPLE has ever rendered, immutably, together with the plan (the complete recipe) it was made from. It is what makes a song safe: the stored take is the guarantee, the seed is only a bonus (see Determinism and Regeneration safety).
The Always rule, enforced three times
API:Takebank.BeginAsync(plan) stores the plan first (a file and a database row) and returns a ticket; ticket.CommitAsync(...) is the only way to create a take, and a ticket works once.
Database: a take row needs a plan row. A trigger refuses a take without a stored plan even for someone who opens the database directly with foreign keys switched off (AMP5220). Plans, takes and their data references can never be updated or deleted (AMP5224); dropping a take is a tombstone flag, the record stays.
Files: the plan is also written as .ample/takes/plans/<hash>.plan.json and the take as .ample/takes/<xx>/<hash>.take.json, a plain copy that survives a damaged database.
Plans
A plan holds: project id, block uuid and path, hash of the block's source, inputs (data or take ids with toolprints), seeds, the rngVersion, plugin id and version, toolprint, settings, intent (preview, bake, variant, freeze, verify) and the parent plan (for regenerations). Its canonical JSON is frozen (fixed property order, seeds sorted by name, other objects sorted by key, no white space, non-ASCII escaped, schema tag ample-plan/1) and the plan id is sha256: plus the hash of that text. The time is not part of it, so the same recipe has the same id. spec-tests/plans/canonical-golden.json pins the canonical text and ids; changing the form needs a new schema tag.
Data and takes
Data blobs are content-addressed (sha256:<hex>, stored as .ample/data/<xx>/<hex>), streamed and hashed while written to a temporary file, then moved into place, so identical content is stored once even when several writers race. A take is its plan plus its data (role and id per blob); its id is the hash of those, so committing the same outputs for the same plan gives the same take. Every render attempt is recorded (started, completed, failed, cancelled, with the reason): a failed or cancelled render keeps its plan, and an attempt that stays started was interrupted.
Reading, verifying, orphans, sizes
ReadTake returns the take with its plan; blobs that are not on disk are listed as missing instead of failing, so the take can be re-rendered from its plan or restored from a backup (AMP5221 when the data itself is opened).
VerifyAsync rehashes the data and reports missing files, wrong sizes and bit rot.
Blobs that no take references (an interrupted put) are found and quarantined, never deleted.
Sizes are available per take and per block (distinct data counted once).
AI proposal builds may open the active project's takebank through Takebank.OpenReadOnly. Exact-plan audio is copied into the disposable proposal takebank; no plan, attempt, take, data row or blob is written to the source project. Existing project files that predate the setting use this cache by default; the AI setting can disable it.
Commands
ample takes list|show|verify [--project P] and ample plan show <take>; a take id can be given as a unique prefix.
Storage: the takebank, protection and clean-up
Status (2026-10-11): overview, protection rules, clean-up (UI, CLI, API), soft limit, options, size and time estimate, compressed takes and the data path on another drive are built. Finals are also built; see Finals. Remaining storage plan parts: plan/storage.
Every render is kept as a take in .ample/data, so a song never renders twice. Over time that adds up (a dubstep session of a day reached 22 GB). The Storage page (Projects, Storage) and ample storage show where the space goes and give it back safely.
What is never removed
One function decides (StorageService.Classify); nothing else does. A take is protected when it is:
chosen (the selected take of a block), a variant or part of a regeneration decision;
baked or pinned: its id is named in a song or module of the project, or in any history revision (so tags, checkpoints and releases keep their takes);
the current take of a block (the newest), and the takes it was made from;
a final;
younger than the keep window (default one hour), so a running build or an open preview is never touched.
Everything else is reclaimable: superseded takes (an older take of a block that has a newer one, beyond the "older takes to keep" option) and takes whose data is already missing. A removed take is tombstoned (its record and plan stay) and its data is deleted (or moved to the archive folder); when the song needs it again it simply renders again.
Never touched: the renders/ folder and every other file of yours.
Dry run, impact, progress
Studio: Projects, Storage: a bar (protected / can be given back), the reasons, the free space of the drive; "See what a clean-up would do…" shows the impact (takes and GB per reason and per block, free space before and after, what stays protected); "Clean up" runs it in the background with a progress bar and Cancel (it stops between items and leaves a consistent store).
API:GET /api/storage, POST /api/storage/plan, POST /api/storage/clean (the fingerprint of the dry run must still match), GET /api/storage/status, POST /api/storage/cancel, POST /api/storage/settings; progress also goes to the event stream (storage.progress, storage.done).
Temporary working files (finished AI tasks, .ample/tmp) older than a day are part of the dry run.
Soft limit
softLimitGb (default 10; 0 is off) never deletes anything: when the project is over it, or the drive has less than 5 GB free, the studio shows a hint with the real numbers and the CLI says so.
Options (.ample/storage.json, also in the Storage page)
Option
Default
Meaning
softLimitGb
10
size above which a clean-up is suggested
keepSuperseded
0
older takes of each block that stay besides the current one
keepMinutes
60
takes younger than this are never cleaned up
archivePath
none
a clean-up moves the data here instead of deleting it
dataPath
none
the takebank on another drive
intermediates
compressed
compressed or wav: how new WAV takes are stored
silenceRuns
true
configured in the file; the current renderer does not read this option (F2)
Smaller takes
New WAV takes (64 KB and up) are stored compressed: a lossless container (16 bit byte planes + Brotli). The decoded bytes are exactly the bytes that were rendered, so the content address, plan ids and take ids do not change, and silence and quiet passages shrink to almost nothing; a measured sample of a real project went from 303 MB to 89 MB (29 percent).
Takes made before this stay plain until you run Projects, Storage, Compress the existing takes (or ample storage compress): lossless, each file is decoded and rehashed before it replaces the plain one.
The option intermediates (compressed default, or wav) turns it off for new data. The same data is stored once whatever it is called (content addressing); the overview counts take rows that share data.
The takebank on another drive
ample storage move D:\ample-takes\song (or the Storage page) moves the take data there with progress and sets the dataPath option; ample storage move - moves it back. New data goes to the new place at once, reads look in both places while it moves, and a stopped move continues when run again. When the folder is not reachable (an unplugged drive) a write says so (AMP5603) and what is missing renders again.
Estimate and disk check before a build
GET /api/build/estimate?path= (and the first line of every full build in the build log) says how many blocks have to render, about how many GB they will store and how long it takes. At first this is an educated guess from the length of the song (a stereo WAV per block; light blocks fast, GM, plugin and AI blocks slow); after each finished build the per-type size and render pace are updated with an exponentially weighted average (alpha 0.3) in .ample/analysis/build-stats.json, so the next estimate says "from N earlier builds". The stats are per project, not per machine.
A build of more than 2 GB asks first. A build that does not fit on the drive of the takebank (the estimate plus 20 percent) is refused before anything is written (AMP5604) with what a clean-up could give back; small builds skip the check.
The build log reports the progress with an estimate of the time to go.
Errors
A full disk gives AMP5600 ("not enough free space on the drive of the takebank") and an unwritable store AMP5601, with the block named, instead of a bare I/O error.
Finals: several deliverables of one song
Status (2026-10-11): built (studio, API). A ample finals command is not built yet.
A song can deliver several finals: the full version, an edit, a teaser. Everyday building does not make them; Render does, on purpose, and says first how big and how long it will be (the estimate of Storage).
What a final is
name, the first bar, how many bars (0 keeps everything to the end), a fade in (beats), a fade out (bars) and the format (mp3 or wav). A song without any has one implicit final, full (the whole song). Finals are kept per song in .ample/finals.json.
The API accepts names of 1–60 ASCII letters, digits, spaces, - and _; fromBar is clamped to 1–100000, bars to 0–100000, and each fade to 0–64. wav selects WAV; other or omitted format values use the MP3 default. Listing returns each definition with its last rendered take id, path, time and byte size when available.
Rendering one
Projects, Finals (or POST /api/finals/render): the estimate and the disk check first (AMP5604 when it does not fit), then the build of the song, then the kept bars of the master with their fades, exported to renders/<song>-<final>.<format>. The master take is recorded with the final, so a clean-up never removes it. An unknown final is AMP5605.
API
GET /api/finals?song=, POST /api/finals (add or change), POST /api/finals/delete, POST /api/finals/render.
Local server and API
Status (2026-10-03): built in chunk 70. The web UI (chunks 75 to 77) is the first client; the reference of every route is generated from docs/api/openapi.json and appears under "Local API" in this help.
ample serve starts one local web server for one project. It serves the web UI, the help and the API, and it is the only way the UI touches the engine: everything the CLI can do is a route here, and every state change goes through the command bus, so AMPLE Redux keeps its recovery record.
The classic command prints the address and a launch address with the session token in the fragment (#token=...), which browsers never send to a server, and opens the browser by default. ample serve remains a foreground command. ample open attaches to or starts a server for a project; the former lock-and-summary operation is available as ample project open. GET /api/ping is an unauthenticated minimal liveness check used by the instance registry; POST /api/server/shutdown requires the session token.
Safety
Rule
Code
Listens on 127.0.0.1 only, on a free port unless --port is given.
The Host header must be localhost or 127.0.0.1 (protects against DNS rebinding); --allow-lan lifts this and is for a trusted network only.
AMP5282
A request with a foreign Origin is refused (no other site can drive the project).
AMP5283
Every /api call except GET /api/ping needs the session token in X-Ample-Token (a cookie works for GET only, for audio elements).
AMP5284
Optional sign-in (ample serve --login, off by default): set the user with ample auth set-password --user NAME (only a PBKDF2 hash is stored in the AMPLE user data folder, auth.json); GET /login is the page, POST /api/login{user,password} returns a 12-hour session token that works in X-Ample-Token next to the launch token. 5 wrong attempts lock the caller out for a minute (429). --allow-all (any Host, listens on every address, for a tunnel) is refused without --login. Over plain http the password travels in the clear: use an encrypted tunnel.
AMP5297, AMP5298, AMP5299
JSON request bodies default to 8 MiB; MIDI upload is capped at 64 MiB and audio transcription at 128 MiB plus multipart overhead. Responses are no-store and nosniff.
Every error is RFC 9457 problem details with the AMP code in code.
Documents
A song is edited as a working copy in the server. PUT /api/doc and POST /api/doc/edit carry the version the client saw (baseVersion); a stale version gets 409 (AMP5280) instead of overwriting. setParameter updates or adds a source parameter; removeParameter removes a simple parameter line when a file field is cleared. Nothing reaches the file until POST /api/doc/save, which writes through the command bus (AMPLE Redux keeps the old text).
AMPLE Redux
GET /api/redux/records is a read-only, paged journal search for project scope. Filter with ar, task, session, project-relative path, block, field, inclusive UTC from/to, or q. Pages default to 50 and cap at 200; keep the filters unchanged and pass nextCursor back as cursor to continue the same high-water snapshot. Payload and pre-image content are omitted by default; set includePayload=true with one ar reference when the recorded change itself is needed (maximum 2 MB). The search scans newest journal segments first and retains only one segment plus the bounded page in memory. Codex uses the task-authorized GET /api/ai/context/redux-search, which defaults to the current task; scope=session searches the current session and scope=project expands to project history.
Project files
GET /api/project/tree?path=&depth= lists the immediate children of a project folder and can include bounded child folders. GET /api/project/audio?path=... streams supported project audio with byte-range support for the shared player; it refuses non-audio files, private paths and linked files. GET /api/project/hash?path=... streams one project file through SHA-256 and returns its lowercase digest without entering .ample or following links. GET /api/project/peaks?path=... returns a cached, bounded 16-bit WAV waveform summary; GET /api/project/preview?path=... returns capped MIDI-note, song, lyric or file metadata for thumbnails. Both refuse private and linked paths and cap decoded file reads. GET /api/project/usage?path=... parses project songs and modules and reports blocks whose file parameters point at the selected file or a file under the selected folder. POST /api/project/upload?dir=...&name=...&onConflict=... accepts raw bytes or one multipart file, up to the server limit (512 MiB by default; configure ample serve --max-upload-mb <1..512>); multipart forms can provide dir, name and onConflict fields. Imports use the command bus; conflicts can be refused, renamed, overwritten with a recoverable pre-image, or checkpointed before overwrite. POST /api/project/folder creates a folder under an existing parent; POST /api/project/rename and /api/project/move move one file or folder to an unused path under an existing parent. DELETE /api/project/folder?path=... removes an empty folder through the journal; DELETE /api/project/file?path=... removes one project file through the journal. Each mutation publishes files.changed. Private .ample paths are not exposed by the tree, usage or mutation routes.
MIDI
POST /api/midi/upload?extension=.mid&track=all stores bounded SMF bytes under a content-addressed project path and returns its rendered bar count. POST /api/midi/module converts the file (and optional selected track) into an editable generated module. GET /api/midi/info?file=... returns aggregate track, PPQ, tempo and length data. GET /api/midi/download?file=... returns a new .mid by parsing and re-encoding the source event stream; POST /api/midi/module-download exports edited notes from the module's current working copy. Neither download writes to the project.
Audio transcription
GET /api/plugins lists analysis capabilities and declared resource needs. POST /api/audio/transcriptions accepts a WAV, local transcription plugin id, JSON options and an optional job id; network-dependent plugins are refused, GPU plugins require explicit consent, uploads are limited to 128 MiB/10 minutes, and aligned vocal/backing stems plus MIDI artifacts are bounded and validated before the result is marked complete. Pitch metadata is capped at 8 MiB. analysis.progress events carry the job id. GET /api/audio/transcriptions/{id} returns provenance and artifact URLs; GET /api/audio/transcriptions/{id}/{role} streams the source, a stem or MIDI artifact with range support; GET /api/audio/transcriptions/{id}/peaks?role=source&points=N returns the bounded 16-bit WAV waveform summary. Results are private project analysis data under .ample/transcriptions/, not takes.
Dangerous acts are two steps
Dropping a line, the history cleanup, retagging and replacing a take first return a dry run with a one-use token (ten minutes) and the text the user must type. Only POST /api/acts/confirm with the token, the typed text and a reason does it. A confirmation without an open dry run is refused (AMP5281).
Audio and waveforms
GET /api/data/{id} serves stored data with range support; WAV data is audio/wav, so an audio element can stream it. GET /api/takes/{id} carries audioDataId, the data of the take that is WAV audio (the role is the name of the output port, so it is found by content). GET /api/takes/{id}/peaks?points=N (16 to 4000, default 800) returns { rate, channels, seconds, points, rms, peak, peaks } where peaks is points min and max pairs ([min0, max0, min1, max1, ...], values from -1 to 1) over the whole take; it is computed in one streaming pass (a ten-minute take is never held in memory) and cached. Only 16-bit WAV can be summarised.
POST /api/project/upload?dir=...&name=...&onConflict=... imports supported files through the command bus (512 MiB default maximum, adjustable with ample serve --max-upload-mb <1..512>); conflict choices are refuse (default), rename, overwrite and overwrite-record. GET /api/project/tree?path=...&depth=... lists a project folder and immediate children without following links or entering .ample. POST /api/effects/preview accepts a song path, effect block, its selected upstream take, parameters and a range of at most 30 seconds; it returns WAV from the built-in integer renderer without recording a take or journal entry. Preview output is kept only in a bounded process-local cache.
History and control views
GET /api/history/graph (every revision of every line with its parent, the lines, tags and current line; each node's reduxRef is the compact AR reference of its exact durable history command, or null), GET /api/history/diff?from&to, GET /api/history/peek?ref (the files of a state as text; changes nothing), POST /api/doc/parse (parses text that is not open, for a state that is only looked at) and GET /api/control/block?path&block (freeze level, seeds with their history, selected take, variants, supported and unsupported variant fields, the explainer, open regeneration decisions). POST /api/control/variants/preview returns deterministic per-take field changes without rendering; POST /api/control/variants accepts selected field names and renders temporary values into immutable take plans. count may be 1–20, optional startIndex selects a deterministic segment of the same proposal sequence, and optional baseVersion prevents a multi-request batch from continuing after its song changes. The Studio requests one take at a time and aborts the active request on cancellation. Seeds are 64-bit numbers and travel as strings.
GET /api/redux/records provides bounded, read-only journal search with cursor paging; GET /api/redux/status returns aggregate storage and budget status; GET /api/redux/compact-preview previews exact compaction impact; POST /api/redux/compact performs the separately confirmed compaction.
Library
GET /api/library (linked folders with their live state, assets, duplicates), POST /api/library/mounts (link and scan), /scan, /relocate, /access (read-only or read-write), /import (copy into the project), GET /api/library/uses?path (where the song uses assets), GET /api/lock and POST /api/lock/update, and the two-step act /api/acts/unlink-mount. The home folder of the library is ServerOptions.LibraryHome (default: AMPLE_HOME, else the local application data of the user).
Storage and build estimates
GET /api/storage returns takebank usage, protection/reclaimable totals, storage options and cleanup state. POST /api/storage/plan creates a dry-run plan; POST /api/storage/clean starts it only if its fingerprint still matches. GET /api/storage/status and POST /api/storage/cancel inspect or cancel cleanup. POST /api/storage/compress, /api/storage/move and /api/storage/settings manage lossless compression, takebank relocation and project storage options. Cleanup and compression progress use storage.progress and storage.done events.
GET /api/build/estimate?path=... performs a plan-only estimate for the song and reports rendered block count, estimated bytes/time and whether the takebank drive has room. It does not render or store data. Builds under 50 MiB skip the space refusal; other estimates require 120% of estimated bytes free. Build statistics are learned per project in .ample/analysis/build-stats.json.
Finals
GET /api/finals?song=... lists defined finals and the implicit full final when none are defined. POST /api/finals adds or updates a definition; POST /api/finals/delete removes one; POST /api/finals/render deliberately builds, cuts and exports one final after estimate and disk preflight. Finals are project metadata, stored in .ample/finals.json, and are not song-language syntax.
Assistant context
The /api/ai/context/* routes provide scoped read-only context (project, song, block, files, history, docs and analysis), structured proposal edits, score import, task memory and project-log access. GET /api/ai/context/access requests permission to write outside the project; POST /api/ai/context/write-outside and /api/ai/context/findings are the mediated write paths. These routes are task-context scoped and do not replace the proposal/accept flow under /api/ai/tasks.
Backup
GET /api/backup (what the project remembers and the backups in that target), POST /api/backup/create, /verify, /policy (target and backup before every dangerous act), POST /api/export (archive file), and the two-step acts /api/acts/prune-backups and /api/acts/restore-in-place. GET /api/health carries the last backup.
Events
GET /api/events is a server-sent event stream: build.progress, build.done, doc.changed, history.changed.
Not yet
The library routes are a placeholder until chunk 85. The API is version 0.1 and may still change until the UI has used it.
The web UI (as built)
Status (2026-10-05): shell, canvas, ports and links, palette, groups and block properties (chunk 75); history, branches, compare, variants, seeds, the freeze ladder, playback and the arrangement (chunk 76) are built. The editors and managers come with chunk 77. The Studio UI track adds the advanced dark Studio skin and controls while preserving the current app's complete feature inventory.
Opening a song resets the Source editor's file binding and reloads it when visible; a hidden Source editor follows the active song when reopened. Song switching asks before discarding unapplied source text. The editor file picker and project browser share this guard (2026-10-10; browser acceptance pending). The last opened song is kept in the user's recent-project record, so F5 and a server restart reopen it even if the port changes. If that song was removed, Studio opens the first available song.
Block playback shows a pending loading indicator; click again or use Stop to cancel. Only the latest playback request may start after asynchronous loading/output initialization. Audio metadata loading times out after 15 seconds. The browser retains its token in tab session storage across F5. Media/event cookies are named per server port and token generation, so an old tab cannot overwrite a newer tab's cookie after restart. A genuinely stale session token still requires opening the new launch tab; authentication is not weakened (2026-10-10; runtime/browser acceptance pending).
The Assistant bottom panel uses its available height: heading/actions/search stay fixed and only the conversation list scrolls. In the AI task dialog, excess wheel movement at the Activity log or sample-list boundary scrolls the outer content. Its message composer places attachments above a full-width field, grows to three lines and then scrolls; Enter inserts a line break and Ctrl+Enter or Alt+Enter sends. When reviewing a completed proposal, Codex's written summary appears beneath the fixed tab row in Output, Changes and Checks; newest presented audio samples appear first (2026-10-16).
Folder tree expansion uses an open/closed folder icon in place of the arrow. The size slider changes between details and tiles directly, without a resistance zone. Audio waveforms carry an invisible accessible range input for direct mouse/touch scrubbing and keyboard arrow seeking. Playing detail rows expand their name cell to show a waveform and collapse on Stop or natural end. Tile width presets start at 240px and reach 540px; the default is 360px, constrained by available panel width. WAV peaks are supported; other audio formats retain the placeholder when server peaks are unavailable. These follow-up changes are source/syntax checked; browser/audio acceptance is pending (2026-10-10).
Project browser tiles are wide cards suited to audio names. Folder and file-type SVG icons appear in the tree, detailed list, tiles and picker. Audio tiles lazily load waveform peaks, with a patterned placeholder when unavailable. Hover or keyboard focus exposes Play/Stop, Loop and a seek slider; touch devices always show them. Preview uses the shared player and local playback volume, starts only on Play, and stops on card leave, folder/view/filter changes, bottom-tab navigation, dragging, page hiding or window blur. Scrubbing keeps playback alive during the pointer drag. Browser/audio acceptance remains pending (2026-10-10).
Block status and local listening volume (2026-10-10)
The right title-bar dot is now a status control. Red ! identifies a source/build error; amber ! identifies a block skipped by an upstream error; blue pulses while building; filled green means newly rendered in the last build involving that block; outlined green means an existing take was reused. Grey means not built/out of date, with a dash when disabled. Status tooltips include the available error or build time. Clicking an error opens Log, selects Song log or Build log, follows upstream failure messages and scrolls/focuses the corresponding error row. Completed build/cache badges no longer occupy the block body; current step and detailed progress remain visible during a build, then disappear for each block as its result arrives. Semantic flags such as dynamic/baked remain separate.
Ordinary Play/Stop actions use icons; Build and play/Cancel build retain their explanatory labels. The small arrow beside Play opens Loop, whose toggle keeps the menu open. The volume icon opens a vertical 0–150% fader with a reset to 100%; moving away from baseline gives the icon an amber tint and a tooltip explicitly distinguishing listening volume from master volume. It is held in this tab's memory, resets on reload, and is never written into song settings, output files or render plans. Shared playback, proposal audio, loop playback, file/clip previews, A/B, transcription stems, recording auditions and live MIDI previews use the local gain. Shared source meters remain before this gain. Values above 100% amplify playback and can clip at the device; they do not modify the rendered waveform.
The default studio-dark theme is token-driven by web/styles/tokens.css. Shared element treatment lives in skin.css; Studio panels build on studio.css. Available themes are studio-dark, light, high-contrast, auto and classic; the selection is remembered in the browser. Custom graph and waveform drawing reads the same CSS tokens.
ample serve serves the web app at /. It looks like the clickable sketch on purpose: the sketch's CSS is the app's CSS, and the same regions, buttons and behaviours are used. What is real here that was a mock there: every change goes to the server as an edit of the song text, so the graph and the source are always the same document, and the history, the takes, the seeds and the audio are the real ones.
What works
Project exports (2026-10-10)
Right-click a project file and choose Export to download its original saved bytes. Audio files additionally offer Export as → WAV / MP3 / FLAC. Folder menus omit ordinary Export and expose only Export as → ZIP. ZIP includes nested files and empty directories without following links or entering private .ample directories. Project sources are never overwritten.
Converted and ZIP exports run as temporary jobs. The progress dialog shows the current step, elapsed time, and either compressed source bytes/files or converted audio seconds/output bytes. ZIP has a byte-based progress bar after file discovery; audio conversion uses an indeterminate bar with live timing/size updates rather than guessing a percentage. Cancel export stops compression or terminates only the job's own ffmpeg process and discards its temporary output. A completed job starts a browser download and offers Download again for 30 minutes. Conversion requires ffmpeg on PATH or AMPLE_FFMPEG; a missing encoder is reported in the dialog. WAV conversion uses 24-bit PCM; FLAC is lossless, MP3 uses ffmpeg's MP3 encoder. Export reads the saved project file, not unsaved source-editor text.
The source editor supports Ctrl+mouse-wheel font scaling from 8 to 24 px; the browser page does not zoom while the pointer is over the editor, and aligned gutters and diagnostic overlays follow the same size. Ctrl+Space and inline completion offer sequencer channel keys such as pan, filter project audio samples by the text typed after sample=, and complete project file or sample-folder parameters by substring within the file types accepted by their block. GM SoundFont fields also complete pack: with the MuseScore General alias.
Area
Behaviour
Layout
Library left, canvas, properties right, tabbed panel below. Each panel can be hidden; the bottom panel and the side panels resize; the layout is remembered in the browser.
Palette
Generated from spec/templates (GET /api/templates): categories, class and version badges, search, drag onto the canvas or double-click, right-click for template info.
Canvas
Wheel zoom about the pointer, background drag pans, Fit, 1:1, minimap (click or drag to move the view), click, Ctrl or Shift click and Shift-drag rectangle select, Ctrl+A. Blocks snap to a 10 unit grid; cards start at a readable width and expand up to 680 px to fit longer titles and port labels. Automatic columns size themselves to their widest card, with node-height-based row spacing. Opening a song automatically runs Fit after its graph is loaded and the canvas is visible, so all blocks start in view. Fit explicitly shows the entire graph. Double-click a block title to rename it inline; collisions receive the next free suffix. The arrow beside type and version opens that block's Studio GUI, or selects its properties when it has no dedicated GUI. Audio sources list connected effects in signal order, including disabled effects shown dimly; choosing one focuses its block and properties. The right-click menu groups Add, Build & export, Layout, Edit, block-specific tools, AI and Remove; pinning and Auto format are under Layout, as is Fit in the canvas menu. Add divides effects into EQ & filters, Dynamics, Space, Modulation, Levels & stereo, Timing & transforms and Other submenus; effect bypass is under Edit. The source icon remains at the toolbar's right edge. A dim pin beside the block name becomes bright when pinned. The source block's Properties Advanced dialog can add, remove, reorder or bypass effects while preserving the downstream route as one undoable graph edit. Keyboard: Tab between blocks, arrows move, Enter shows the source, Delete, Ctrl+Z/Y/A/G.
Ports and links
Drag from an output dot. Compatible inputs light up and the others dim while dragging; an incompatible drop shows a message and changes nothing. Connect and disconnect are edits (from lines).
Moving
Moving blocks writes layout name x y lines. Blocks that never moved are placed by an automatic layout.
Groups
Select blocks, Ctrl+G; links that cross the border become the group's ports; double-click opens it, Ctrl+Shift+G ungroups.
Project files
File parameters have an editable project path, picker, Open and Clear actions. Open routes audio into shared playback, MIDI into bounded details and its linked block, lyrics into the linked lyrics editor, text into Source, and unknown types to their Project location. Clearing removes the parameter line in one versioned edit. Project audio streams with range requests.
MIDI modules
Project MIDI files can be imported as MIDI blocks, inspected using bounded metadata and converted in place or into a new song through a generated module. Generated clips, instruments and mixers remain editable in the nested node view; per-track event nodes expose every retained event in paged, filterable properties with bounded payload previews, while the banner flags only remaining source-level features. General MIDI instrument properties can preview a test note or up to 128 routed track notes, including the all-channel setting. A/B comparison reports note/timing/velocity differences and switches audio at the matching relative position.
Audio transcription review
Choose a local analysis plugin and WAV, then review source/vocal/backing stems on a shared scrub timeline and inspect the plugin's confidence-bearing vocal pitch lane. Correct the derived note estimates, download them as MIDI or import them as a MIDI block. Results stay in the project's private .ample/transcriptions/ folder. No separation model is bundled.
Properties
The block heading has a source button beside its title and the Advanced GUI button at the far right when available. Rename, template parameters (showing template defaults until overridden in the source), lock, bake, refresh and disabled state, ports, notes. The Info tab repeats the template's read-only description as a tip. Long text uses the template-declared expanded editor. Several blocks: group, align, distribute, delete. For a dynamic block also the seeds and the controls (below).
Build and players
The Build button builds the song; each block shows built, cache hit, failed or skipped. The Build log lists new entries first with timestamps and elapsed time, retains entries for the session, and can be cleared or exported as .txt. Audio blocks get a ▶ with a progress bar after a build.
History
The History tab has Project history (every revision of every line, graph or newest-first list, tags, details, checkpoint, fork, promote and cleanup) and Edit history (undo / redo) (the open song or nested module's undo states, current working state and redo states). Select a step to preview it read-only, return to the live buffer unchanged, or restore it as one new undoable edit. Double-click asks before restoring; Ctrl-double-click restores directly. A compact AR reference appears only when the exact file text hash matches a durable saved pre-image in AMPLE Redux.
Look only
"Look only" reads another state without changing anything on the server: the song of that state is shown read-only under a banner; building, saving and editing are off. Leaving restores the exact local working document and view, including unsaved edits.
Branches
The Branches tab lays one line out left to right: the path from the root line to the focused one, its direct leaves, dots for revisions. Right-clicking a branch card offers go, look, compare, fork, tag, fold, and drop or restore as applicable. The Structure tab lists the lines and tags plus a searchable block list (name, type or class).
Compare
Compare... opens a wide, scrollable diff of two states (lines, tags, revisions), with both revision selectors visible even for long titles, expandable files, added and removed lines, line numbers and surrounding context. Opening an older revision from History keeps it selected even outside the twenty recent choices. A final newline is a line terminator, not an extra blank line; missing final newlines are marked. In the variants view, two takes can be compared A/B with synchronised playback and a loudness-matched switch.
Variants
Choose output-affecting numeric, fixed-choice and seed fields, preview each deterministic before/after value, then make 2, 4, 8 or 20 takes. Each card shows the fields recorded in its immutable render plan, waveform, ▶, your score (0-100) and flag, keep, discard and pick. Temporary field values never edit the song source.
Seeds
Locked by default. Unlocking says what it breaks at the current freeze level; typing a seed or randomizing is journaled and kept in the seed history. Seeds are 64-bit numbers and are shown exactly.
Controls
The freeze ladder (free, seeds, melody plan, take) writes freeze and bake into the source; the table lists what each kind of change does at that level (from the server's rules); Regenerate is safe: warning first, the old take stays, and a different result asks what to do (default: restore the previous take).
Playback
A ▶ on every built audio block, each block's cached waveform with click/drag seeking, the built song waveform, a loop switch and one lit global stop control beside Preview song. Preview song builds what is missing and plays the song; dragging over the ruler chooses bars and previews just those. Audio streams with range requests; waveforms use GET /api/takes/{id}/peaks.
Arrangement
Lanes generated from the blocks and their parts (at N for M as name), ruler in bars and seconds, zoom; the labels stay fixed at the left while the timeline scrolls horizontally, and the waveform and ruler keep their own rows when scrolling vertically. The bottom panel batches height changes while its grip is dragged. Drag clips to move or resize, double-click or right-click to set exact timing, and trim a configured bars scope when shortening. Edits use a versioned source operation and one undo step.
Assistant and log
The bottom Assistant tab manages searchable, compact project-local Codex sessions, opens the latest request, and folds earlier requests into each session; threads resume after restart, and selected checked changes can be accepted while continuing the same conversation. The full existing prompt and proposal interface is shared with other entry points; Source remains in the top view switch. The Log panel separates Build log, Project log and Song log; Song log lists current compiler errors and warnings, removes them when resolved, and opens their source location on double-click.
Undo
Ctrl+Z and Ctrl+Y step through edit operations; one gesture is one step. The Edit history view lays out undo states, the current work and redo states in order, with read-only preview, return-to-current and explicit restore. Redux badges are looked up by hash across the file's paged durable journal records; transient states remain unbadged.
Errors
A song with errors is read-only (banner); current compiler errors and warnings are listed in Song log, and double-click opens the source location.
Effect properties and numeric input
Numeric fields keep typing and arrow-key editing; focusing a ranged field opens a small floating slider that closes on blur or Escape. Block properties use remembered, keyboard-navigable tabs. The six audio effects have compact cards with bypass, their existing template parameters and visual summaries; ▶ builds that block and plays its resulting take.
The Studio Advanced dialog uses the same journaled edit path. Its buttons in the canvas and block properties both recognize connected audio: they offer to build the upstream input until a take is available, then allow playback. EQ and filter show estimated frequency-response plots; the compressor transfer curve follows the integer engine's steady-state linear-amplitude law, while bounded waveform and gain-reduction analysis comes from its exact render; duck edits a 16-step pattern; distortion, delay and reverb show parameter-driven plots; mixer has four gain and pan strips. Other plots are guidance, not measurements of the integer engine. With a built upstream audio take, Local preview decodes a bounded 30-second bypass render (fetched and decoded once per take, then reused across control edits) and applies Web Audio graphs for EQ, filter, compressor, duck, distortion, delay, reverb, chorus and convolution; it is labeled approximate and updates after control edits. Chorus previews a triangle-LFO-modulated delay line (fixed 20 ms base delay, depth/period/mix matching the template) so the effect is audible, not just the dry input. Convolution uses the selected project or pack impulse response, with live mix, level, predelay, length and normalization; changing the IR replaces only the impulse response while the input and playback position continue. Hear the real thing uses the renderer for the exact effect preview. The server preview is bounded and cached in process memory, and never creates a take or changes project history. A playback analyser drives the Studio output meter when Web Audio is available; the shared waveform scrubber supports seeking and loop regions.
The denoise panel's Preview runs a bounded spectral denoise approximation in a browser worker, so audio playback stays responsive without waiting for a server render. It uses the selected noise profile and current reduction/sensitivity controls; Compare / render still offers the exact AMPLE effect preview. The bypass audio for the worker is fetched once per take and reused for every slider change (it never depends on this block's own parameters), and the preview position is read again right before the filtered audio swaps in, so a control edit no longer re-fetches over the network or jumps the playback position backward. The panel's Optimize: auto / speed / memory group is shown apart from the sound controls: it is an execution-policy parameter (affectsOutput: false in the template), so every choice renders the exact same cleaned audio and only trades the server render's time against its memory use on a long sound.
The Project bottom panel shows the project tree/list with search, filters and view choices. The view mode, tile size, sort and detail-column layout are saved in the shared project settings; the current folder and tree width remain remembered per project in this browser. The detailed list includes lazy Used by and SHA-256 columns. Tile cards and the picker show lazy WAV waveforms, MIDI note-roll summaries, song tempo/block counts and lyric excerpts using bounded cached project-preview routes. Project and picker OS file drops share the duplicate dialog, including Keep both and Apply to all. The file picker reuses the project tree, reveals nested preselected paths, shows file references, accepts uploads with per-file byte progress and offers all four conflict choices with Apply to all, creates folders, and has context actions for rename/move and deletion (folders must be empty). Lists above 2000 entries render a fixed-height virtual window. Tree rows support arrow-key navigation, Left/Right expansion, double-click expansion, / search and Backspace to move up; file fields can clear their path and sampleset fields select folders. Internal project-file drags carry relative paths; OS file drops use the upload flow.
Not yet (and where it comes)
The Studio UI track is tracked in the build plan; detailed chunk files are maintained alongside the owner's worktree plan.
For new plugins/views and a quick check on whether existing AMPLE work needs to migrate, see Extending the Studio UI. The complete, interactive AMPLE app mockup is maintained in the Studio plan.
The full mockup must preserve every capability in the real AMPLE UI, even when the redesign moves actions into compact controls; maintain the current-app parity inventory and mockup findings in the Studio UI plan's mockup/NOTES.md.
The source editor, lyrics editor, plugin settings, library, projects, branch manager, export and the settings in the properties panel: chunks 77 and 85. The language server in the editor: 80.
Planned (2026-10-04): studio effect panels with preview, a file picker for every file field and a Unity-like Project panel; see plan/studio-ui/.
Planned (2026-10-04): a launcher track (instance registry, ample open, a tray hub and project picker for Windows, a user-level asset home); see plan/launcher/.
Honest differences from the sketch
Groups live in the view, not in the song file. The language has no inner graphs yet, so a group is stored in this browser per project. The source is never changed by grouping.
Notes, scores and flags are also kept in the browser.
There is no cache probe before a build, so blocks show "not built" until a build has run (the build itself uses the cache).
Undo restores the song text before the gesture; it does not undo view changes.
"Controls" shows the rules of the server (what is ignored, what breaks reproduction) instead of the sketch's expected melody/words/sound table, which was an expectation and not a measurement.
Playback uses an audio element streaming from the server, not Web Audio: it never holds a whole take in memory, but it is not sample-accurate (the A/B players stay within 0.1 s of each other in the tests).
Arrangement gestures edit the source placement; the text remains authoritative. Other waveform/clip-content editing remains in the source and the relevant block editor.
History: going to a revision that is not the tip of a line forks a line there first (the server only moves between lines).
The history graph has no named checkpoints separate from revisions: a checkpoint is a revision of kind "checkpoint" with your name as its message.
Files: the project panel, drag and drop, the switcher and the save menu (2026-10-08)
The project panel is the home of file management. Right-click on a folder, on an empty spot or on the + button: Add › creates a song, timed lyrics (lyrics/*.lrc), chords (chords/*.chordpro), an empty MIDI clip (assets/midi/*.mid), a module, a sample folder, a text note or a folder, each with a valid skeleton, through the same code as ample add <kind> <name>; a new file opens in the editor that fits. Right-click on a file: Use in song › adds a block that reads it (the block types come from the accept lists of the templates), Point a block at it › changes the file parameter of a block that exists, Open as text, Show where used, Rename, Move, Delete.
The Project tab starts at the selected project root, with its files listed. Folder rows in the tree can be dragged into another folder; the move uses the same reference preview as the Move command and refuses moves into the source folder or its descendants.
In the file view, files are always listed before folders. The active sort column and direction are applied separately within each of those two groups, in tile and detail views.
With no block selected, the properties panel shows Song properties. Its Seed override is blank when the song follows the project master seed; entering a seed overrides it for that song, and clearing the field restores the project seed.
Rename and move are a refactor. The panel first lists every line of the songs and modules that names the path (also inside a voice map) and what it becomes; Move applies it, one journaled step. A song with unsaved changes stops the refactor until it is saved or reverted. Advanced › has Rename without refactor and Move without refactor: only the file moves and references may break. On the command line: ample mv <from> <to> [--no-refactor] [--dry-run].
Drag and drop. A file dragged from the panel (or from the operating system, which uploads it into assets/<kind> first) lights up where it can go: in the source editor the lines that take it (outlined) and the places between blocks (a line), the nearest one marked; a drop fills the path (a different existing path asks first, Alt replaces) or adds a block; one Ctrl+Z takes it back. Insert file… in the editor bar does the same from the keyboard. File fields in the properties and the studio panels light up and take the drop; a node that reads that kind of file takes it on its file parameter; an empty canvas makes a block. The targets come from the accept lists of the template parameters and from web/lib/file-kinds.mjs (extensions to kinds); the edit a drop makes is web/lib/drop-targets.mjs.
The file switcher. The list of files next to the source editor is hidden by default (the Files button brings it back). The Recent ▾ dropdown is visible beside the project name at the top-left of the canvas and in the Source header. It shows the current song and source file under Open now, then recently opened songs and editable text files; a dot marks a file with unsaved or recoverable changes. The full-screen recovery diff lists pending source drafts and song/module undo buffers. For each other buffer, choose to restore it when opened, use its saved version or decide later. Choosing a recent file uses the normal switch flow, including its unsaved-change or recovery prompt. The quick switcher above the text still supports Ctrl+P or click, typing a few letters, arrows and Enter; with no query, recent files come first.
Moving and hidden names. Drag a file or folder onto a folder (a tile, a row or a folder in the tree) to move it into it, or onto the path at the top of the panel to move it out of the folder you are in. It asks first, listing the lines in songs and modules that follow the move (the same as Move…). Names that start with a dot, such as .git, are left out of the panel, the tree and the file picker unless the Hidden chip is on. New folders come from the + menu (New folder…) and from Add → Folder.
Uploading audio. An uploaded MP3, Ogg or FLAC file also gets a WAV next to it (song.mp3 gives song.wav; a name that exists gets -2), made by ffmpeg at the same sample rate as 16-bit PCM, because blocks play WAV files. A toast names the new file. FLAC is lossless; an MP3 or Ogg decodes to exactly what it sounds like, which means nothing is lost on top, but nothing that the encoder dropped comes back. Without ffmpeg the upload still works and the toast says the conversion was skipped (AMPLE_FFMPEG sets the path).
Changes and saving. The same control sits in the source editor, the lyrics editor, the piano roll and (as the menu only) next to Save at the top: a dot says typing, not applied (text in the source editor that is not applied), changed (the working copy differs from the saved file) or nothing (saved), the disk icon saves (Ctrl+S works everywhere; the editor applies what was typed first) and ▾ opens Save as… (a copy as a new song), Revert changes, Export…, Render block to file… (the take of the selected block as WAV or MP3 in a project folder, renders/ by default), Show in the project panel and Copy path. The Source file list and quick switcher include only songs and editable text files, not audio assets; following an audio-file link selects the block that uses it instead.
Patterns and sections (2026-10-08)
The step grid of a drums or guitar block has a pattern chooser (the main pattern, the named ones, New, Duplicate, Rename, Delete) and a section strip under it: one chip per section with its pattern, its bars (− and +), arrows to move it, a cross to remove it, and drag and drop to reorder; + section adds one. Everything writes the patterns and song parameters of the block (see Samples). The Patterns view (the Patterns view button in the panel, or the block menu) opens the same grid, chooser and strip full width, with the voice map below it for the samples of the lanes; an audio file dragged from the project panel onto a lane gives that voice the sample. The FL Studio-like workspace is the advanced sequencer block: Sequencer.
Build, play and stop (2026-10-08)
The header has three controls. Build builds what is out of date (also unsaved changes; blocks that did not change are not rendered again) and does not play. Play plays the built song; when something is out of date, or nothing is built yet, it says Build and play, lists what is out of date in its tooltip, builds first and then plays. Stop is one button: it cancels a build that runs (the blocks that were ready are kept; press again to stop audio that plays), otherwise it stops the audio. Loop repeats the audio, and when bars are chosen on the arrangement ruler a Play bars a-b button plays only those. The line next to Stop says what plays (the song, a block, a project file). A disabled button says why in its tooltip (errors in the song, a read-only state, a build that runs).
Keys: Space plays or stops (not while typing or in a dialog), Ctrl+B builds (Ctrl+Enter applies the source editor). Blocks that failed in the last build show a red badge (2 failed) that opens the build log, and the log ends the build with a line that names them. The audio of the song is downloaded from the file menu (Download the song audio, WAV or MP3). A block whose take is out of date shows no player: its take is removed as soon as the block changes.
Build errors in the code (2026-10-08)
A line of the build log that names a failed block (sq: failed: AMP5380: ...) is a link: click it and the Source view opens with that line in view. The line is underlined in red (and has a red dot in the gutter). The line is the parameter the message names (a missing file points at the file line), otherwise the header of the block. The same log line has a Show block button that goes to the block in the node editor instead (selected and in view). Hover the line for a tooltip with the code, the block and the full message in selectable text, with a Copy message button; move the pointer onto the tooltip to select. When you change that line the mark goes away; the next build puts it back if the problem is still there (a new build also clears every mark first). The same red line shows in the Source view when you open it later, for as long as the line is as it was when the build ran. Model: web/lib/build-errors.mjs.
The sequencer workspace (2026-10-08)
A sequencer block has a full-screen workspace (the Open the sequencer button, or the block menu): the channel rack, pattern tabs with step grids and the piano roll, the playlist with clips and sections, and the lanes strip. See Sequencer.
Running the tests
dotnet build ample/Ample.sln
cd ample && npm install && npx playwright install chromium
npx playwright test --project=chromium # spec-tests/parity/*.spec.ts against a real ample serve
node --test web/lib/test # geometry, graph, history logic
Specs that change history, seeds or takes start a server of their own (freshServer() in web/tests/support.ts), so what other specs did before cannot matter.
Editors and managers (chunk 77, as built)
Views: Blocks, Source, Library and Branch manager share the canvas area (web/app/views.js). The view switch stays above every view. A project that opens with errors starts in the source editor; the node editor is read-only (banner) until they are fixed.
Source editor (source.js, lsp.js): a locally bundled CodeMirror 6 editor with AMPLE syntax highlighting, a gutter and diagnostic/build squiggles, driven by the real language server over /lsp (live diagnostics, Ctrl+Space completion, Ctrl+click definition, rename, format, fix all). Valid song text syncs to the working copy after a short pause; the Properties panel follows the block under the text cursor and edits either way update the other view. Properties are hidden with an explanation while the source has errors. Save makes a revision; Ctrl+Enter confirms the current buffer. Text files the song refers to (lyrics, chords, notes) and the generated ample.lock (read-only) open in the same editor.
Unapplied edits in source files are kept as separate browser recovery drafts. Switching files can preserve a draft; after reload, opening that file offers its timestamp and size comparison, a full-window saved/recovered view, and explicit restore or continue-saved choices. Drafts stay in this browser and project; applying or reverting the file removes its draft.
Lyrics editor (lyrics.js, lyrics-model.js): timed [bar N] text lines with live checks (empty line, order, long line), sort on apply, import and export of .lrc.
Plugin settings (pluginui.js): generated from the manifest's schema, validated before it is stored in .ample/plugin-settings.json.
Library manager and mounts (libraryui.js, side panel "Folders and links"): linked folders, assets, details, where used, ample.lock line, copy into project. Offline mounts are marked and never become write targets.
Projects (projects.js): recent list with a health summary, new project, settings (black box budget, retention, default engine, master seed through the dangerous act), health (findings, black box chain, interrupted commands to restore or accept, backup age), backups, export and cleanup.
Dangerous acts use one dialog (dangerous.js): dry run, impact, backup suggestion with "Make a backup now", typed words and a reason; the confirm call only works with the token of the dry run (tested through the API too).
Not built
No live lyrics preview against a take, no branch rename, no replace-with-impact in the library manager, "Open project" starts a second server instead of switching.
Number fields (properties panel)
Numeric parameters are typed fields with the browser's small up and down arrows (also ArrowUp and ArrowDown in the field); a value is committed on Enter, on leaving the field or on an arrow click, and goes through the same edit path as every other parameter. A parameter whose template says number, and the known ranged ones (swing 0 to 60, humanize 0 to 100, stagger 0 to 80, gain -24 to 12, steps, bars), get this field; a value that is not a number (like derived for a seed) stays a text field. A value outside the range is moved into it with a message, and an empty field goes back to the last value. Focusing a field with a range shows a floating slider for that same value; typing remains available. The pure part (ranges, reading what was typed) is web/lib/numfield-model.js.
Multiline text parameters open in a wide responsive dialog with a tall, vertically resizable editor. Soft wrapping keeps long lines readable while preserving explicit line breaks when applied.
Progress of a block while it builds
While a build runs, a block that is busy keeps its size: its status tag names the step that is busy ("synthesis 13%"), a thin bar along its bottom edge shows the whole block, and while the pointer is on the block a panel opens below it (animated, floating over what is below) with every sub-step and its own 0 to 100 % (a step that is busy without a known end is striped). Blocks that wait for others say "waiting". A block shows its result as soon as it finishes, even when another block at the same level is still rendering; the toolbar counts results from the current build only. The build-log time estimate counts newly rendered blocks, not cache hits. The ACE-Step plugin reports plan, synthesis and isolation; ace-server only says running or done, so those bars move by an estimate and jump to the end when the step is done. The Progress button next to Map turns the bars off (remembered in this browser). A plugin reports progress with ctx.progress(fraction, step, stepFraction); the engine passes it on as the event build.block (block, state, fraction, steps), at most ten times a second.
Ctrl+click in the source editor
Hold Ctrl in the source editor and everything that can be clicked is underlined: the names of blocks (in a block header and in a from line) and the paths of project files (for example the value of file). Ctrl+click on a block name shows the block in the node editor; Ctrl+click on a file opens that file in the source editor (a song opens as the song). A file without source, such as audio, selects the block that uses it.
Sample set hints (2026-10-03, chunk S12 part 1)
Below the sample kit or sample set chooser the properties panel shows what the chosen set has: "N layers on disk, engine plays M" (and the same for round robins), a "what you are missing" line (layers left on the source according to set.json, files the engine does not play yet) and a collapsible region table (voice, layers, round robins, what plays, loop). The text comes from web/lib/setinfo.js (tested in web/lib/test/setinfo.test.mjs) and the numbers from GET /api/samples. Sets without extra files show nothing new. Velocity, choke and gain fields follow with the engine chunks.
Piano roll guide
Covers web/app/pianoroll.js and PR01-PR22 of plan/piano-roll/README.md and plan/piano-roll/SPEC.md. The text stays the truth: every
gesture below is one ordinary edit of the clip's notes parameter (or, for the humanize block and arrangement parts, of another parameter or
block), always undoable. Partial features and engine limitations are called out where they apply.
Playback and long notes (2026-10-18)
Play/Space starts at the independent playhead, initially the clip start. Selecting or drawing notes and scrolling no longer change that position.
Click the ruler to seek; dragging it still pans. Alt+Play or Alt+Space explicitly previews from the selection. Play again pauses; Stop returns to
the clip start. Loop repeats the complete clip, including when playback starts halfway through it, and voices are cut at the loop seam.
Dragging a note edge can extend over several bars; the roll displays contiguous tied pieces as one note and resizes or deletes that whole note.
The saved text still has one valid entry per 4/4 bar. ;tie=N joins pieces without forcing an instrument program/channel; legacy routed tie
fields still work. Other position edits of tied notes remain source-only. The right edge has a visible grip and can be grabbed even on short notes.
Per-clip meter (PR22) remains open; the ruler-grouping selector still changes the view only.
Play and hear-notes resolve the connected instrument through supported MIDI effects. Synth, built-in piano/bass, one-shot sampler and supported
plain WAV sample sets reuse the local preview renderers; GM uses the configured SoundFont audition endpoint. Seeded MIDI effects use the exact
effective seed. The label identifies instrument preview versus the sketch fallback; both are approximate listening previews, not stored renders.
Cold or unsupported voices use the audible sketch while supported voices warm in a bounded cache. Unsupported expression/sets remain explicit
fallbacks. This does not preview downstream audio effects or promise byte identity with a build. Closing the roll cancels local workers and playback.
Rendering (PR01)
The three canvases (notes grid, arrangement/automation lanes, sound row) scale their backing store with devicePixelRatio (sizeCanvas()), so
they stay crisp on high-DPI screens; a window resize listener re-sizes them while the roll is open. Redraws go through a single
requestAnimationFrame dirty loop (requestDraw()), so a burst of drag/wheel/resize events collapses into one draw per frame. Notes are culled
to the visible time range by a binary search on a sorted-by-start index (sortedRows/visibleRows) before the per-note pixel check, so a very
large clip only pays for the notes actually on screen. dialog.dataset.drawMs reports the last frame's draw time (PR13's frame-time probe;
report-only, nothing fails on it). Canvas sizing and note glyph drawing live in pianoroll-draw.js; pointer coordinate conversion and note hit
testing live in pianoroll-input.js. The dialog keeps gesture lifecycle and drawing composition with the shared model, undo and selection state.
Look (PR02, PR03)
By default a note is a vertical gradient keyed on velocity (cool blue to hot amber, web/lib/pianoroll-theme.mjs), with a 1px top highlight and a
glow when selected. The "high contrast" toggle restores the previous flat, solid fill (no glow), for low-vision or reduced-motion use.
A clip's scale parameter ("c:minor", "f#:dorian", ..., or "none", parsed by web/lib/scales.mjs) drives: a root+scale selector in the
toolbar, row shading (out-of-scale rows dimmed, not hidden), a glowing border on root rows, a thin red outline on out-of-scale notes, a "Fit to
scale" button (moves the selection's out-of-scale notes to the nearest in-scale pitch, one edit), and a click-to-use Krumhansl-Schmuckler key
guess (detectKey(); never applied without a click). The scale table also lives in spec/scales.json (kept in sync by hand; the C# engine/LSP
side does not read it yet).
Gestures (PR04)
Gesture
Result
left-drag on empty grid
draw a note (length by drag); a plain click uses the last-used length
left-drag on a note / its edge
move / resize
Shift + left-drag on empty grid
brush: paints notes of the last-used length, one per grid step, never stretching
right-click on a note / right-drag
delete / eraser sweep (one undo step for the whole drag)
toggle Slide on the selection (not while step input owns s as a piano key)
Alt+U / Alt+S / Alt+R
hi-hat chopper / strum / configure humanize
Alt+Left
return from a ghost focus-switch
?
shortcuts card
Hum… records and reviews a sung line, then converts the parsed note entries to the roll's absolute-time note values before adding them to the clip. Detected notes are estimates; correct them in the roll before building.
Not implemented: Alt+wheel fine-pitch on a note (needs PR06's per-note fine field to matter to the ear first; the data field exists, the gesture
does not yet), the "Unlimited Paint" brush drawing a ramp rather than a straight paint (shift+drag always uses one fixed length, not a stretch).
Ghost notes and focus-switch (PR05)
Other midi-clip blocks whose arrangement parts overlap this clip's are drawn as faint read-only outlines under this clip's own notes (toggle:
"ghosts"), read from their notes text or rendered from chords/melody (own ppq and part offset are converted into this clip's tick/bar space).
"Ghost clips" has one visibility checkbox per source; hidden ghosts cannot be focused or copied. Double-clicking a shorthand ghost opens its
conversion prompt before editing.
Per-note properties (PR06)
Entry grammar: bar:start:length:pitch:velocity[:channel:bank:program[:tie]] followed by zero or more ;key=value pairs:
Key
Range
Meaning
pan
-100..100
stereo position of this note's voice
fine
-100..100 cents
fine pitch offset of this note only
rel
0..5000 ms
release time of this note (no default: unset means the instrument's own)
cut
0..100 %
low-pass cutoff (100 = no filter)
slide
0 or 1
this note glides the previous note's voice to its pitch (PR08)
port
0..2000 ms
portamento time into this note from the previous one
A value equal to its default (0, 0, -, 100, 0, 0) is never written, so a clip stays byte-identical until a property is actually used. Unknown
;key=value pairs are kept verbatim by the roll's own model (web/lib/notes-model.mjs) and re-emitted unchanged; the C# reader
(StructuredMidiNotes.cs) and the JS validator (expanded-midi-notes.js) agree on the same grammar and ranges, and the midi-clip renderer passes
the values through into its JSON note objects. The gm/SF2 renderer honours pan, fine, rel and cut in sound (see PR07 below);
slide and port are handled by the GM/SF2, synth and pitched sample-set renderers; one-shot sampler behavior remains unchanged. The language server completes these keys after ;; invalid values produce AMP2012.
Edited MIDI-module export maps pan to CC10, fine pitch to pitch bend after setting the bend range to one semitone by RPN, release to CC72 and cutoff to CC74. Each distinct combination uses a free channel, separate from retained source channels; export fails if no free channel remains. Pan uses 0..127, release maps 0..5000 ms linearly to 0..127 and cutoff maps 0..100 linearly to 0..127. Import reconstructs values at each note start. Portamento maps to CC65/CC5 on an isolated channel. Slide notes export as a 32-point pitch-bend ramp plus a private AMPLE marker so reimport can restore the editable slide note; other MIDI applications can play the ramp and ignore the marker. These MIDI controls remain lossy representations.
Lower lane (PR07)
A switcher (velocity / pan / fine pitch / release / cutoff) under the grid draws one stem per note. Pan and fine pitch are centred lanes (a value
draws up or down from a midline); the others draw from the baseline like velocity always did. Dragging re-picks the nearest note at the current x
on every move, so one drag paints a freehand ramp across several notes (one undo step). Shift-drag draws an exact straight line between the
first note and the pointer, recalculating every note in that span from the original values; double-click resets the nearest note to the lane's default.
Engine:src/Ample.Core/Engine/SamplerRenderers.cs's SF2 note player (PlaySf2Note) sets a note's resting pan (CC10), pitch bend (fine,
within the renderer's fixed +-200 cent bend range) and brightness/cutoff (cut, CC74) from its own values, reusing the exact mechanisms already
built for controller-lane automation; rel replaces the SF2 region's own release time outright when set. An actual CC10/CC74/PitchBend event in
the song still overrides the resting value from the frame it occurs, unchanged. Synth renderer v2 also applies pan, fine pitch, release and a per-note low-pass before its master filter. Sample-set piano, bass and guitar apply pan/fine/release/cutoff during sample playback and use integer pitch paths for slides. Drums and the one-shot sampler keep their existing clip-level sample behavior.
Slide notes (PR08, GM/SF2 audio)
A note's slide flag is reachable via the S shortcut (toggles the selection) and the note-properties popover; a slide note shows a dashed
diagonal connector from the nearest earlier note. GM/SF2, synth and sample-set piano/bass/guitar renderers continue the most recently started voice on the same MIDI channel, glide in cents across the slide note's duration, and do not strike a second note. A slide without an active voice is silent. MIDI export writes the bend ramp and AMPLE marker described above.
Chord stamps (PR09)
spec/chords.json / web/lib/chords.mjs: 47 chords (triads, suspended, sevenths, ninths, elevenths, thirteenths, sixths, jazz/altered, power,
cluster - scoped down from the plan's "about 80"). A dropdown plus a "Stamp" arm toggle: while armed, a plain click on empty grid plots the chord
rooted at the clicked row. The canvas shows an outline preview under the cursor. "Chord options" sets inversion, spread, drop-2, octave doubling
and a bass MIDI pitch; preview and placement use the same options.
Hi-hat roll chopper (PR10) and strum (PR11)
Alt+U slices the selection into N equal pieces (web/lib/chop.mjs: 1/2 through 1/32, or 1/4t/1/8t/1/16t triplets) with exact tick arithmetic (the
pieces always sum to the original length), a gate-length percentage and a velocity ramp (flat/rising/falling/seeded humanised). Alt+S
(web/lib/strum.mjs) groups notes that start within a 32nd-note tolerance and staggers each group over a beat span following a curve
(even/slow-then-fast/fast-then-slow/guitar/harp), fading velocity toward the end; direction is down/up/alternate. Both open a plain popover
(division/curve/options + Apply), not a custom floating dial widget as sketched in the epic.
Humanize (PR12)
Alt+R creates or updates a midi-humanize block, configures its velocity and timing parameters, and previews the exact drift with the same stable
random stream as the engine. The preview reads the block's effective seed, including a locked or overridden value; if the seed cannot be read,
preview and Bake are disabled. Bake applies the previewed changes directly to the notes as one undoable edit. When inserting a new block, the
roll routes the clip's existing MIDI outputs through it and preserves disabled connections.
Performance, accessibility, polish (PR13)
A frame-time probe (dialog.dataset.drawMs) reports each draw's duration (never fails anything). Popovers return focus to the canvas on Cancel.
A ? shortcuts card lists every gesture above. Not verified: touch gestures (no touch device in this pass), prefers-reduced-motion (there is no
CSS animation to gate - the only "motion" is per-frame canvas redraw, already driven by actual changes, not a timed animation), and a full
screen-reader audit beyond the existing note-navigation announcements.
Extra tools (PR15-PR22)
PR15 arpeggiate, PR16 legato/staccato/remove-overlaps, PR17 flip/mirror/reverse/scale-time:web/lib/roll-tools.mjs, reachable from a "Tools"
dropdown in the toolbar (same-count transforms write back by index; arpeggiate replaces the selection, like the chopper). "Flip pitch" uses the
selection midpoint; "Mirror pitch…" lets the user choose a MIDI pitch as the mirror axis.
PR18 colour groups:grp (1-8, the same ;key=value grammar as PR06, no engine change needed since the engine already passes unknown-but-
valid-integer keys through). A group filter dims notes outside the chosen group, "Select group" selects them, a colour tab marks each note.
Choose a group and MIDI channel, then use "Move group" to change every note in that group in one undoable edit.
PR19 scale-lock and chord-lock: scale-lock snaps newly drawn, brushed and moved notes to the current scale; Up/Down transpose selected notes
within the scale. Chord-lock uses sounding notes in visible chord/melody ghost clips for the same gestures. When both are enabled, pitches meet
both locks; when no chord tone is sounding, scale-lock still applies on its own.
PR20 riff machine:web/lib/riff.mjs, a seeded step generator (density, accent, pitch spread, optional scale snap) reachable from the Tools
menu; scoped from "in the selection" to "from the cursor/selection start, for N bars" (the roll has no empty-time-range selection).
PR21 multi-clip editing: "Copy ghosts" pastes every visible ghost note into the open clip; a "Reference clip" picker keeps any clip visible
even without arrangement overlap. "Split edit" opens two independently editable roll dialogs side by side; both write normal undoable edits.
PR22 per-clip meter (implementation open): the owner chose per-clip meter. Arrangement at N stays on the project timeline; each clip's meter
defines its own bar length in quarter-note beats. Four bars of 3/4 take 12 beats and do not auto-align to a project bar. The current parser and
renderers still assume four quarter-note beats per bar; the view-only selector does not claim to change playback until the end-to-end work lands.
What's still open
Remaining manual acceptance: a full screen-reader audit. PR22's agreed semantics are recorded, but its per-clip meter implementation must update
the parser, arrangement and playback together. The piano-roll tour example and in-app help entry are now present; automated verification follows
after the feature changes.
Extending the AMPLE Studio UI
The Advanced piano roll uses roll-transport.mjs for playhead/loop math and roll-preview.js for connected-instrument audition through existing client-side renderers. Preserve its explicit approximate/fallback label, exact effective MIDI-effect seeds, bounded cache and close-time disposal when adding views. Bar-crossing notes remain valid per-bar entries joined by ;tie=N; never synthesize a GM program/channel merely to attach a tie.
Project exports use project-export.js and the token-protected /api/project/export job API. Long conversions/compression must remain asynchronous, show progress, support cancellation, and write only to the job's temporary directory. Do not buffer a completed archive in browser memory; download through the completed job endpoint.
Title-bar status controls use .node-status; completed render/cache states belong there, while .tag.stat is reserved for live build steps. Route error inspection through showBlockStatusError so the log panel, source diagnostic and upstream failure navigation remain consistent. New playback paths must use playbackOutput(context) after their processing/meters, or registerPlaybackAudio(element) for native audition audio. Do not put this tab-local 0–150% gain into offline rendering, exports, song parameters or recording capture. See web-ui.md.
Canvas blocks use a two-row header: a compact cyan nameplate with an automatically sized title and pin toggle, then a toolbar with type, version, the current block's GUI/properties arrow, connected effects and the source-editor action at the right edge. Double-click the name to rename through the normal edit command; the title remains a drag handle, with pointer capture kept on the title so double-click or double-tap still reaches its rename handler. Two-finger touch gestures on the canvas zoom around their midpoint and pan the graph; native browser touch zoom is disabled only on that surface. RDP gestures forwarded as Ctrl+wheel use the existing canvas wheel zoom. Canvas geometry uses the complete 60-pixel header height for ports, links and layout. Volume blocks remain narrower and taller with their channel controls on the node. Each channel has a fader and a live output meter in dB; Link/Mono and channel mutes use compact switches. Live preview plays the resolved upstream signal through the block settings without rebuilding, and meter ownership follows that block rather than any unrelated audition. Built-take playback remains available separately.
Clicking the active bottom-panel tab collapses the panel; clicking a different tab or a tab while collapsed opens it. Tab clicks use the existing layout toggle so visibility controls and saved layout stay aligned.
This guide is for AMPLE plugins, built-in blocks, editors and panel work. It explains how to fit new work into the default dark Studio UI and how to tell whether existing work needs migration.
Start here
Treat the Studio mockup as a historical reference only; it does not define current UI behavior and must not be updated. Read the Studio UI plan for current implementation guidance.
Read the web UI status and the relevant chunk in the plan table. The plan distinguishes real behavior from mock-only behavior.
Search the existing UI before adding a new control. Reuse its components, token names, command path and panel registration.
Visual language and controls
Use semantic CSS variables from web/styles/tokens.css for surfaces, text, borders, focus and state. Use skin.css for common element skins and studio.css for advanced panels. Do not add a local hard-coded palette.
Respect data-theme and reduced-motion preferences. Keep readable contrast and visible keyboard focus in every theme.
Use web/app/ui.js helpers (el, ask, showMenu, toast) and existing field/control modules rather than building ad hoc dialogs or numeric editors.
Keep controls keyboard operable and named. Sliders, knobs, curve handles, toggles and menus need keyboard behavior, an accessible name and appropriate ARIA state/value attributes.
Use the shared studio/knob.js control for rotary parameters. Drag in normalized position and then apply the parameter's linear/logarithmic scale, matching its indicator; a linear value delta across a wide log range causes low values to hit minimum with tiny drags (verified in source, 2026-10-10). Keep incremental pointer deltas, an unsnapped drag position for stepped controls, and Shift fine control so changing sensitivity or reversing at an endpoint does not jump or stick.
One pointer gesture should commit one edit. Preview the drag in the UI, then submit a single batched edit when it ends; Escape/cancel should leave the document unchanged.
Source editor
The Source view is a locally bundled CodeMirror 6 editor. Its pinned dependencies and offline bundle are maintained by package.json, package-lock.json, web/codemirror/ample-language.js and tools/build-codemirror.mjs; rebuild with npm run build:codemirror. The AMPLE mode uses lexical facts from spec/tokens.json and the documented block-header shape. Theme colors use Studio tokens, and the bundle is lazy-loaded from web/vendor/ with no CDN. Preserve the source view's LSP client, Apply/Save semantics, read-only file support, gutter, diagnostic/build overlays, links, completion, drops and keyboard actions when changing the editor. Focused browser acceptance is tracked in CM01.
Adding a block, plugin or view
Define the block/template contract in spec/templates/ and its language/data behavior in the applicable spec and tests.
Use the library and graph paths in web/app/libraryui.js, canvas.js and graph.js. Keep node state and each effect's bypass state separate. A node can use the independent disabled header flag and its context-menu toggle under Edit; it remains visible but produces no take, and required consumers are implicitly disabled with the warning AMP5253. Effects are ordinary typed AMPLE blocks joined by audio links. Open an effect's Advanced panel to edit its own parameters; use the audio block's arrow to navigate to connected effects, with search when multiple are connected. Add → Effects → EQ & filters → Equalizer inserts a normal neutral EQ block connected in the graph. Keep new block actions in the existing Add, Build & export, Edit or Block tools context-menu groups instead of lengthening its top level. Graph links are the source of truth; do not add embedded chain managers or badge-based effect controls. Build, undo and source editing use the normal language/command path.
A delayed context-menu action must read its group from the current store when clicked. Saving the view may reload group objects while a submenu is open, leaving references captured when the menu was built stale (verified 2026-10-16).
Preserve the existing port types. Inputs always sit on the left of a block; outputs always sit on the right. Audio and render ports display their compatible stream names on both sides: audio → audio, render → render. Tooltips retain the underlying port names, including numbered mixer channels; source connections retain those identifiers. The graph presents midi as Events. Event-processing blocks may be added later using existing event types and links, but do not imply that analogue signals, new event types, or new backend processing are implemented by the Studio UI.
Render ordinary parameter editing through the existing properties and fieldFor paths. Add a bespoke Advanced panel only when the block benefits from a visual editor; register it in web/app/studio/panels/registry.js. web/app/studio/modal.js owns the accessible dialog shell and transport. Panel modules belong under web/app/studio/panels/; share controls from web/app/studio/.
Hidden properties tabs use display: none; only the active tab contributes height. Verified 2026-10-17: overlapping tabs hidden with visibility: hidden still reserved space, so a delayed master-level result in Info moved the Parameters action buttons down. Keep the actions immediately after the active content and cover delayed hidden-tab updates in layout tests.
Obtain current values through the document/store and send changes through edit() or the documented command API. Do not write project files directly from a component.
For custom visualizations, read tokens at render time. Canvas/SVG drawing must respond to the current theme, selection, enabled/bypassed state and reduced motion.
Preview contract
Label browser DSP or plot estimates as approximate. Do not present a visual estimate as a measurement of the integer renderer.
The server effect preview is the exact audible reference for the same ControlService-selected input take, range and parameter values. It is bounded, does not create a take, and does not touch the takebank or journal. Keep the preview range and cache bounded. A preview request must use the take selected for the immediate upstream block; do not silently change selection as a preview side effect. An explicit Build and use input action may build and select that take.
Offer the exact server preview only for types registered with the built-in effect preview route. Generic blocks can still expose input playback and ordinary properties, but must not send unsupported effect preview requests.
Live local preview uses web/app/studio/preview/audio.js and the shared player: it loads the selected upstream take with the selected effect bypassed, then plays it through a persistent Web Audio approximation graph. Label the result approximate. While playback continues, knob movement updates the live graph immediately without restarting the audio; commit one document edit when the gesture ends. A/B toggles the selected effect's bypass in the active live graph, or replays the rendered comparison when rendered preview is active. Keep input playback and exact server-rendered preview under the Compare / render details disclosure.
Generator blocks without audio inputs play their own built take. Keep their controls distinct from effect previews; do not require an audio input for generator playback.
Keep unavailable preview controls disabled with a short reason (for example, no source take). Never build a take silently just to make a preview button work.
For costly exact-render analysis, make the action explicit. The compressor's waveform and gain-reduction analysis only requests the bounded integer render after the user presses Analyze; show the analyzed range and distinguish input and output traces.
Plot-based effect panels should update their curve, taps, room shape or timing readout from knob preview values, then commit through the shared control callback once at gesture end.
Advanced preview transport exposes a Loop toggle beside Play for the full sample; Shift-drag selects a loop range. Keep the panel's loop preference across Stop/Play and transient preview reloads, and restore the pre-panel player loop state when closing. Native full-sample looping must survive playTransient replacing its audio source.
Resolve effect preview input from the latest upstream build take, matching block playback; use separately selected takes only as the initial fallback. On input identity changes, cancel pending audition work and stop old preview playback. Key decoded audio by take identity. Verified source mismatch on 2026-10-10: the source block could play a new voice while Advanced still requested its older selected take. Server previews must ignore disabled audio/render links.
A live spectrum uses the shared player analyser and unsubscribes when its panel is replaced or closed. A decoded take estimate is a separate display and must remain labeled approximate.
Tempo-based effect playheads derive bar phase from the shared player clock and project tempo; dispose the player listener with the panel.
Playback, transport and scrubbing should reuse the shared player rather than creating competing audio clocks.
File fields and project assets
Use web/app/fields.js so each file field inherits its accept rules, picker, clear, open/reveal and drag/drop behavior. Internal drag payloads carry project-relative paths; OS drops go through the journaled upload/conflict flow, which reports per-file transfer progress. The shared picker windows lists above 2000 items; reuse filelist.js instead of rendering an unbounded list in a plugin panel. Do not bypass the command service for upload, overwrite, rename, move or delete.
Do concurrent changes need migration?
Use this quick check when another AMPLE feature was started before a Studio UI change:
Check
No migration needed when…
Migrate when…
Layout
The feature already uses the current library/canvas/properties/bottom-panel shell and remains usable at narrow sizes.
It adds a competing full-page shell, hides required transport/panels, or breaks resize behavior.
Styling
It uses semantic tokens and shared skins, with focus and reduced motion handled.
It hard-codes colors/surfaces or loses contrast/focus under the current themes.
Editing
All project changes use the store/command path, remain undoable and preserve one-gesture/one-step behavior.
It writes files directly, bypasses journal/pre-image safety, or cannot be undone.
Effects/state
Effect bypass and node enablement remain independent; the source node's Advanced dialog manages its ordered connected chain, and each effect's parameter editor stays inside that same dialog.
A new path conflates node disable with effect bypass or lacks its canvas/property affordance. Chain edits must preserve type-safe links, terminal consumers and one-gesture/one-undo behavior.
Files
It reuses the project picker, accepted-file rules and project-relative drag payloads.
It adds a second picker/upload flow or writes around journaled file operations.
Help and proof
The web UI docs, help, parity matrix and a scenario/spec describe the behavior.
The behavior has no current documentation or the scenario no longer matches it.
If all checks are in the “No migration needed” column, link the change to this guide and the full mockup in its handoff. Otherwise update the affected app region, mockup, docs, parity scenario and tests in the same implementation chunk. These migration rules apply only to AMPLE.
Pure web unit tests: one focused node --test web/lib/test/<file>.test.mjs.
Server/language change: build the touched project; run one focused dotnet test --filter ... when it fits the agreed narrow test window.
Browser interaction: add/update a Playwright spec and its .scenario.md; run it only when browser/test-host execution is authorized for the task. AMPLE browser checks use a fresh Playwright Chromium context, not the Codex browser integration.
Report synthetic-fixture Playwright results separately from active-demo results. Synthetic audio verifies UI and request wiring; it does not establish that a selected take in the active demo plays correctly.
Always run git diff --check. A syntax/unit/build check is not visual or browser acceptance.
Generation settings and reproduction (2026-10-17)
For audio cards, request bounded server waveform summaries; never fetch/decode every complete song just to paint a thumbnail. Serialize background summary requests, cache their in-flight promises, and release media sources on Stop. Every recurring poll needs an in-flight guard and teardown cancellation. Avoid redundant polling of a task already shown in its dialog. Close event streams in hidden tabs and reconnect on return. Browser regression coverage: spec-tests/parity/ai-network-budget.spec.ts.
Generation fields must display effective numeric defaults, including model-aware CFG, rather than leaving an ambiguous blank. Preserve explicit edits through source reload. Review the seed/CFG near miss checklist for source → request → take coverage. Producer controls expose Check reproduction with separate reference/new-render playback and warnings for changed configuration/software; the original take stays selected.
The language server (as built)
Status (2026-10-03): built in chunk 80. The fresh VSIX was installed and exercised in an isolated VS Code 1.140.0 profile on Windows.
Ample.Lsp is a Language Server Protocol 3.17 server for .amplet songs. It holds no language logic: every answer comes from Ample.Language (parser, queries, formatter, edits), so the editor, the CLI and the web UI cannot disagree. A test runs the server over the whole conformance corpus and compares its diagnostics with the library's.
Stdio transport (Content-Length framed). Editors start this. Nothing else is written to stdout.
ws://127.0.0.1:<port>/lsp of ample serve
WebSocket transport: one JSON-RPC message per text frame. The browser's session cookie is the token; the same host and origin checks as the API apply.
editors/vscode/
The VS Code extension: starts ample lsp, TextMate grammar, snippets, language configuration, commands.
What it does
Capability
Notes
Text sync
Incremental; positions are UTF-16 (positionEncoding: utf-16).
Diagnostics
Pushed on open, change, save; pull (textDocument/diagnostic) too. Each carries the code, a codeDescription.href to <help base>#AMPxxxx, the hint in the message and the related line.
Hover
Block type, block, parameter (from the spec data).
Definition, references, rename (with prepare)
A block and every from reference.
Symbols, folding, selection ranges
Document symbols (blocks), workspace symbols over the open documents, folding of blocks, selection from word to line, block and document.
Completion
Block types as snippets (class, version and parameters filled in from the spec), parameters, the values of a parameter with fixed choices, block names, ports after from x. and ->, classes, at and for after the tag, the # view group/size/pin lines with block names, keywords. The studio editor also shows the best guess in grey while typing (Tab takes it; Esc hides it until another line or a space) and Ctrl+Space opens the list.
Signature help
The parameters of the block type around the cursor.
Formatting
Whole document (a range request formats the whole document too, because the formatter works on whole songs).
Code actions
Every quick fix of the library, "Fix all", "Bake" and "Lock" for the block under the cursor.
Semantic tokens
Full and range: block types, names, parameters, flags, classes, numbers, strings, comments.
Inlay hints
The type of each connection after a from line.
Cancellation, order
Requests are handled one after the other, so results are in order; a cancelled request that has not started is dropped (error -32800); after shutdown requests are refused; exit gives code 0 after a clean shutdown, 1 otherwise.
Measured budgets (2026-10-03, Windows 11, one developer machine, Debug build)
What
Budget
Measured
Diagnostics after an edit in a song of more than 5,000 lines (parse, analyse, publish)
150 ms
52 ms
Completion in the same song
50 ms
under 1 ms
The numbers come from LspTests.Budgets_for_a_five_thousand_line_song, which fails above 1 s and 500 ms; the budget is checked by reading the printed numbers. A Release build and other machines were not measured.
Decisions
No OmniSharp, no StreamJsonRpc. The protocol is a thin layer over System.Text.Json (about 700 lines): no dependency, nothing to keep in step with a library, and the in-memory channel makes every capability testable without a process.
The grammar and the snippets are generated from spec/ by tools/gen-editor-assets.mjs; --check is part of check-all. Grammar tests (editors/vscode/test/grammar.test.mjs) use vscode-textmate with the same regex engine as VS Code.
Not done (and where it goes)
Code lens, document links, on-type formatting, semantic tokens delta, declaration and type definition: not built (the language has few of the things they need).
Cross-file references and a project folder are not used: the language has no includes yet, so every document stands alone. The read-only fallback for sources outside the project comes with the editor chunk (77).
Hover does not show the seed lock state or take information, and there are no "extract to group" or "unlock seed" actions (groups are not in the song file yet; seeds live in the project, not the song).
Diagnostics are pushed on every change (no debounce); at the measured speed this is fine.
The VS Code integration test packages and installs the VSIX, activates the extension, checks its commands, and sends a completion through the language client to a deterministic local LSP fixture. The real CLI also passed a separate stdio initialize/shutdown smoke check on 2026-10-03. No second client (Neovim or Helix) was tested.
Diagnostic columns are code points in the library and UTF-16 units in the protocol; the server converts them (tested with an astral character).
Instance registry
AMPLE records each running project server below instances/ in its AMPLE home: %LOCALAPPDATA%\ample by default, or AMPLE_HOME when set. The record format is defined by the JSON Schema. The .launch sibling contains the current browser URL and token; it is restricted to the current user (Windows ACL without inherited rules, Unix mode 0600). The token never appears in the JSON record or ample instances output.
Each record contains the project id and name, folder, server pid and process start time, port, start/update times, AMPLE version, machine name and start source. Agent starts include their self-reported name, reason and optional session. ACE health is optional. ample instances lists only records whose process still has the recorded start time and whose port answers GET /api/ping with the same project id. ample instances --clean removes stale record and launch files; --json emits records without launch URLs. --home <folder> selects a registry home for isolated use and tests.
The server registers after it has bound its port, refreshes updated every 15 seconds and removes both files during clean shutdown. A crash can leave files behind; readers treat them as stale, and --clean removes them. GET /api/ping returns only { "ok": true, "id": "<project-id>" } without a token. POST /api/server/shutdown uses the normal session-token requirement.
Verification covers fake process and ping probes, atomic concurrent writes, stale cleanup, private launch-file permissions and the live server ping and token-gated shutdown route. The registry does not prove that an agent's self-reported identity is authentic.
Opening AMPLE projects
ample open <project-folder> attaches to a registered server for that project or starts one in the background, waits for its local liveness check, and opens the private launch URL in the default browser. --no-open prints the launch URL instead. A .amplet or .module target resolves to its containing project. With no target, ample open lists recent projects; --last selects the newest one.
The last successful port is saved in recent.json. A later open tries that port first and chooses a free port if it is already occupied. Child server output goes to <AMPLE_HOME>/logs/<project-id>.log; each log rolls at 1 MiB and retains two older files. Launch tokens are kept in the per-user .launch file and are not written to the server log. Set AMPLE_HOME or pass --home to use a different writable user-data folder.
ample open --agent <name> --reason <text> records self-reported agent attribution. When the sibling Ample.Hub.exe is installed, the CLI starts it if needed and sends a correlated v1 JSON-lines request over the current-user-only pipe. When the hub is not installed or reachable, agent starts use the visible CLI fallback. AMPLE_AGENT and AMPLE_AGENT_REASON provide the same attribution through the environment. The hub pipe's versioned fields and limits are documented in the L05 plan; the pipe server is implemented by L05.
tools/AMPLE.bat [target] is the Windows double-click/command launcher. It uses a packaged ample.exe beside the batch file or at the package root one folder above; otherwise it runs the CLI project from source. The classic ample serve command remains a foreground server, opens the browser by default, and prints its launch URL as before. If the instance registry cannot be written, classic serving stays active but ample open cannot discover that server.
The optional --song value is validated by the CLI, but the web UI does not yet consume a song deep link.
Packs and ACE-Step settings
AMPLE looks for installed packs in this order: AMPLE_PACKS, packsFolder in %LOCALAPPDATA%\ample\settings.json, %LOCALAPPDATA%\ample\audio-packs, the AMPLE root supplied to serve --root, then ancestors of the executable and project. ample packs list shows that order; ample packs where explains a missing General MIDI bank; ample packs link <folder> creates a junction instead of copying an existing audio-packs folder.
The fetch commands install into %LOCALAPPDATA%\ample\audio-packs by default. Add --into to retain the repository audio-packs layout. The same settings file can contain ace.exe, ace.models and ace.port; tools/start-ace-server.bat reads those values or the ACE_EXE, ACE_MODELS and ACE_PORT environment variables.
Plugins (as built)
Status (2026-10-02): protocol version 1 is implemented in chunk 60 and marked experimental until the first real plugin (ACE-Step, chunk 65) has used it.
A plugin is a program in its own folder with a plugin.json. AMPLE starts it as a separate process, talks to it over stdin and stdout and never lets it write outside its job folder. Everything that is not built in (AI voices, analysers, effects) arrives this way.
The manifest (plugin.json)
Field
Meaning
id, version, title
Lower-case id with dashes, dotted version, display name.
kind
voice, analysis or effect.
protocolVersion
1. A newer major is refused (AMP5271).
experimental
True until the protocol is frozen.
entry
command and args of the program; arguments are relative to the plugin folder.
platforms
windows, linux, macos.
blocks
The block types it renders (for example ai-voice).
determinism
exact, machine-exact (same bytes on the same machine and toolprint) or unverified.
toolprintFields
What the plugin puts in its toolprint (engine commit, model hashes, hardware class).
needs
gpu (the host runs one GPU job at a time), network, diskMb, secrets (names; only these are passed, as environment variables).
settings.schema
A small JSON Schema (type, enum, minimum, maximum, pattern, required, default, additionalProperties) that drives the settings editor and validates the values (AMP5279).
All problems of a manifest are reported at once (AMP5270). ample plugins validate <folder> checks one.
The protocol (version 1)
JSON-RPC 2.0, one JSON message per line on stdin (host to plugin) and stdout (plugin to host). Stderr is captured as log text.
initialize {protocol, host} returns {protocol, id, version, blocks}; the protocol must be 1.
describe {} returns {toolprint, determinism}; the toolprint becomes part of every plan.
render {jobDir, block:{type,name,parameters}, seed, sampleRate, samplesPerBar, firstBar, barCount, activeBars, inputs, settings} returns {outputs:[{role, path}], meta}. Large data goes through files: inputs are in jobDir/in/, outputs must be written inside jobDir and are named by relative path.
analyze {jobDir, analysis, input: "in/audio.wav", options} returns {result} and may return bounded {outputs:[{role, path}]} artifacts for file-producing analysis jobs. Analysis output paths must be relative to the private job folder and point to files the plugin wrote there.
cancel {id} and shutdown are notifications; progress {id, fraction} and log {level, message} flow from the plugin.
Local audio transcription
Local reference voice synthesis
Quoted voice parameters are decoded as language strings, including an empty reference_text. With auto_detect on, an empty transcript invokes Whisper rather than being passed to Qwen as literal quotes. Plugin 0.1.5 invalidates takes made with the former decoding behavior.
Each engine can have its own interpreter: voice-clone.pythonXtts, voice-clone.pythonQwen3 and voice-clone.pythonChatterbox (empty means not set). The one for the block's model wins over voice-clone.python, so an environment that only works for one engine is used only for that engine. An explicit voice-clone.python is authoritative for every engine: dependency checks report missing modules or incompatible versions instead of changing interpreters or automatically installing into that environment. Progress shows sys.executable. Qwen with auto_detect on also requires faster_whisper; disable auto detection when supplying the transcript manually, or prepare that dependency in the selected environment. The generic python default retains automatic setup behavior. Plugin 0.1.4 invalidates previous takes so a former CPU-fallback result is not silently reused.
Dependency compatibility note (verified 2026-10-10): qwen-tts 0.1.1 requires Transformers 4.57.3; the owner's shared Transformers 5.2.0 environment failed to import check_model_inputs. Use a separate Qwen interpreter with compatible Transformers and Hub versions. An environment may reuse existing CUDA packages through a .pth entry and keep its conflicting packages local; this avoids downloading PyTorch again but depends on the shared environment remaining available. Import/CUDA checks establish dependency readiness, not successful model synthesis.
The experimental voice-clone block uses a project audio file as its voice reference and speaks its text parameter. A pause is written in the text: I'm walking [pause 800 ms] down [pause 1.2 s] this road is made per piece with the same reference and AMPLE puts the silences between them (at most 10 s per pause and 60 s together; a pause only at the start or end is allowed). To put pieces of different blocks one after the other use a join block, and to start a sound later by seconds or beats a shift block: a placement (at <bar> for <bars>) is in whole bars. XTTS and Chatterbox need no reference transcript. Qwen3 requires reference_text; when that is blank, auto_detect on runs faster-whisper. The block refuses to build when Qwen3 has neither. MP3 and other accepted formats are converted to mono 24 kHz WAV with the configured ffmpeg when the engine requires it. Configure the local Python executable, model root and ffmpeg in the plugin settings. On first use, the plugin prepares an isolated Python environment and installs the selected engine's packages there; model weights are not downloaded. Qwen3 does not support Dutch or Flemish. Generated WAVs are checked for samples, finite values and non-silence; empty or silent output fails the build with an explicit error. A plugin version upgrade invalidates the previous voice-clone takes once. Speech quality and GPU runtime remain unverified until a build is run with the selected local model. Loading and synthesis progress explicitly show the selected GPU (CUDA) or CPU device, including long-running heartbeat updates; a CUDA check in another environment does not verify the active worker.
plugins/analysis-local-transcription is an optional local adapter for the transcription wizard. It expects audio-separator, basic-pitch, soundfile and scipy to be installed in a local Python environment and requires a complete, preinstalled Audio Separator model folder. Nothing is bundled, installed or downloaded. The compute-device choice defaults to GPU and requires explicit permission for each job; choose CPU to hide CUDA from the worker. GPU use requires GPU-enabled dependencies in the selected Python environment; otherwise GPU mode may fall back to CPU. The worker sets offline environment flags and blocks Python socket connections; missing model companions cause the job to fail instead of downloading them.
In the wizard, select Local stem separation and MIDI estimate and provide options such as {"pythonPath":"C:\\\\Python310\\\\python.exe","modelDirectory":"C:\\\\AudioModels\\\\UVR","separatorModel":"UVR_MDXNET_KARA_2.onnx","modelLicense":"license name or URL","drumMode":"skip"}. The model and all companion files must already exist in that folder. The result records the model SHA-256 and license text supplied in options; the UI labels the latter as user-entered, unverified metadata. The adapter aligns returned stems to the source duration, asks Basic Pitch for vocal and accompaniment MIDI estimates, and returns vocal pitch activity for review. When the selected model returns separate accompaniment stems, it emits one MIDI artifact per stem and a combined multitrack MIDI artifact for one-step import, and suggests editable GM programs from safe stem labels; a single instrumental stem produces one backing artifact. Stem labels are not instrument recognition, and the clamped 0–1 Basic Pitch amplitude is not calibrated confidence. Drum stems are omitted by default because Basic Pitch does not map drum hits; set drumMode to pitched to request a rough pitched estimate. Runtime and transcription accuracy have not been measured on this machine. Model terms are separate from package licenses; verify the selected model's terms before use.
What the host guarantees
A clean environment: nothing inherited except PATH and the few variables a program needs to start; a declared secret is passed only to the plugin that declared it, and its value is redacted from captured logs.
A private job folder (deleted afterwards); an output path outside it is refused (AMP5272).
Timeouts (AMP5276), cancellation (the process tree is stopped), crash containment (AMP5275), invalid output (AMP5273), oversized messages and outputs (AMP5274), plugin-reported errors (AMP5278). A failed render is a failed attempt in the takebank: the plan stays, there is no take, and the next build can try again.
One GPU job at a time for plugins that need the GPU.
The plugin's description (toolprint) and determinism claim are stored in the plan of every take it makes, so regeneration warnings and reproduction checks know what produced a take.
Writing a plugin
plugins/sdk/ample-plugin.mjs is a small Node helper (serve({initialize, describe, validate, render})); plugins/echo is the reference plugin (deterministic, no GPU) and plugins/flaky misbehaves on purpose (crash, hang, garbage, huge output, path escape, errors, logging a secret) for the host tests. Any language works: read lines, answer lines.
Commands
ample plugins list|describe <id>|validate <folder>|run <id> (run is a self-test: handshake and description).
Voice-clone warm cache (2026-10-10)
voice-clone.keepWarmS (default 600, 0 = off) keeps one Python worker per interpreter alive and the loaded model (and Whisper) in memory, so a rebuild shortly afterwards goes straight to synthesis instead of loading the model again. Only one engine is kept: asking for another engine unloads the first.
The worker gives the memory back by itself and logs why (voice model cache: evict (reason); the log holds the event, the reason, counts and megabytes only):
idle for longer than keepWarmS;
free video memory of the device below voice-clone.minFreeMb (default 1000);
video memory used outside this worker grew by more than voice-clone.pressureMb (default 1024) since the model was loaded. This is computed from the device totals (torch.cuda.mem_get_info) minus what the worker holds itself; it never looks at other processes, their names or their arguments.
A pressure check only runs between builds, never during synthesis. A timeout or a crash ends the worker and so empties the cache. When the generic python default re-launches into the plugin's own isolated environment, that one-shot child has no cache: use an explicit interpreter (per engine) to benefit. Not measured on real hardware yet: the speed-up and the eviction on real VRAM pressure.
Contention while a build runs (added the same day): the worker keeps looking at the device totals while it loads and synthesizes. When it sees pressure (free memory below minFreeMb, or memory outside the worker grew by more than pressureMb), the running build is allowed to finish, and the model is unloaded right afterwards instead of being kept (log: contended, then evict). All three values and keepWarmS are plugin settings. Measured on the owner's machine (8 GB card, torch 2.6.0+cu124): the totals are readable (mem_get_info), and allocating 300 MB inside the worker does not change the "outside this worker" figure, so the worker's own use is not mistaken for another program. Not yet measured: a real load that competes with another program.
GPU plugins kept warm by the host (2026-10-10)
The host starts a plugin process per job and used to stop it afterwards, so nothing a plugin kept in memory survived to the next job. A plugin that needs the GPU and declares the setting keepWarmS (seconds, 0 = off) is now kept running by PluginHost after a successful render and reused by the next job of the same plugin; it is stopped when it has been idle for keepWarmS, when PluginHost.ReleaseWarm is called (for a "free GPU memory" action), when the host stops, or when a job fails or times out (a failed job's process is never kept). The events ("kept for up to N s", "reused", "released (reason)") go to PluginHost.OnWarmEvent; they name the plugin and a reason, nothing about any other process. Only voice-clone declares keepWarmS so far (default 600).
The host also watches the memory totals of the GPU while it keeps a process (NVML nvmlDeviceGetMemoryInfo, fallback nvidia-smi --query-gpu; totals only, never processes): every five seconds it releases a kept process when free memory is below the plugin setting minFreeMb (default 1000) or when the memory in use grew by more than pressureMb (default 1024) since it was kept (our idle processes do not grow, so growth is another program). Every keep, reuse and release is appended to .ample/logs/gpu.log (time, plugin, reason). GET /api/gpu returns the totals and what is kept; POST /api/gpu/release gives the memory back now (the button "Free GPU memory now" in the plugin settings view). The GPU settings are the voice-clone plugin's keepWarmS, minFreeMb and pressureMb.
Not kept warm on purpose: ACE-Step (its server backend keeps the models loaded in the server the owner runs; the command line backend starts the program per job, so a kept Node process would hold nothing) and the local transcription plugin (it runs through the analysis path, blocks the network and asks permission per job for the GPU, so a long-lived worker would go against that). Only the first GPU is looked at.
When the environment of an engine cannot be used, the error names which setting chose the interpreter and which setting to change (for example: "The chatterbox engine runs in the Python from voice-clone.python. It is missing: ... Set voice-clone.pythonChatterbox to a Python that has them"). Nothing is installed or switched automatically.
ACE-Step plugin
Status (2026-10-03): built against a stand-in server. The /lm → /job → /synth transport is regression-tested locally, but real-server audio behavior remains unverified and needs the owner's GPU approval.
plugins/acestep turns ai-voice and ace-music blocks into requests to an acestep.cpp server and brings the audio back as 16-bit mono PCM at the project's sample rate, cut or padded to the block's length. ace-music may intentionally include accompaniment and may be instrumental when no lyrics are linked. ai-voice first requests an a-cappella performance and then extracts its vocals stem from the resulting mix, so the delivered take is the extracted stem rather than the initial music-generation result. The manifest says determinism: unverified, so regeneration warns (AMP5101 and AMP5102) until a reproduction check has come out the same.
Inputs and meaning
Lyrics editor file is the canonical lyric source. Its timed markers are removed before ACE receives the words. ai-voice refuses to render without it; ace-music accepts no lyrics.
Vocal direction is written performance guidance, not an identity picker or a promise of a named singer.
Mood / scene arrives through style and is appended to the ACE caption.
Melody arrives as the MIDI clip's JSON at the configurable melodyField (default melody) in the /lm request. Set that field to the exact server revision's documented conditioning property before a real run; the linked edge is recorded as melodyPlan: requested, rather than being silently ignored.
Download formats
Built WAV takes download without conversion. MP3 export runs the configured AMPLE_FFMPEG executable (or ffmpeg on PATH) with libmp3lame; the response identifies the exact encoder banner in X-Ample-Mp3-Encoder. If the encoder is unavailable or fails, AMPLE returns AMP5290 rather than serving a renamed WAV.
What it sends and expects (ACE-Step server API)
Step
Request
Answer
Plan
POST /lm with JSON caption, lyrics, language, seed and duration
{ "id": "..." }
Render
POST /synth with the completed plan
{ "id": "..." }
Voice-only extraction
POST /synth as multipart form data: extraction request plus the rendered WAV in audio
{ "id": "..." }
Poll/fetch
GET /job?id={id} then GET /job?id={id}&result=1
job state, then the multipart audio result
The extraction request uses task_type: "extract", track: "vocals" and a Base or SFT DiT model. It is on by default for ai-voice (vocalExtraction: true). The plugin discovers a compatible model through GET /props; the first Base or SFT model that /props lists wins, which is a light (non-XL) one when you have it. Set vocalExtractionModel (the file name as /props lists it; a missing .gguf is added) when you want a specific model, for example an XL one for more detail. The model used is written into the take's metadata (vocalIsolationModel). A Turbo-only server fails before it creates a take, rather than returning a music-backed result labelled as vocals. An optional bearer token is read from the secret ACESTEP_TOKEN.
Rules the plugin follows
Only a local endpoint (localhost, 127.0.0.1, ::1) unless the setting allowRemote is on.
Lyrics, prompts and file names never go to the logs; only "job submitted" style messages do.
The seed is the block's derived seed (or the locked seed from the seed history); lm_seed and steps come from the block parameters.
The result records whether a melody was requested and whether vocal extraction ran; it does not claim that unverified MIDI conditioning was honoured by a particular server revision.
Errors become plugin errors with a plain message (server unreachable, HTTP status, answer not JSON, job failed, timeout), never a hang: the host's timeout and cancellation still apply.
Trying it without a GPU
node ample/plugins/acestep/stub-server.mjs prints a port and answers with a tone (modes: ok, nobase, error500, malformed, neverdone, noaudio). Point the setting endpoint at it. The stub models the request contract only; it cannot demonstrate audio separation quality.
Manual verification on the real server (to be run by the owner)
Needs a running ace-server (GPU in use, so confirm first) and a tunnel to it.
Confirm /props advertises a Base or SFT DiT model, or set vocalExtractionModel to one installed model file name (ace-server only accepts the name exactly as /props lists it; before 2026-10-03 the plugin cut off the .gguf and every extraction failed at once).
Make a project with one ai-voice block and a lyrics block, build it, and listen: the take should be the extracted vocal stem, the right length and mono.
Run ample verify-reproduction on the block: same means the server is reproducible for that seed on this machine (the plugin's determinism can then be recorded as verified-local); different means regeneration will always ask.
Note the server commit, the model files and the GPU in docs/kb with the date.
Isolating the voice: ace, demucs or off (plugin 0.4.0, 2026-10-03)
The setting vocalIsolation chooses how the voice is separated from the music that ACE-Step makes:
Value
What happens
Needs
ace (default)
A second ACE job with task_type: extract. Works with a light Base or SFT model (acestep-v15-base-Q4_K_M.gguf 1.45 GB, -Q5_K_M 1.70 GB, the same for sft) and with the heavy XL ones. Set vocalExtractionModel to pick one.
A Base or SFT DiT in the server's models folder
demucs
Demucs (htdemucs, two stems) runs on the CPU in a Python of your own: python -m venv demucs-venv, then pip install demucs soundfile, and set demucsPython to its python.exe.
The Python, about 2 GB for torch and the model
off
Take the synthesis as it is: fastest, may keep some music.
Nothing
Set modelsFolder to the folder of the server's models and the take records the sha256 of the model that was used (vocalIsolationSha256), so a sound can be traced to its exact model; for Demucs the hash is that of the checkpoint in the torch cache. All four light ACE models and the XL base model were tested end to end on 2026-10-03 (RTX 3070, 8 GB); the models come from Hugging Face Serveurperso/ACE-Step-1.5-GGUF and their sha256 matched the repository's. tools/start-ace-server.bat starts the server in a window of its own.
Choosing the model that makes the sound (plugin 0.5.0, 2026-10-03)
synthModel picks the ACE-Step model for the synthesis: default (an explicitly resolved turbo model, 8 steps), turbo, sft (cleaner and softer voices, about 50 steps, slower; the usual choice for singing), base (the raw model, about 50 steps), or the large xl-turbo, xl-sft and xl-base. For automatic family selection, the plugin lists the models the server offers (GET /props) and takes the best file of that kind: Q5_K_M first, then Q6_K, Q8_0, Q4_K_M and BF16. synthModelFile names one file exactly and wins. On the server backend, an explicit filename is sent directly even if /props omits it, so ACE itself can accept or reject the attempt with its actual response; on the CLI backend it must be present in modelsFolder. The steps parameter defaults by family (turbo 8, SFT and base 50). Both ACE blocks expose guidance as CFG, visibly resolved to 7 for Base/SFT and 1 for Turbo; that exact value is sent as guidance_scale and recorded in take metadata. Changing CFG can change the generated result, so use the same value for seed comparisons. shift is also exposed (SFT 1, Base/Turbo 3); solver defaults explicitly to euler. The model that was used is written into the take (synthModel). Model selection remains in plugin settings.
Backend: server, command line tools or both (plugin 0.6.2, 2026-10-17)
Generation recipes must survive the complete path into the immutable take; seeds alone are insufficient. See the 2026-10-17 near miss and review checklist. LM sampling is explicit: temperature 0.85, CFG 2, top-p 0.9, top-k 0 and an empty negative prompt. lmModelFile selects the LM explicitly for either backend; the legacy cliLmModel remains accepted. Otherwise an advertised 4B Q8 model is preferred, then a stable sorted selection. The resolved LM and synthesis requests, returned plan and available hashes are retained. These defaults are a reproducible baseline, not a listening-quality guarantee.
The setting backend says how the sound is made, and every song states it itself (the schema marks it x-songRequired; the project value is only the default for new songs, which is server):
Value
What happens
server
ace-server on endpoint (it must be running).
cli
The acestep.cpp tools ace-lm and ace-synth, run by the plugin (needs aceBinFolder and modelsFolder; lmModelFile picks the language model; the selection policy is described above).
server-then-cli
The server when GET /health answers, otherwise the command line tools.
Same seed, same models and same request give the same audio on the server and on the command line tools: on 2026-10-04 a 30 s demo (turbo Q5, language model 1.7B) gave the same sha256 for the server (twice) and for ace-lm plus ace-synth on this machine. Not checked: other models (sft, XL), other graphics cards or builds, a freshly started server; so the takes stay unverified. The take metadata records the backend that was used.
A song without acestep.backend gets the warning AMP2013 in the editors with a quick fix ("Add acestep.backend (the project default)", also in Fix all) that writes settings { acestep.backend server }; a build of such a song fails the block with AMP5370 and the same advice, which also appears in the build log.
Library manager
Status (2026-10-03): the service, the CLI and the API are built (chunk 85, see "As built" at the end); the screens come with chunk 77. The rest of this page is the design; where the built version differs it says so below. The clickable sketch (sketch/index.html, view Library) mocks the screens with invented data.
A song depends on things that are not in its text: sample packs, sound fonts, voices, plugins and their models, block templates, external data, imported audio, lyrics and chord files. The library manager lists all of them in one place, shows where each one is used, and lets you replace one by another (with an impact analysis), also per folder. It builds on the linked folders (mounts) from Tech: global catalogs reach AMPLE as read-only links, and the library manager is how you browse and use them.
1. What is in the library
| Kind | Examples | Comes from |
| --- | --- | --- |
| drum kit, synth pack, sound font, sample, audio import | kit/tape, felt-piano, old-take.wav | a catalog (a linked folder, global or project) or the project folder |
| text file | lyrics/chorus.lyr, chords/main.chords | the project |
| voice | anna, bo | a plugin (the voice engine) |
| plugin, model | acestep 0.9, acestep-turbo-q8 | installed, with their pins in ample.lock |
| block template | drums 1.2, piano pinned 2.0 | the template library |
| external data | weather.example/api (locked 2026-10-01) | recorded once, part of the project |
Every entry has a name, kind, version, hash (content fingerprint, strict, content or loose per Tech), size, where it lives (catalog or project folder, with the virtual path), access (read-only or read-write) and a status:
ok: present and matches the hash in ample.lock.
changed: present but different from the pinned hash. You can accept the new hash (a change to the working copy) or restore the pinned file from the catalog or a backup.
missing: not reachable (a link that points nowhere, a file that was deleted, a plugin that is not installed). Relocate repairs a link without touching the project (a machine-local setting).
2. Identity: every catalog and every asset has a uuid
Each catalog gets a uuid the first time it is linked (cat-7f3a-91c2). The uuid, not the path, is what projects, ample.lock and the usage index refer to, so a catalog can be moved, renamed, put on another drive or opened on another computer and everything keeps resolving. Only the machine-local mapping "this uuid lives at this path here" changes.
How a folder is recognised as that catalog: the uuid is written to a small marker file in the catalog root (.ample-catalog.json) when the link is writable at link time; for read-only media (a locked drive, a shared network folder) AMPLE cannot write one, so it keeps the uuid in its registry in Home together with a fingerprint of the catalog (a hash over the sorted file hashes, sampled). Relocating to a new path runs an identity check: marker uuid match, or fingerprint match (with a percentage when files were added or removed). A mismatch says "this looks like a different catalog" and asks before relinking.
Project assets get a uuid the first time AMPLE sees them (ast-3c1d-9b02), stored in .ample/ and in the usage index. A rename or move (a refactor) keeps the uuid; a copy gets a new one and records "copy of". Catalog assets get their uuid from the catalog index (a hash-derived id plus the catalog uuid), so the same file in two catalogs is two assets.
In the song and the lock: the song text shows names (readable); ample.lock stores ids and hashes (asset ast-3c1d-9b02 "kit/tape" @ cat-7f3a-91c2 sha256:...). Renaming an asset or moving a folder changes the name shown, not the id, so history, tags and sealed releases keep pointing at the same thing. Two catalogs with an asset of the same name are told apart by catalog uuid.
Duplicates and conflicts: two links that report the same uuid are the same catalog (a mirror); AMPLE uses the one that is reachable and warns when their fingerprints differ.
3. Global asset catalogs: linked folders ("hard links")
A global catalog is a folder (for example D:\Music\Samples) linked into AMPLE for every project; a project link exists for one project only. Links are AMPLE-level virtual links, not filesystem hard links (see Tech, "Linked folders (mounts)").
A link is read-only or read-write. A read-only link is never written to (by AMPLE or by plugins) and can never be an output folder. Catalogs are almost always read-only.
AMPLE indexes and fingerprints a catalog (content hashes, pack hashes, fingerprinter plugins; incrementally and cached), so the song can refer to an asset by id (shown as its name) and hash, and a changed or moved file is noticed.
The library manager shows catalogs with a link and a lock icon, their folders as a tree, a missing badge when the link is unreachable, and offers: Relocate (where is this on this computer), make read-only / read-write, Unlink (dangerous act: it lists how many uses would turn missing), Link a folder.
4. Reference or copy (and what a backup contains)
Using a catalog asset references it by id (shown by name) and hash; nothing is copied. That keeps the project small but means the asset must exist where the project is opened. Two ways to make the project travel:
Copy into project (per asset or folder): the files go into the project (packs/), the song can then point at the copy (use Replace), and backups and exports contain it.
Backup profiles decide what happens with referenced assets: by reference (only name, hash and size are recorded; restore says exactly which catalog assets are needed), or include the assets that are in use (vendoring): AMPLE copies exactly the used files into the backup. See Backup.
A restored project asks once where each named catalog lives on this machine (the machine-local setting), then verifies the hashes.
5. Where is it used? (the usage index)
Now: the working copy is scanned: block parameters that name the asset (a kit, a set, a voice, a file, a data source), block types for templates, needed plugins and models. Uses inside groups and templates are found too and show the full path (chorus_vox › ACE-Step render).
In history: a reverse index in history.db (asset_use(asset hash, revision, block, parameter)), updated when a revision is made, answers "in which revisions, on which lines, in which tagged or sealed releases is this used". Old revisions keep their reference; nothing in history changes when you replace something now.
Navigation: each use has Block (shows it in the node editor) and Source (jumps to the line); the properties of a block show a library button next to a parameter that names an asset.
Unused: assets nothing uses now. Project assets that are unused are cleanup candidates (see below); catalog assets are never removed by AMPLE.
6. Replace, with the impact analysis
Replace changes references in the working copy, never the catalogs, and never history.
Pick the asset, tick which uses to replace (all by default), choose a replacement of the same family (a drum kit by a drum kit, a sound font by a sound font, a voice by a voice, a lyrics file by a lyrics file).
The dry run shows: each use and its change (drums.kit: kit/tape → kit/808), the blocks that will rebuild (their cache keys change, so they and everything downstream turn grey), sealed or tagged revisions that still use the old asset (they keep it: a release stays reproducible), whether the replacement comes from a read-only catalog (referenced, never modified), and how to undo.
A thick warning (the shared dangerous-act dialog): it changes what the song sounds like; typed acceptance (the asset's name), a backup suggestion and an optional reason (kept in the operation log).
Undo: revert in the source editor, go back to the last revision, or replace it back. Replacing is not saved until you save.
Not replaceable by reference: block templates (updated per block with Update), plugins (installed, updated or removed in the plugin list) and models (they belong to a plugin).
Replace a folder: choose the folder or catalog that takes over; assets are matched by relative name and family; the dry run shows the mapping (pairs, and the unmatched assets that keep their old reference); then the same warning and typed acceptance. Useful when a library is reorganised or a better version of a kit pack arrives.
7. Other operations
Operation
Applies to
Notes
Relocate
a missing link
identity check by catalog uuid (marker file or fingerprint); machine-local; the project, ids and hashes stay the same
Accept the change
a changed file
records the new hash in ample.lock (working copy)
Rename, move (refactor)
project assets and project folders
the asset uuid stays; the names in the song are updated automatically (not a replace: the content is the same); not offered for read-only catalogs
Copy into project
catalog assets
makes the project self-contained for that asset
Remove
unused project assets
dangerous act: dry run, backup suggestion, typed acceptance; files go to the system trash; older revisions may no longer rebuild unless the take bank or a backup has them
Clean up unused (project)
all unused project assets
same dialog, listing everything
Verify all
everything
re-hashes and compares with ample.lock
Link a folder / Unlink
catalogs
Unlink lists how many uses would turn missing
8. The screen
┌ Library ─────────────┬ search [____] filter [all ▾] [Clean up unused] [Verify all] ┬ asset: felt-piano ──────────┐
│ All assets (31) │ name kind version where size status used │ sound font · 2.0 · 140 MB │
│ CATALOGS (linked) │ kit/tape drum kit 1.4 🔗🔒 Samples 64MB ● 1 use │ where: global catalog Samples │
│ 🔗🔒 Samples (5) │ felt-piano sound font 2.0 🔗🔒 SoundF. 140MB ● changed 1 │ status: changed (pinned 09-12) │
│ 📁 kit (3) │ grand-piano-v3 sound font 3.0 🔗🔒 SoundF. 620MB ● unused│ Used now (1) │
│ 📁 loops (2) │ ... │ ☑ piano · set [Block][Source]│
│ 🔗🔒 SoundFonts (3) │ │ In history: 8 revisions; v1.0 │
│ ✎ Project folder (3) │ │ [Replace...] [Accept change] │
│ ✎ Stems (missing) │ │ [Copy into project] │
│ INSTALLED │ │ │
│ Plugins, Models, Block templates, Voices, External data │ │
└──────────────────────┴────────────────────────────────────────────────────────────────────────┴──────────────────────────┘
Selecting a folder in the tree shows a summary (how many assets, how many in use, link path, access) and folder actions: Replace this folder with..., Relocate..., and Rename / move (project folders only).
9. Safety and integrity
The engine only serves paths inside the project and its links; .. never escapes; read-only links are never written, also not by plugins.
A catalog can change under the song (someone edits a file): the hash comparison reports changed, verify fails with a clear message, and builds that depend on it say so. The song never silently uses a different file than the one it was made with.
Replace and Remove are dangerous acts (shared dialog, see Project management); Relocate, Accept the change, Rename and Copy are not.
Should block templates also be replaceable in bulk (every piano 1.0 by piano 2.0) with the same impact analysis, or stay per block?
How are same-named assets in two catalogs shown and resolved (ambiguity, priority order of catalogs)?
Should a catalog be able to carry a manifest (names, versions, licences, tags) that AMPLE reads, so a catalog author can describe their assets?
Licences and credits: should the library record each asset's licence and feed the credits module (with weights) automatically?
How much of the reverse index is rebuilt on restore versus stored in backups?
Should the catalog marker file also carry a manifest (name, description, licence, version), so the uuid and the catalog's description travel together?
What happens when a catalog is forked (copied to a new place and then edited): a new uuid with "copy of", or an explicit "this is the same catalog" choice?
As built (chunk 85, 2026-10-03)
Where:Ample.Core/Library (Library, LockFile, UnlinkMountAct), the CLI ample library list|scan|link|add|relocate|verify and ample lock update|verify, and the API routes /api/library* and /api/lock* (see the API).
Thing
How it works
Catalog
One SQLite database per user (library.db in the home folder: AMPLE_HOME, else the local application data). Mounts and assets; project links carry the project id.
Mounts
Linked by a person naming a folder; nothing is found by itself. Read-only unless you say --read-write. A writable link gets a marker file .ample-catalog.json with the catalog uuid; a read-only one is never written to, so its identity is a fingerprint of the first thousand file names and sizes. State is worked out live: online, offline (folder gone) or other (a different catalog is at that path).
Assets
Uuid (ast-...) on first sight, kept across renames, moves and content changes (a changed file is a new revision, same uuid). Name = the path without extension (kit/tape), or name@version for packs and plugins. Kinds come from file names; only files of known kinds are opened, for hashing (sha256). Other files are listed by name and size and never read. Hidden folders and files are skipped.
Scan
Incremental: size and modification time unchanged means not read. A new file whose content was just lost from another path is the same asset moved. Gone files are marked missing, never deleted. Duplicates are found by hash.
Relocate
Identity check: the marker has the same uuid, or the fingerprint is the same, or at least 80 percent of the known assets are there with the same size; else AMP5292 unless forced. No song and no lock file changes. FindMoved looks for the marker two folders deep under given roots.
Read-only
Every write goes through ResolveWritable, which refuses read-only mounts (AMP5293), offline ones (AMP5290) and paths that leave the mount.
Resolution order
project folder (<name> or assets/<name>, with or without extension), the project's linked folders, the user's linked folders, installed packs. Missing: AMP5291 (AMP5290 when its folder is offline); pinned hash differs: AMP5294.
ample.lock
Written in a fixed order (assets, packs, plugins; by name) so the same project gives the same bytes: <id> "<name>" mount <catalog uuid> <hash> size <n>. update works out what the songs use (a parameter value that is an asset name, or starts with it and a /); verify says ok, changed, missing or offline per entry.
Packs
pack.json needs a name (letters, digits, -, _, .) and a version. Installed by copy into <home>/packs/<name>@<version> or linked where it is. The AI assistant can trigger this itself from a public .zip URL through ample_install_pack (GET /api/ai/context/install-pack): downloads it, extracts it, finds pack.json at the root or inside one wrapping folder, and calls the same copy-install path. Always needs the user's approval (an AiApproval of kind installPack), even when the link was in the request, since it changes the shared library rather than the task's disposable workspace; at most 200 MB.
Import
Copies an asset into assets/ of the project with a .provenance.json next to it (source asset, catalog, hash, time), as a command recorded in AMPLE Redux. Never over an existing file.
Unlink
A dangerous act (dry run, typed words unlink <name>, reason): the folder and its assets leave the catalog, no file is touched.
Usage index
For the open song: block, parameter and line of every use (the "now" half of section 5).
Measured (2026-10-03, Windows 11, Debug build, one machine): scanning 50,000 small files took 6.5 s the first time (all hashed), 1.8 s when nothing changed (none read) and 1.7 s with one changed file (one read); making the files took 26 s. With 5,000 files (the default of the test): 0.8 s, 0.2 s, 0.2 s. AMPLE_PERF=1 dotnet test ample/tests/Ample.Core.Tests --filter Category=Library repeats the 50,000 run.
Not built yet: the screens (77), file watching (a rescan is explicit), the reverse index in history.db (the "in history" half of section 5), replace with impact analysis and replace folder, accepting the change of one asset (a lock update accepts all), fingerprinter plugins, voices and models as catalog entries (they belong to plugins), rename and move refactors of project assets, the language's own diagnostic for a missing asset (no block parameter is typed as an asset yet, so a missing asset shows up when the build resolves it), and network mounts beyond what the identity check gives.
Opt-in modules and plugins
Status (2026-10-03): the reusable block modules (use, module files), the quality checks and the credits are built (chunk 94, see "As built" at the end). The opt-in feature switches, the plugin catalog and the settings screen below are still the earlier design sketch.
The AMPLE core stays small: text sources, a timeline, stable random, history, take bank, hashing and rendering through plugins. Everything else is a module that a project switches on, or a plugin (an external tool behind one contract). Nothing is installed or enabled by default except the core.
Enabling
In the project (.ample/ settings or at the top of an .amplet file):
features {
quality on # scoring, levels, rule packs
credits on # contributors, weights, declared vs measured
signing on # signed releases and tags (layers 5 and 6 of tag protection)
git-mirror off
secrets on # backup secrets sealed inside the backup
lyrics on # timed lyrics, checks, queries
assist diff # off | diff | button | auto
}
A module that is off adds no commands, no files and no checks. Turning one on is a normal revision. A project that needs a module the installed AMPLE does not have says so on opening ("needs: quality >= 1.2"), and still opens read-only.
Levels start weak and tighten. Experiment mode relaxes rules, marks results as tainted and expires. A human rating stays the gate for a release.
credits
Contributors, roles, per-block weights, declared versus measured splits
Records, not legal conclusions.
signing
Signed releases and tags, verify of who set what
The private key is not in backups by default. When on, tag layers 5 and 6 are on by default.
git-mirror
One-way mirror to git, full conversions both ways
Raw git objects are kept so the round trip is lossless.
secrets
Credential store with its own timeline
Sealed with a master passphrase, recovery key offered; see Backup.
lyrics
Timed words, checks, queries, own .amplelrc format and importers
Checks that need word timing need the alignment plugin.
assist
Proposals for text, timing and arrangement
A project setting for how much is automated: diff (shown as a patch), button (one click makes a revision), auto (applied on its own line, reviewed later).
Plugins (separate programs, opt-in per project)
Plugins talk to AMPLE through one contract; AMPLE never links against a model.
Transports: in-process (a .NET library), out-of-process (JSON-RPC over stdio), http, and an MCP adapter (untrusted by default).
Manifest (plugin.json): id, version, transports, what it offers, toolprint rules (what identifies the build), compatibility ranges, models it needs and where they come from, and the permissions it asks for.
Long jobs are async with progress and cancel. Artifacts travel by hash, never by path.
Models: often separate downloads (word timing, pitch, separation). A plugin declares them; AMPLE shows "not installed" and offers the install, and never silently falls back. A scorer that is missing shows as "not available" in the review grid.
Trust: plugins are pinned by hash in ample.lock; an updated plugin has to pass upgrade-check (does the release still reproduce) before it replaces a pinned one.
Versions and compatibility
Every module and plugin has a version, and a project records the range it works with. Compatibility groups are proven by canary renders, not just claimed (see Determinism and Tech). Format versions apply to .amplet, .amplelrc, .ample exports and plugin.json; readers keep unknown fields.
Open details
Which modules ship in the first version (suggested: core, lyrics, quality minimal, secrets).
Where the plugin and model catalog lives (per project, with an optional shared user-wide one, as for soundsets).
How permissions are shown and approved (network, disk, GPU).
Plugin settings screen
Every plugin has two entry points in the sidebar (and in the project settings), next to its enable checkbox, name and version:
ⓘ Properties opens a small dialog: what it does, id, version, status, toolprint, permissions, the models it needs (with missing ones marked), which blocks use it now, and a summary of its settings. From there: Show in the library or Advanced settings....
⚙ Advanced settings (only for an installed plugin) opens a full-screen editor that takes over the editor area, like the lyrics editor:
Tabs per plugin, declared in its manifest (for the voice renderer: General, Models, Pipeline, Performance, Permissions). Fields are typed (select, number, toggle, text) so the editor needs no plugin-specific code.
Ordered lists with drag and drop for things that have an order: model priority (the first enabled model that fits is used), pipeline stages (required stages cannot be switched off), file types to index, stems to produce. Rows can also be moved with the arrow buttons, and every list has Sort A → Z, Enabled first and Required first.
Undo and Redo, one step at a time (Ctrl+Z, Ctrl+Y or Ctrl+Shift+Z outside text fields), as long as there is something to undo; the buttons show how many steps are left. Reset to defaults is a step that can be undone too.
Two ways back:Apply and back (keeps the settings in the working copy) and Undo and back (throws the session's changes away, with a confirmation if anything changed).
Plugin settings are project settings (stored with the project, pinned alongside the plugin version in plugins.lock), so they travel with backups and exports and show as unsaved until Save. Machine-specific values (device, threads, install paths) belong to the machine-local settings, see Project management.
Permissions are part of the same screen (network access, reading the project and linked folders, writing only to the output folder). A plugin cannot get more than its manifest declares.
As built (chunk 94)
Reusable modules
A module is a file modules/<name>.module (project, linked folder or pack) and a song uses it with use <module>[@version] as <name> { ... }. The syntax, expansion rules, seed scoping and version pins are in language spec 07. In short: the tool expands every use into plain blocks named <instance>/<block> before building, so each instance has its own stable seeds; ample.lock pins module name, version and hash (AMP5328 when one changed); the editor keeps showing the short use.
ample modules list shows the modules of the project; ample modules add <file> checks a file and copies it into modules/; ample modules expand <song> prints the expansion and the problems (AMP5320 to AMP5327).
ample check, build, the variants and regeneration commands and the server's build routes all build from the expanded song.
Not done: the node editor does not draw a module instance as a node (the source editor shows it), a module cannot move its blocks on the timeline, and there is no sharing or catalog of modules (out of scope).
Quality checks
ample check quality [--take id] measures the newest take of every block that has audio (or one take) and applies the rules of spec/quality-rules.json:
Rule
What
Default limit
Q001 clipping
runs of three or more full-scale samples
none allowed
Q002 true peak
4x oversampled peak
-1 dBTP
Q003 loudness
integrated loudness (ITU-R BS.1770-4, gated) of the master
-14 LUFS within 1.5 LU
Q004 silence
longest quiet stretch inside the song (lead-in and tail excluded)
2 s
Q005 DC offset
mean value
0.01
Q006 sample rate
a final take not at 44100 Hz (previews are not checked)
-
Q007 tempo and key fit
needs an analysis plugin (chunk 96); reported as "not run" until then
-
Results are kept per take in .ample/quality/<take>.json (derived data, easy to make again). They are advisory. .ample/quality.json can set the target and tolerance, thresholds per rule, switch rules off and name the rules that must pass before a tag or a promote ({"require":{"tag":["Q001","Q002"],"promote":["Q001"]}}); then the newest take of the master block is measured at that moment and a failure stops the tag or promote with AMP5333.
Limits: only 16-bit PCM WAV (mono or stereo, mixed down) is measured; the true peak uses a cubic interpolation, not the 48-tap filter of the standard; there is no UI for the results yet (the data is in the files and the command output).
Credits
ample credits [--format md|json] [--out file] [--write] makes the credit sheet from the plans of the newest takes (plugin, version, voice, toolprint), the modules and the library assets that the songs use (with licence and credit as kept in the library), and the people in .ample/credits.json ({"usage":"commercial","people":[{"name":"...","role":"words","note":""}]}). Licence warnings (AMP5332): a non-commercial asset when the project has a release (a tag, or usage commercial), a no-derivatives asset, an asset without a licence. Advisory, not legal advice.--write keeps the sheet in .ample/credits/; ample export and the server's export refresh it first, so every export carries a sheet made from what was really used.
Analysis (chunk 96)
Tempo, key, loudness, spectrum, pitch and melody analysers and the analysis plugin contract are in Analysis. They are plugins by design (never in the core) and are not wired into the quality rules yet.
AMPLE Redux journal
Status (2026-10-18): AR record references, durable sequence allocation, bounded read-only search (including the Codex MCP tool), redacted assistant-task archives, opt-in session conversation capture, independent browser recovery drafts, project history preview and explicit retention controls are implemented. The History tab also exposes the open song or nested module's browser undo/redo states: preview is read-only, double-click confirms restore, Ctrl-double-click restores directly, and AR badges appear only for an exact text hash found in a durable pre-image record. Checkpoint preflights all source drafts and saves every matching dirty song/module work buffer before creating one snapshot. Existing projects continue using .ample/blackbox/ on disk for compatibility.
The journal and the Always rule
1. The idea
The history (lines, revisions, tags) holds the moments the producer chose to keep. AMPLE Redux is the append-only recovery journal underneath it. It records durable project changes and assistant-task artifacts so work can be inspected and recovered after a crash, a bad replace, a deleted block, a seed overwritten or an undo that went too far.
It is the second half of the Always rule (Decisions): the Always rule says a take never exists without its plan; AMPLE Redux says no change exists without a trace.
2. How it differs from the other layers
Layer
Who decides
Granularity
Lives
Shown
Editor undo/redo
automatic, per open song or nested module
one edit step; text and undo/redo stack are recoverable from this browser
browser recovery buffer, offered when that file is opened after restart; the recovery diff can preselect restore or saved version for other files
yes: Undo/Redo controls and the Edit history timeline
Automatic checkpoints (auto/...)
automatic, before risky moves
one per move, promote, update, migration
in the history
hidden by default
History (lines, revisions, tags, checkpoints)
the producer
one per Save
the project, kept forever (until cleanup or purge)
yes
AMPLE Redux
automatic, always
each durable record has an AR reference
.ample/blackbox/ (compatibility path)
recovery and read-only inspection
AR references (implemented)
Every journal record has a monotonic sequence and two representations: the canonical, copyable form AR-0000000000000001 and the compact UI form AR-1. IDs contain no date or suffix; timestamps are stored separately in UTC. The sequence high-water mark is persisted before appending a record, so a failed append may leave a gap but cannot make that number available again. The maximum sequence is 9,999,999,999,999,999. AMPLE's project writer lock remains the single-writer boundary; multiple read-only viewers can inspect the same archive.
3. What is recorded
Everything that changes the working copy or the project:
Text and structure: edits to .amplet and the other text files, block additions, deletions and moves, parameter changes, links, groups, the layout.
Seeds, freeze levels and plans: every seed change (with the old value), freezing and unfreezing, picked variants.
Time and placement: arrangement edits.
Library: replace, rename, relocate, copy, remove (with the full before state).
Settings: project, plugin and (non-secret) module settings.
Renders: every build start and finish, which take hash it made, and its plan (already stored by the Always rule).
Deletions: the full content that was deleted, not only the fact that it was.
History operations: go, promote, fork, drop, cleanup, tag and checkpoint moves (they are in the operation log too; the journal keeps the same facts in time order with everything else).
Never recorded: secret values (only the name and version of a credential, as everywhere else), transient keystrokes before save, and audio (takes live in the take bank; the journal only holds their hashes). The Edit history timeline can therefore show an AR badge only when the whole file-text state exactly matches a durable song or module pre-image stored in Redux; the lookup pages through the journal and an unrecorded browser-only step has no AR badge.
4. Format
Append-only entries in segment files (or a table): id, time, who, session, group id (one user action, such as "replace kit/tape by kit/808", is one group), kind, target (block id, file, setting), before and after (a small patch, or a content hash for bigger things), and a hash of the previous entry (a chain, so tampering and gaps are detectable).
Crash-safe: each entry is flushed before the change is reported done; a torn last entry is detected and ignored on the next start.
Snapshots: every N minutes or K entries a full snapshot of the working copy (a cheap one: file hashes and the text), so restoring a moment does not mean replaying everything from the beginning.
Small: text patches are tiny; the big things (takes, imports, baked data) are referenced by hash. A heavy week is expected to stay in the tens of megabytes (an estimate to measure, not a measurement).
4b. A separate store inside .ample/, built to survive when things go wrong
AMPLE Redux is its own store, not a table in history.db: it currently uses the compatible storage path .ample/blackbox/. If the history database is corrupted, AMPLE Redux is not; if Redux has a problem, the history is not. Bias: better too much than too little.
.ample/blackbox/
README.txt how to read this folder without AMPLE (plain text, always rewritten on open)
MANIFEST segment list, sizes, hashes and the head of the chain; rewritten atomically, MANIFEST.prev kept
current.log the open segment (append-only, flushed before a change counts as done)
seg-000123.log.zst sealed segments (compressed, checksummed, never edited again)
snapshots/ periodic full copies of the small important files (song, lyrics, lock, project.json, links, assets, plugins.lock)
db-backups/ a rolling daily copy of history.db (7 kept)
damaged/ anything that failed a check is moved here, never deleted
Self-describing and simple: JSON lines, each entry framed with a magic marker, a length and a CRC, plus the hash of the previous entry. A person (or a 50-line script) can read it; a damaged entry can be skipped because the next marker resynchronises the reader.
Write-ahead, fail closed for dangerous changes: the entry is written and flushed before the change is applied. If the write fails (disk full, permissions), destructive changes are refused (delete, replace, purge, cleanup, migrate) with a clear message; trivial edits continue but a persistent red notice says AMPLE Redux is not recording.
Capture generously: both before and after values (not only patches) for anything small; the raw command and its inputs, so a later bug in AMPLE cannot hide what was asked; a snapshot every 5 minutes while working, and always before a dangerous act, an upgrade or a migration; the content of everything deleted.
A second copy: sealed segments and snapshots are also copied to a mirror outside the project folder (<Home>/blackbox-mirror/<project uuid>/), so deleting or overwriting the project folder does not take AMPLE Redux with it. On by default, can be turned off, and says where it is.
Integrity checks: on open the tail is verified (a torn last entry is cut off, the bytes kept in damaged/); ample blackbox verify checks every CRC, the whole hash chain and the manifest; ample blackbox salvage reads what can be read past a damaged part.
Not tied to the clock: each entry has a sequence number, the wall-clock time and a monotonic time, so a clock jump or a time-zone change cannot reorder history.
One writer, many readers: a single writer per project (the instance lock); recovery tools and scripts read without a lock.
Permissions: owner-only files. Not encrypted by default; optional encryption at rest (records and mirror sealed with AES-256-GCM) is built, see Security. Never put .ample/blackbox/ in a cloud-synced folder (the health check warns).
Protected by AMPLE itself: nothing inside its retention window is deleted by AMPLE; "Delete project" lists its size in the dry run; a restore from a complete backup brings it back.
What can go wrong, and what saves you
Situation
What happens
Crash or power loss while writing
the torn last entry is detected and set aside; everything before it is intact; unsaved changes are offered back
Disk full
destructive changes are refused; red notice for the rest; nothing is silently lost
history.db corrupted
AMPLE Redux is separate; restore a daily db copy from db-backups/ or rebuild the working copy from snapshots plus entries
Project folder deleted or overwritten
the mirror in Home has the segments and snapshots
A bug in a new AMPLE version corrupts data
the raw commands and before-values are in AMPLE Redux; the snapshot taken before the upgrade is the way back
.ample/blackbox/ deleted by accident
the mirror; and the health check shows "AMPLE Redux archive missing" at the next open
Cloud-sync conflict copies
detected by the manifest chain; the health check warns and keeps both copies
Clock jump
sequence numbers keep the order; the jump itself is recorded
Power loss while sealing a segment
sealing is write-to-temp then rename; the open segment stays valid until the rename succeeds
5. Recovery (the only place it shows)
Hidden from the normal screens; reachable from Health, AMPLE Redux (advanced) and ample recover:
Timeline of groups ("12:41 replaced kit/tape by kit/808", "12:43 deleted block bass2", "12:47 seed of chorus_vox changed") with search ("everything that touched chorus_vox's seed", "everything deleted today") and filters by block, kind and time.
Restore a part, not the world: a block, a file, a parameter, a seed, a placement or a setting as it was at a chosen moment, into the working copy as an unsaved change (so it can be reviewed, saved or undone), or as a new line from that state.
Preview what a moment looked like (read-only, like peek) and compare it with now.
Automatic nudges: after a crash ("recovered N unsaved changes"), after a mass change ("12 blocks removed: recoverable from AMPLE Redux for 90 days") and when a dangerous act finishes, so the producer knows the net is there without having to think about it.
The journal is read-only history of the working copy: recovering never edits it, it only adds new entries.
6. Retention and cleanup (later, deliberately)
A policy, shown once in the project settings and the Health panel, adjustable:
| Age | Kept |
| --- | --- |
| first 90 days (default) | every entry, and never thinned during the last 7 days |
| 90 days to 1 year | one snapshot per hour plus every entry of a group that deleted or replaced something (their content is kept the longest) |
| older than 1 year | only what a revision, tag, checkpoint or release refers to, and the deletions' content |
Also a size budget (default 10 GB, adjustable): a warning at 80%, and only then is the oldest detail thinned. The defaults err on the side of keeping too much.
Takes are pinned by the journal. A take or baked block that the journal still refers to is not removed by gc or cleanup until its journal entries expire. That also rescues the variant you liked but did not pick.
Cleaning it up is a dangerous act when done by hand (ample journal prune --older-than 14d): dry run, impact, backup suggestion, typed acceptance, tombstone. The automatic thinning follows the policy and is logged.
Backups and exports: the journal is only in the complete backup profile and never in a partial export by default (it contains deleted content). A restore says whether a journal came along.
7. Privacy and safety
The journal holds deleted text (lyrics, notes) and old settings. It is local, in .ample/, never sent anywhere, excluded from sharing, and part of the "what would be lost" dry run when a project is deleted.
Secrets never enter it (see Project management); a credential change appears as "credential X rotated to version 3".
Because it is hidden, the first run tells the producer once that it exists, what it keeps and for how long, and where to turn it off or shorten it.
8. Testing
Replay test: replaying the journal from a snapshot must reproduce the working copy exactly, at random moments.
Crash test: kill the process at random points while changing things; the journal and the project must agree after restart.
Fuzz: random sequences of edits, deletions and replaces, with random restores, never lose data that the journal still holds.
Budget: writing an entry stays under a stated time per edit, so it is never felt.
9. Build order
It is a thin layer if the core is built around commands that change state (every change goes through one function that also writes the journal entry). So: the journal writer comes with the core (the same step as the history and the take bank), recovery tools come later.
10. Open questions
The original journal UI/recovery questions are resolved by AMPLE Redux (chunk 101): the user-facing name is AMPLE Redux; retention is a configurable size budget with warnings and explicit, previewed compaction; browser drafts cover multiple files; and the history view supports read-only visual preview and return to the exact current editor state. Encryption remains off by default, with optional encryption documented in Security.
Backup and restore (mock)
Status (2026-10-03): the backup store, the archive (export and import), restore, prune and the CLI and API are built (chunk 90, see "As built" at the end). The rest of this page is the earlier design sketch (profiles, encryption, release bundles); where the built version differs, "As built" says so. The screens come with chunk 77, the vault and encryption with chunk 92.
ample backup produces one zip. ample restore puts it back into a new folder (never overwrites). Everything below is a design sketch.
What is inside
my-song-backup-2026-10-02T1015.zip
├─ BACKUP-MANIFEST.json # backup id, ample version, profile, time, machine; sha256 per file; one root hash
├─ RESTORE.md # plain-language instructions (readable without AMPLE)
├─ project/ # sources (small)
│ ├─ song.amplet ample.lock rules/ cuts/ credits/ lyrics/ chords/ midi/
│ └─ .ample-project.json # name, master seed, format version
├─ store/
│ ├─ history.db # consistent copy of the SQLite history (VACUUM INTO)
│ └─ cas/ab/cd/<hash>... # frozen audio and stems by hash (the big things)
├─ releases/ v1.release v1.1-tweak.release club-remix.release [v1.bundle if the git mirror is on]
├─ snapshots/ v1.snapshot # full inventory with all hashes and toolprints
├─ ledger/ ledger.jsonl # hash chain of releases (+ last hash stored separately)
├─ takes/ bank.json pinned/...
├─ tools/ registry/ (manifests, toolprints, certificates, verification history) store/ (full profile, license permitting)
├─ packs/ # soundsets (full profile, with license info)
├─ exports/ v1/ master.flac stems/
├─ external/ copied/... MISSING.txt
├─ secrets/ # only when chosen, see below
│ ├─ secrets.enc # encrypted bundle of the selected secrets
│ └─ SECRETS-README.txt # what this is; there is NO password recovery
└─ meta/
├─ environment.json # OS, GPU, driver, ample version
├─ models.lock # hashes of used models (the models themselves are not included)
├─ secrets-index.json # names and where they are used (never values)
└─ EXCLUDED.txt # what was left out on purpose, and why
Profiles
Profile
Contents
Rough size
minimal
sources, lock, snapshots, ledger, take metadata
KB to a few MB
standard
+ pinned takes, releases, exports
tens of MB
complete
+ all takes, tool binaries, packs, copied external files
GB
Choose what to export in a checklist (also saved as a named profile): sources, takes, releases, assets, tools, packs, credits with contact details, and each secret separately.
Secrets
Secrets live in a vault outside the project (OS key store; on Linux an encrypted file). The project refers to them by name (secret:weather_api). Only secrets you declare count (tokens, passwords, private keys, URLs with tokens), never your whole environment.
Choice
Result
Secrets inside (suggested default)
secrets/secrets.enc in the zip, encrypted with your master passphrase. The rest may stay readable, or everything can be encrypted (--encrypt all).
Secrets in a sibling file
my-song-backup-....secrets.enc next to the zip, same backup id. Store it elsewhere (password manager, other disk).
No secrets
names and usage only, no values
The release signing key is off by default and must be ticked on purpose, with a warning: whoever holds it can sign in your name.
Encryption
Envelope: a random data key encrypts the secrets (AES-256-GCM or XChaCha20-Poly1305, authenticated); that key is wrapped with a key derived from the passphrase (memory-hard, e.g. Argon2id, parameters stored in the header).
No password recovery. Lose the passphrase and the secrets are gone. Optional recovery key: a random key shown once (words or QR) and stored offline.
The manifest hashes the ciphertext, never a plaintext secret (a hash of a weak secret can be brute-forced).
Prefer an existing, reviewed format (the age format, passphrase or public keys) over a home-made one, if a good library exists.
Honest trade-off: secrets inside the same file as the data is convenient, but whoever has the zip and the passphrase has everything. The sibling file splits that knowledge. When "inside" is chosen the tool prints one line: "Secrets are in this file, encrypted. Keep them separate instead?"
Restore without retyping
$ ample restore my-song-backup.zip
Passphrase: **** -> 4 secrets restored tested: weather_api ok, remote_token ok
Missing (not in the backup): ftp_pw -> [fill in now] [later]
One passphrase, then all secrets go back into the local vault automatically (re-protected for the new machine; machine-bound secrets cannot be copied raw, which is why they are exported into the encrypted bundle).
Anything missing appears in one screen: the name, where it is used, and a test button.
If the sibling file is absent, everything else is restored and only that list is asked for.
Checks
ample backup verify file.zip compares every file with the manifest and the root hash without unpacking into a project.
Reproducible zip: sorted entries, fixed timestamps, so identical content gives an identical zip hash.
Audio (FLAC) is stored without extra compression; above 4 GB use Zip64 or a split backup.
ample restore --verify checks all hashes after restoring and reports what is missing or not reproducible (frozen-only without a copy).
Not included (also listed in meta/EXCLUDED.txt)
Caches and temp files, logs, big models (hash only), drivers and system files (recorded as environment), and by default the full take bank in standard.
As built (chunk 90, 2026-10-03)
Where:Ample.Core/Backup (BackupStore, Archive, PruneBackupsAct, RestoreInPlaceAct, BackupPolicy), the CLI ample backup create|list|verify|prune|policy, ample export, ample import, ample restore, and the API routes /api/backup*, /api/export, /api/acts/prune-backups, /api/acts/restore-in-place (the health view also shows the last backup).
The backup store (a folder you choose)
<target>/objects/ab/abcdef... one file per distinct content (name = sha256), written atomically (.part then rename)
<target>/backups/<id>.json one manifest per backup, immutable, written last
<target>/verified.json which backups passed a verify (the "last good" record)
Incremental by content: a second backup stores only the files whose content is new; an unchanged project stores nothing (0 new files in the tests).
What goes in: every file of the project except .ample/locks, .ample/tmp, .ample/backups, .ample/vault*, *.secret, *.tmp and the SQLite journals. history.db is copied with the SQLite backup API, so the copy is consistent while the project is open. --no-takes leaves out .ample/takes and .ample/data.
Manifest (format 1): id (time and the start of the entry hash), project id and name, time, whether takes are included, and per file path, hash and size.
Resumable: an interrupted backup leaves only objects (and .part files); the next run reuses them and writes the manifest. Left-over temp folders are cleaned after an hour.
Space: the free space of the target is checked before anything is written (AMP5300).
Verify rehashes every stored file (missing and damaged ones are listed) and, when clean, records the backup as the last good one. Restore copies each file through a hash check and refuses damaged data; it goes into a new folder (AMP5301 if it is not empty).
Prune (--keep N, --daily D) is a dangerous act with a dry run; the newest backup and the last good one are always kept; files that no kept backup uses are removed.
In-place restore is a dangerous act: files are replaced or added, files that are not in the backup are moved to .ample/tmp/restore-trash-* (never deleted), the history comes back as it was. Close other programs that use the project first.
Before dangerous acts:ample backup policy --target <folder> --auto on (or /api/backup/policy) makes a backup before every dangerous act the server runs; a failing backup stops the act (AMP5305). The dry run of every act can tell how old the last backup is (BackupPolicy.Suggestion).
The archive (export and import)
One zip file (any zip tool reads it): manifest.json and files/<path>. The manifest is { "format": 1, "tool": "ample", "projectId", "projectName", "created", "includesTakes", "entries": [ { "path", "hash": "sha256:...", "size" } ] }. ample import checks every hash while extracting into a new folder (AMP5302, and nothing is left behind), refuses a folder that is not empty, rejects paths that leave the folder, and with --new-id gives the project a new id (the default keeps the id). Secrets and locks are never exported.
Measured (2026-10-03, Windows 11, Debug build, one machine)
A project of 40 MB of audio and a few small files: create 209 ms (191 MB/s), verify 193 ms, restore 208 ms.
Not built (and where)
Backup profiles (minimal, standard, complete) and the checklist of what to include (only takes in or out), encryption and secrets bundles (chunk 92), release bundles and the git mirror, remote targets, scheduled backups (only "before a dangerous act"), point-in-time restore from AMPLE Redux pre-images, RESTORE.md inside the backup, and the backup screens (chunk 77).
Encrypted projects (chunk 92, as built)
When a project is encrypted (Security), every object a backup stores is sealed (AES-256-GCM, frames of 1 MiB) and the backup folder gets a copy of the wrapped key as encryption.json (never the passphrase); verify and restore open the objects with the passphrase from AMPLE_PASSPHRASE. A folder that holds the key of one project refuses another project's key (AMP5314). Exports stay plain zip files; only their black-box segments stay sealed. Backups never contain the vault (vault.json lives in the library folder, outside every project).
Security: the vault, signing and optional encryption
Status (2026-10-03): built in chunk 92. This page says what is protected, what is not, and how to use it.
What is built
Part
What it does
Where
Vault
Secrets (API keys, tokens) live in one file of your library folder (vault.json), sealed with a key made from a passphrase. Projects and plugin manifests only carry the name of a secret. A plugin gets only the secrets its manifest declares, as environment variables, and the value is redacted from its captured logs.
You sign the newest record of a project's black box (or a file such as an export) with your own key. ample verify checks the hash chain and every signature: a changed record, a cut-off journal, a forged signature log or an unknown signer is reported (AMP5312).
Every record of the black box (and its mirror in your home folder) is sealed; backups made from then on are sealed file by file. A data key is wrapped by your passphrase; changing the passphrase re-wraps the key and re-encrypts nothing.
Passphrases and secret values are never arguments (they would show in process lists and shell history): they come from environment variables (AMPLE_VAULT_PASSPHRASE, AMPLE_PASSPHRASE, AMPLE_NEW_PASSPHRASE) or from standard input (echo the-secret | ample vault set NAME).
Cryptography used (no custom primitives)
Need
Choice
Why
Sealing
AES-256-GCM (.NET AesGcm), random 96-bit nonce per record and per 1 MiB file frame
Authenticated; part of the platform. XChaCha20-Poly1305 would need an extra library.
Passphrase to key
PBKDF2-HMAC-SHA256, 600 000 iterations, random salt
Part of the platform. Argon2id (memory-hard) would be better against GPUs but needs an extra library; revisit.
Signatures
ECDSA P-256 with SHA-256 (.NET ECDsa), IEEE P1363 format
The platform has no Ed25519.
Random
RandomNumberGenerator
Operating system source.
The threat model
Protected
A stolen laptop, a copied project folder or a backup drive in the wrong hands cannot show the black box (what was deleted, replaced or written, with the pre-images) without the passphrase, when encryption is on. Neither can the encrypted backup objects.
A secret never reaches a project file, a journal record, a backup or an export: commands carry the secret's name, never its value, and the vault file keeps values sealed. A canary-secret test scans the project, the black box, a backup and an export.
Tampering with the black box shows: every record carries the hash of the one before it (and a checksum), a signed head ties the chain to a key of yours, and AES-GCM refuses a changed sealed record.
Not protected
Anyone who has the passphrase, or the unlocked process, sees everything. While the program runs the key is in memory.
The song, lyrics, notes, the history database and the takes in the project folder are not encrypted (they are the work you share and the files other tools read). Use your system's disk encryption (BitLocker, FileVault, LUKS) for those.
Exports are plain zip files by design: they are meant to be shared. Their black-box segments stay sealed, so the receiver needs the passphrase for those only.
File names, sizes and the hash of each backed-up file are visible in the backup manifest (the objects are named by the hash of their plain content, so someone who already has a file can tell that it is in the backup).
The hourly summaries that thinning leaves of old black-box segments (hour, kind, count) are not sealed.
A signature proves that a key signed a head at some time and that the journal still contains that record unchanged. It does not prove when (the time is the signer's clock) or who holds the key.
Signatures of records that thinning later removed cannot be checked any more (they are listed as "no longer checkable", not as a problem).
A malicious process running as you can read the vault passphrase from the environment. The vault protects against theft of files, not against malware on a running machine.
Key loss
Lost passphrase and no recovery kit: the sealed black box and the sealed backups cannot be opened by anyone, including us. There is no back door. The song itself is not sealed and stays usable.
ample encrypt enable is a dangerous act: the dry run says this in plain words, and you must type encrypt this project and give a reason.
ample encrypt recovery-kit --out file writes a text file with the data key itself and the key settings. Keep it offline (paper or a drive in a drawer). Whoever holds it can open the sealed data without the passphrase, so it is as secret as the passphrase.
ample encrypt disable (also a dangerous act) writes the black box in the clear again. Backups made while it was on stay sealed; their folder keeps a copy of the wrapped key, so the passphrase still opens them.
A backup folder holds the key of one project: backing up a second project with another key into the same folder is refused (AMP5314).
How it works
.ample/encryption.json holds the format, the KDF settings, the data key sealed by the passphrase-derived key, and an HMAC key check. It holds no secret by itself.
A sealed black-box line is enc1: plus base64 of nonce, tag and ciphertext of the plain line; the hash chain and the checksum are over the plain lines, so verification works the same with or without encryption, and plain and sealed lines may be mixed (the switch is crash-safe). The journal opens the key from the passphrase source (the environment) and refuses to open an encrypted project without it (AMP5310).
A sealed backup object starts with AMPLEENC1\n, then frames of up to 1 MiB, each with its own nonce and tag.
Signatures are lines of .ample/signatures.jsonl: key id, public key, the sequence number and line hash of the signed record, the time and the signature over ample-sig1|project id|seq|hash|time. File signatures are <file>.sig.
Key rotation retires the active signing key (its private half is deleted) and makes a new one; retired keys keep verifying.
Not built (see the plan)
The operating system credential stores (Windows Credential Manager/DPAPI, Keychain, libsecret) are not used: the vault is a passphrase-sealed file on every system.
No UI yet for the vault, keys or encryption (the web UI shows nothing of it); everything is on the command line.
The data key is not rotated (only the passphrase). Re-encrypting with a new key means disabling and enabling again.
Secrets in a backup bundle (secrets.enc), a sibling secrets file and "restore without retyping" from the backup design are not built; backups simply never contain the vault.
Argon2id and XChaCha20-Poly1305 (see above).
Analysis
Status (2026-10-04): built in chunk 96; M19 adds project-scoped audio transcription review. Analysis lives outside the core, in Ample.Plugins, and never changes a take.
What is there
Analyser
Result
Method
Tolerance measured on synthetic fixtures
tempo
BPM, confidence, beat grid
onset strength (positive energy change) and autocorrelation, shortest strong lag, parabolic refinement
within 1 BPM for clean click tracks from 70 to 180 BPM
key
key (tonic and mode), confidence, chroma, three candidates
chroma from an FFT and the Krumhansl-Kessler profiles
right key for the C major, E major and A minor chord sequences of the tests; the relative key is the usual confusion
within 10 cents for tones from 110 to 880 Hz, also with 20 cents of vibrato
melody
melody plan: notes (pitch, start and length in beats on a quarter-beat grid, bar)
segmentation of the pitch contour; the tempo comes from the option {"tempo":BPM}, else from the tempo analyser, else 120
a synthesised five-note melody with a rest comes back as the same pitches on the same beats
These tolerances are measured on synthetic signals only. Nothing is claimed for real recordings until they are measured on the owner's own songs, outside the repository. pYIN (a probabilistic layer over YIN that copes better with real voices) needs a library or a Python plugin; the choice for now is plain YIN, recorded here so that it can be replaced behind the same schema.
Results, schemas and caching
Every result must fit spec/schema/analysis-<kind>.schema.json (the same small schema subset as the other spec schemas); a result that does not is refused (AMP5343).
ample analyze <take> --with <analyser> [--options json] stores the result in .ample/analysis/<key>.json and links it to the take in .ample/analysis/index.json. The key is a hash of the audio content, the analyser id, its version and the sorted options, so the same audio is analysed once, a new analyser version recomputes, and two takes with the same audio share a result. ample analyze --list lists the analysers.
The results are not stored as take data: the take bank counts data that no take owns as orphans and would quarantine them. A take, its plan and its data are never touched.
Analysers are pure: the same samples and options always give the same text (tested).
The melody plan and re-sing
The melody analyser writes {"schema":"ample-melody/1","tempo":T,"notes":[{"pitch":60,"start":0,"length":1,"bar":1,...}]}. Stored as data with the role melody of a take and pinned with freeze plan, it reaches the plugin as the input melody (see Controllability); the engine records its data id in the plan. A test builds a plan from a synthesised melody and feeds it through freeze plan into the echo test plugin. What a real model does with it (a re-sing that keeps the notes) depends on the plugin and is not claimed.
Analysis plugins (the contract)
A plugin of kind analysis lists the analyses it offers in analyses (its blocks may be empty) and answers the request analyze with { "jobDir", "analysis", "input": "in/audio.wav", "options" } by { "result": { ... } }. The host copies the take's audio into the job folder, checks the result against the schema and returns it (AMP5343, AMP5344). For file-producing analyses such as source separation, the response may also include outputs: [{ "role": "vocals", "path": "out/vocals.wav" }]; paths are relative to the private job folder and are rejected when missing, duplicated, outside the folder or oversized (AMP5272, AMP5274). plugins/analysis-fake exercises this contract with generated bytes. A real separation/transcription plugin is not bundled.
Permission: a plugin whose manifest needs the GPU or the network is never started unless the user says yes to the question the host asks (--allow-gpu, --allow-network on the command line; AMP5340 otherwise). The built-in analysers use neither.
Not built
The optional analysis-local-transcription plugin uses preinstalled Audio Separator and Basic Pitch packages, plus a user-selected, preinstalled separator model. Model weights and Python packages are not bundled or downloaded; network access is blocked. The compute-device choice defaults to GPU, with a CPU option, and GPU jobs require explicit permission. Configure its Python executable, complete local model folder and model filename in the transcription wizard's JSON options. See plugins. Real-model accuracy and runtime have not been measured here.
The UI uploads at most 10 minutes/128 MiB, shows progress, synchronized stem playback, a 16-bit WAV waveform and up to 8 MiB of returned vocal pitch/model-activity metadata, and keeps results under .ample/transcriptions/.
The review UI groups vocal pitch frames above a model-activity threshold into editable note estimates and can download them or import them as a MIDI source block. The local Basic Pitch adapter's displayed 0–1 value is the clamped note amplitude, not calibrated confidence or a probability. When the selected separator returns multiple accompaniment stems, the adapter estimates one MIDI artifact per stem and suggests an editable GM program from the stem label; it also emits a combined multitrack MIDI artifact for one-step import. A model that returns only a complete instrumental stem produces one backing estimate. Stem labels come from model filenames and are not instrument recognition. Drum stems are skipped by default because Basic Pitch does not map drum hits; the explicit drumMode: "pitched" option requests a rough pitched estimate, not a percussion map. Non-vocal MIDI artifacts can be downloaded or imported as MIDI source blocks. Raw vocal MIDI artifacts are download-only; vocal notes must be reviewed and accepted in the pitch lane before AMPLE imports them. Creating final instrument-block tracks from these estimates remains a user-reviewed conversion step.
The quality rule Q007 (tempo and key fit between blocks) still reports "not run": wiring the analysers into the quality rules is the next step.
No model weights are bundled, and pYIN is not implemented. The local model adapter reports estimates, not recovered ground truth.
Real instruments: sample drums and guitar
General MIDI tracks can also use the gm block and the licensed MuseScore General SF2 pack. Fetch the full GM bank with node tools/fetch-gm-soundfont.mjs --download; verify its pinned digest with --verify. The gm block supports project-contained SoundFonts and pack:musescore-general; converted MIDI modules use the pack alias. The SF2 renderer reads only samples selected by the requested bank, program, key and velocity zones and keeps all pitch, envelope and loop arithmetic integer-based. Modulators and SF3 are not yet supported.
Status (2026-10-03): built as part of chunk 98 follow-up. Tested in tests/Ample.Core.Tests/SamplerTests.cs (21 tests), web/lib/test/stepseq.test.mjs (the pattern grammar of the editors), web/lib/test/numfield.test.mjs (number fields) and in the browser (spec-tests/parity/samples.spec.ts for building, samples-ui.spec.ts for the editors).
The built-in drums, bass and piano are small synthesised sounds. The drums block (with a sample kit) and the guitar block play recordings that you keep in the project, and every knob stays text: the pattern, the strum, the chords, the swing.
Sample sets
A sample set is a folder of WAV files inside the project, for example samples/rusty-kit/ or samples/emily-guitar/. A block names it with kit (drums) or set (guitar); a name with a / that is a folder holding WAV files is a sample set, anything else (the built-in kit/tape, felt-piano) is the old synthesised sound.
Formats: 8, 16, 24 or 32-bit PCM and 32-bit float WAV, mono or stereo (mixed down), any sample rate (converted to the build rate with a fixed-point linear interpolation).
Where they come from: your own recordings or free ones. The demo project uses two CC0 sets by Karoryfer Samples (Swirly Drums and Emilyguitar, from the sfzinstruments collection on GitHub). Keep a samples/README.txt with the sources and licences; the credits sheet only knows what the library knows.
Layers and round robins on disk (2026-10-03): one flat folder stays. name.wav is layer 1, round robin 1; other files are name_v<L>_r<R>.wav (1-based, at most 3 digits, _ as separator because notes contain #; name_v2.wav and name_r2.wav alone are allowed; anything else is a plain name). NoteOf and the voice lookup read the base name. The engine still plays layer 1, round robin 1 only (a folder with extra files builds the same bytes as before), but every file is hashed into the plan, so adding one makes a new take. An optional set.json next to the WAVs (layersOffered, layersTaken, roundRobinsTaken) records what the source had and what the fetch took. GET /api/samples groups by base name and returns layers, roundRobins, offered and per voice voices; the picker says "N layers on disk, engine plays M".
In plans and the cache: the hash of every file of the set is part of the plan of the take (input kind sample, with the project path). Replace a sample and the block makes a new take; all other blocks stay cached. A take's plan says exactly which files made it.
Drums
drums beat stable@1.2 at 1 for 8 {
kit samples/rusty-kit
pattern kick:x.......x.....x. snare:....X.......X..o hat:x.x.x.x.x.x.x.x.
swing 12
humanize 25
}
The kit folder holds one file per voice, named after the voice: kick.wav, snare.wav, hat.wav (and any other name you use in a pattern: tom.wav, crash.wav, hat-open.wav).
The pattern is a name (beat-A, beat-B, which still work and use kick, snare, hat) or one grid per voice, voice:steps, separated by spaces. 16 steps make a bar; a shorter grid repeats (kick:x... is four on the floor of sixteenths). x is a hit, X an accent, o a ghost note, . a rest.
swing (0 to 60): how far the odd sixteenths are pushed later, as a percentage of a step.
humanize (0 to 100): a little deterministic variation in volume (up to 20 %) and timing (up to 5 ms), drawn from the seed of the block: the same song always sounds the same, another seed sounds different.
choke (optional text, template drums 1.3): rules separated by ;, each voice:closer,closer (for example choke open:hat,pedal). A hit of a closer voice cuts the sounding hits of the first voice from that moment, with a 7 ms fade. The hits are collected first and mixed in the order they were made, so no rule gives the same bytes as before. A rule without a colon or closers fails with AMP5351.
step velocity (drums renderer version 4): besides x (hit), X (accent) and o (ghost) a step may be a digit 1 (softest) to 9 (loudest), for example kick:9...5...9...3.... In the studio, shift+click on a step cycles through the digits 3, 5, 7 and 9, and the velocity lane under each voice sets any digit by clicking or dragging the bars.
voice options in the pattern (drums renderer version 4): a voice grid shorter than 16 steps repeats (a grid of 12 steps is a bar of three beats repeated; the step editor's length box writes this). A voice grid may end with @ and options separated by commas: m mutes the voice, l<dB> sets its level (-24 to 12), p<pan> pans it (-32 left to 32 right), s<swing> gives the voice its own swing in percent (0 to 60), t<semitones> tunes the voice up or down (-24 to 24, for toms and percussion), for example kick:x...x...@l-3,p-8,s30. A pattern with any panned voice renders stereo (equal-power pan table); without pan the output stays mono. An unknown option fails with AMP5351.
map (optional text, template drums 1.4): replaces voices of the kit by samples of other kit folders, entries separated by ;, each voice:folder/sample (for example map kick:samples/other/kick2;snare:samples/other/clap). The mapped kit folders go into the plan of the take. A malformed entry fails with AMP5351, a sample that is not in the folder with AMP5350.
fallback (optional text, template drums 1.4): a kit folder used for pattern voices the kit does not have. Without it a missing voice is an error (AMP5350); with it the voice plays from the fallback kit and the build warns with AMP5256.
A voice that has no file, or a grid with other characters, fails the block with AMP5350 or AMP5351 and says what the folder has or what is allowed.
Named patterns and sections (2026-10-08)
A drums block (and a guitar block, for its strum) can hold several named patterns and a song that puts them in order:
drums beat stable@1.2 at 1 for 16 {
kit samples/rusty-kit
pattern kick:x.......x....... snare:....x.......x... hat:x.x.x.x.x.x.x.x.
patterns verse=kick:x...x...x...x... snare:....x.......x... hat:x.x.x.x.x.x.x.x. / fill=kick:xxxxxxxx snare:xxxxxxxx hat:xxxxxxxx
song pattern*3 fill verse*4 fill
}
patterns: name=voice grids entries separated by /. Names are letters, digits, - and _; pattern, beat-A and beat-B are taken. The grids are the same as in pattern, with all voice options.
song: the sections in order, each a pattern name and optionally *bars (one bar when it is left out). The word pattern is the plain pattern of the block; beat-A and beat-B work too. Bar k of the block plays entry k of the sequence; the sequence repeats when the block is longer. Without song the plain pattern plays on every bar, as before.
A name in song that is not a pattern, a repeat that is not a whole number from 1 to 999 and a pattern defined twice are AMP5351. The built-in sound (no kit) only has beat-A and beat-B, so patterns need a kit.
For the guitar the entries are strums (verse=D...D.u.D.u. / fill=DdDdDdDd...) and the word pattern means the plain strum.
In the properties panel the grid has a pattern chooser (New, Duplicate, Rename, Delete) and a strip of sections (chips with their bars, move, remove, drag to reorder); the Patterns view (the button in the panel, the block menu) shows the lanes with the samples, the grid and the sections full width. Bass and piano have no pattern grammar of their own; they get one in the planned sequencer block.
Velocity layers
A voice or note may have several recordings for soft and hard playing. name.wav is layer 1, round robin 1; name_v<L>_r<R>.wav is layer L, round robin R (kick_v2_r1.wav, c3_v3_r1.wav). With N the highest layer number of a voice or note, a velocity v (0 to 32768) plays layer min(N-1, v*N/32769) (0 based, integer arithmetic); a layer without a file is played by the nearest layer that has one (the lower on a tie). A folder without layer files sounds exactly as before.
Drums:o is velocity 14000, x 26000, X 32768 (humanize changes it a little first).
Guitar:D 32768, U 26000, d 20000, u 16000.
Piano and bass: every note of the midi-clip has a MIDI velocity (1 to 127, default 96) that is scaled to 0..32768 for the layer and also scales the gain (96 is the old level). In a melody write it as a third field: c4:1:110. The bass uses the velocity of the lowest note of the bar.
Every layer file is hashed into the plan, so replacing kick_v2_r1.wav makes a new take.
SFZ sets (2026-10-15, Phase 1 "basis" of plan/sfz-import/README.md)
A set (not yet kit) value may name an .sfz file instead of a folder of note-named WAVs, for example set samples/karoryfer/unruly-drums.sfz. AMPLE reads the file's own opcodes instead of a filename convention, so a pack's own mapping is used as-is — no manual fetch-and-rename step is needed for a pitched instrument.
What is read today:<global>/<master>/<group>/<region> header inheritance, #include (verbatim, nested, relative to the including file's own folder), #define (textual substitution), sample (with default_path), lokey/hikey/key, pitch_keycenter (default: key, else lokey), lovel/hivel (0-127, scaled to AMPLE's 0-32768 velocity range the same way MIDI velocity is), tune (cents) and transpose (semitones, both rounded to AMPLE's whole-semitone Region.Tune), volume/gain (dB), seq_position (round robin position), group/off_by (SFZ03, drum kits only, see below), amp_velcurve_000..amp_velcurve_127 (a static velocity-to-amplitude table, linearly interpolated between the points given, holding the nearest point's level outside its range; applied on top of volume/gain at note time, replacing the default linear velocity response for that region only — layer/round-robin selection itself stays linear), and cutoff/fil_type/fil_veltrack (a static or velocity-tracked low-pass only: lpf_1p one stage, lpf_2p — the SFZ default when fil_type is left out — two stages in series, reusing the engine's existing integer one-pole filter; fil_veltrack (cents, -9600 to 9600) moves the cutoff at max velocity relative to velocity 0, using the same cents-to-ratio math pitch-shifting already uses; any other fil_type and resonance are kept-and-warned, no filter envelope yet).
Also read today (2026-10-15):lfo01_freq/lfo01_pitch/lfo01_delay (a pitch LFO, triangle wave only - forces the whole block through the per-frame expressive playback path); fileg_attack/decay/sustain/release/hold/depth (a filter envelope, same force-expressive note); trigger=release (a release-triggered companion region, see "SFZ sets and release samples" below) and, for a pitched set rendered by the piano block only, trigger=first/legato; xfin_lovel/xfin_hivel/xfout_lovel/xfout_hivel (a one-sided velocity-edge gain taper on the region RegionResolver.Choose already picked, not a true two-region crossfade); resonance (dB, an approximation - a gain bump just below the cutoff, not a true resonant filter pole); and, for a drums kit, off_time (overrides the block's choke fade) and polyphony/note_polyphony (caps simultaneous hits of one voice, first-come-first-served).
What is not read yet, and is kept-and-warned (AMP5394) rather than silently dropped:pan (no per-region pan field exists yet), offset/end (sample trimming), pitch_keytrack other than 100, non-low-pass filter types, trigger=first/legato for bass/guitar (see below), off_mode=normal, the <curve>-block form of curves, LFOs beyond the first/pitch-only, and every *_onccN opcode.
*_onccN (live MIDI CC modulation) is permanently out of scope, not "later":amplitude_onccN, cutoff_onccN, pitch_onccN and the rest exist for a live performer moving a controller while playing; AMPLE builds a song offline from a static, programmatic .amplet sequence, so there is no live CC stream at render time for these opcodes to read. They are read, kept and reported with AMP5394 like any other unread opcode — never silently dropped — so a pack that leans on them for its character is at least visible as "present but not applied." If AMPLE ever grows a live-performance mode this is worth revisiting, but it is not expected to.
Loops and amplitude envelopes:loop_mode=loop_continuous and loop_sustain both use AMPLE's sustain-loop playback; no_loop, one_shot and an absent mode do not loop. loop_start/loop_end are sample-native frame positions, rescaled to the build rate; an omitted end uses the whole decoded sample. loop_crossfade is in seconds. AMPLE does not distinguish continuous from sustain looping after note-off. ampeg_attack/decay/release/hold (seconds) and ampeg_sustain (percent) configure the region's integer ADSR (hold is a plateau at full level between attack and decay); an absent envelope leaves the old fixed fade. These apply to pitched set regions; filter envelopes and release-triggered samples remain open. See plan/sfz-import/README.md.
A kit (drums) value may also name an .sfz file (SFZ05): each region's key is mapped to a voice name through the General MIDI percussion map (36→kick, 38→snare, 42/44→hat, 46→hat-open, 49/52/55/57→crash, 51/53/59→ride, every other tom key→tom, ...) instead of becoming a pitched region — this is a real, documented standard, not a guess, but several related GM keys deliberately collapse onto one AMPLE voice name (every tom key becomes tom). A key the map does not know becomes key<N> (still addressable in a pattern, just without a friendly name) and warns (AMP5394). A region needs key/pitch_keycenter/lokey to be read at all.
group/off_by (SFZ03, drum kits only): every region sharing a group number is closed by a hit of any region declaring that number as its off_by — checked at region precision (SFZ12, 2026-10-16): the choke-and-fade mixing pass looks at each hit's own resolved Region.Group/Region.OffBy, so when one AMPLE voice name covers more than one region with different group membership (for example a soft, ungrouped articulation layered with a hard, grouped one on the same key), only the hit whose own region is actually in the group gets cut — not every hit of that voice name. SfzReader still also derives the older voice:closer1,closer2 text form into its result (for the documented wire format), but the drums block's own hand-written choke parameter and the SFZ-sourced region choke are now two independent checks feeding the one mixing pass, not one merged into the other. off_mode=normal is not read yet (kept-and-warned); off_time overrides the block's whole-block choke fade (see above).
Paths are root-jailed, one level wider than the .sfz file itself (SFZ06): every #include and sample= path must resolve inside the whole project (a project-relative set/kit value) or inside the matched pack's own folder (a pack: value) — never anywhere else on disk. This is wider than just the .sfz file's own folder tree on purpose: a real pack's programs and samples commonly sit in sibling folders (confirmed against Karoryfer's "Frankensnare", whose Programs/*.sfz reach ../Samples/...), which a same-folder-only jail would reject outright.
Only WAV (PCM or float) samples are read, same as a plain folder set — a real pack's samples are often FLAC; fetch-audio-packs.mjs's own ffmpeg conversion is still the only path to use a FLAC-only pack's audio with AMPLE today.
In plans and the cache: the .sfz file itself, every file it #includes and every sample it references are each hashed into the plan (kind: sample), exactly like every WAV of a folder-based set today — change any one of them and the block makes a new take, changing an unrelated file next to them does not.
A damaged or missing .sfz file, or one with no usable region, fails the block with AMP5350 (wrapping the underlying AMP5395 detail).
Per-set gain and tuning
An optional set.json in the sample folder, { "gain": -3, "tune": 1 }, evens out packs: gain is whole dB (-24 to 12) applied to every region of the set, tune is whole semitones (-24 to 24) by which the recordings sound sharp (they are played that much lower, drums included). Block parameters (gain, strum, pattern) stay as they are on top. The file is hashed into the plan (set.json as a sample input), so editing it makes a new take; a file that is not such an object fails the block with AMP5350. No file means gain 0 and tune 0, unchanged bytes.
Sustain loops and an integer envelope (S08, 2026-10-15)
set.json may also carry two optional, independent entries. Neither changes a set that does not use them: without them a region's LoopMode stays none and the note fades the same fixed way it always has.
loops, per base name (the same name a .wav/_v<L>_r<R>.wav or an SFZ region resolves to): { "loops": { "arco": { "start": 1000, "end": 5000, "crossfade": 200 } } }. start/end/crossfade are sample frames at the project's build rate. Playback runs the raw recording from its own start up to end; once a note needs more frames than the recording has, it wraps back to start and repeats forever, blending the last crossfade frames of each pass into the material just before start so the wrap point is not an audible click (LoopPlayer, already used standalone before this). A note shorter than the recording is unaffected — only a note that actually runs out of recorded audio ever loops.
envelope: one integer ADSR for the whole set, in whole milliseconds and a 0-100 sustain percentage: { "envelope": { "attack": 5, "hold": 0, "decay": 50, "sustain": 80, "release": 200 } }. Applied per note in place of the renderer's usual fixed fade (today: play the recording, then a fixed ~120 ms linear fade-out): attack ramps from silence, an optional hold plateaus at full level, decay eases to the sustain level, the note holds at sustain for as long as it is held, then release fades from wherever the envelope had reached. An explicit per-note rel= (note expression) always wins over the set's envelope for that one note — a performance override is more specific than the sample's own authored envelope.
Where this is wired in today: the guitar/bass/piano sampled-set note paths (PlayNote/PlayExpressiveNote, including .sfz-sourced pitched sets). The drums kit voice path does not read loops/envelope yet — sustained drum voices are not a common real-world need, and pattern's own o/x/X/digit scheme already shapes each hit's level and length.
Every region's loop/envelope choice is deterministic and integer (no floating point, no real-time clock): the same song and seed render the same bytes on every machine, exactly like the rest of the sampler.
Round robins
Several files for the same voice, note and layer (kick_v1_r1.wav, kick_v1_r2.wav, ...) are different recordings of the same hit. Each hit picks one of them from its own deterministic stream: a StableRng seeded with the block seed xor a constant xor a hash of the voice name (drums: one stream per voice; guitar, piano and bass: one stream for the notes). It is a different stream from the humanize one, so the humanize timing and volume of existing takes do not change. The streams start again at every render: the same song gives the same hits on two runs and on another path, another seed gives other choices. The renderers of drums, bass, piano and guitar are version 2.
Guitar
guitar gitaar stable@1.0 at 1 for 8 {
set samples/emily-guitar
strum D...D.u.D...D.u.
voicing open
stagger 22
gain 0
from chords.notes -> notes
}
The set folder holds recorded notes named by note: e2.wav, a2.wav, c3.wav, eb3.wav, gb3.wav, c#4.wav (C4 is middle C). A note that is not recorded is made from the nearest recording, played faster or slower (like a tape), so a set with a recording every third semitone is enough.
The chords come in as notes (the notes input, for example from a midi-clip). The root of each bar's chord decides the strings.
voicing:open (five strings: the root low, the fifth, the octave, the third above that and a high fifth), triad (root, third, fifth around middle C) or power (root, fifth, octave).
strum: 16 steps per bar; several bars separated by | cycle by bar number. D a loud down-strum, d a soft one, U a loud up-strum, u a soft one, x mutes the strings, . lets them ring. A strum plays the strings one after the other, stagger milliseconds apart (0 to 80, default 22); an up-strum plays them in the opposite order. Strings ring until the next strum.
gain in dB (-24 to 12).
A set without note-named files, a strum with other characters or an unknown voicing fails the block with AMP5350 or AMP5351.
In the web UI
Select a drums or guitar block and the properties panel shows a step editor above the parameters (the same text parameters stay below it, and the source stays the truth):
Drums: a sample kit chooser (the folders of the project that hold WAV files; the built-in sound is listed as such) and, for a sample kit, a grid with one row per voice and 16 steps. A click cycles a step through rest, hit (x), accent (X), ghost note (o). Add a voice of the kit, remove one, start from a preset (beat-A, beat-B) or clear the grid; a voice without a file is struck through in red with the message the build would give. Typed number fields (with arrows, and optional sliders, see Web UI) write swing and humanize; the guitar has stagger and gain.
Guitar: a sample set chooser (folders with note-named files), a strum row (rest, D, d, U, u, x per step), extra bars for a pattern that changes per bar, and the voicing.
A pattern or strum that cannot be read as a grid is not hidden: the panel says why and offers a reset; the text field stays editable.
The editors are disabled while the block is locked or the song has errors, like the other parameters.
Determinism
Internally a folder becomes an Instrument (a list of immutable Region records: key and velocity range, root, tune, gain, loop fields, group, sequence position, sample) and RegionResolver.Pick / PickVoice choose the region for a note or voice, velocity and counter. Drums, guitar, piano and bass all play through it (the guitar shares the nearest-note code of piano and bass). One file is one region, so sound and bytes are unchanged.
All of it is integer arithmetic: the same files, the same song and the same seed give the same samples on every machine (the pitch ratios come from the decimal twelfth root of two, not from floating-point maths). The renderers say so in their toolprint (ample-sampler).
Words from a file
A lyrics block names a file with file lyrics/song.lyr. When that file exists and is not empty, its lines are the words (without the [bar N] markers); otherwise the text parameter is used (lines separated by /). The hash of the file is part of the plan, so editing the words in the lyrics editor makes the voice a new take, and the web UI marks the build as out of date through GET /api/doc/inputs.
Not built
No audition of single samples and no waveform view in the UI yet: sets are folders on disk, and the step editors only write the text parameters.
Sample sets are looked up in the project only (not in linked folders or packs yet).
The demo packs still hold one hit per note (the mf round robin 1 and the vl8 hits); layers and round robins are for packs that bring them.
The bass and piano blocks still synthesise; a sampled piano is the same as the guitar block with another set.
SFZ reading (see "SFZ sets" above) does not yet cover release-triggered samples, filter modulation/envelope, LFOs or <curve> blocks; plan/sfz-import/README.md tracks the remaining work.
The sequencer block
Status (2026-10-08): built (plan fl-block). An advanced block: the node editor and the source stay the main ways to work.
A sequencer block is a small studio in one block: channels (the sounds), patterns (what each channel plays), a playlist (which patterns play when) and lanes (extra outputs that carry the channels you pick). Everything is text, in a project file sequences/.seq that the block names with file.
sequencer sq stable@1.0 at 1 for 8 {
file sequences/groove.seq
lanes lane1=kick+hat lane2=bass lane3=lead
}
The file
sequencer 1
channel kick drum sample=samples/kit/kick.wav vol=-2 color=#e8505b
channel bass bass vol=-6
channel lead notes wave=triangle set=samples/felt shift=0
channel pad loop file=samples/loops/pad.wav bars=2
pattern verse bars=2 color=#4a90d9
kick x...x...x...x...|x...x...x..xx...
bass 1:0:1:45:100 2:0:2:43:100
lead 1:0:1:69:96 1:1:1:72:96
pad on
playlist
clip verse track=1 at=1 for=4
section Verse at=1 for=4 color=#4a90d9
Channel types: drum (a WAV file and steps), loop (an audio file that starts every bars bars), notes, bass and piano (the compact notes of the piano roll, played by a built-in tone, wave=square|triangle|saw, or by a sample set, set=samples/folder; shift moves the pitch in semitones), guitar (a strum row over chords, played from a pitched sample set: set=samples/gtr voicing=open|triad|power stagger=22) and gm (notes played by a General MIDI SoundFont preset: file=pack:musescore-general or a .sf2 of the project, program=0..127, bank). Common settings: vol (dB), pan (-32 to 32), mute=on, solo=on, color.
Guitar row: the strum (16 steps a bar, D d U u x ., bars separated by |), then the word chords and one chord name for each bar: D...D.u.D.u.D.u. chords Am F. Chord names: C, Am, F#m, Bb, G7, Cmaj7 (minor when the name has m, otherwise major).
Rows of a pattern: a drum row has 16 steps a bar (x hit, X accent, o ghost, 1 to 9 velocity, . rest), bars separated by |; a shorter row repeats. A bar written with 12 characters is a bar of eighth triplets, with 24 sixteenth triplets and with 32 thirty-second notes (so x..x..x..x.. is a triplet figure); a bar may use a different grid than the next. A notes row is the text of the piano roll (bar:beat:length:pitch:velocity, the bar inside the pattern). A loop plays in a pattern that has a row for it (pad on).
Length: a sequencer that is placed (at 1 for 4) is as long as its placement; one that is not placed is as long as its playlist (the end of the last clip), not 8 bars.
Playlist: a clip puts a pattern at a bar for a number of bars; when the clip is longer than the pattern the pattern repeats. A section is a named band and a marker (marker Drop at=9) a named flag: neither changes the sound. A loop (loop at=1 for=4) makes the bars from its start on play the loop bars again and again, to the end of the block.
Variations:pattern verse-b from=verse starts with the rows and the length of verse; its own rows replace the row of the same channel, and a row that is just - silences that channel. A variation starts from a plain pattern (one level); in the workspace the Variation button makes one and an inherited row is drawn lighter.
Lines starting with # are comments: they stay above the line that follows them (and at the very top and end of the file) when the editor writes the file, and go away with that line when you delete it; blank lines are not kept. Mistakes are AMP5380 with the line.
Lanes
The block has the output mix (every channel) and lane1 to lane8. lanes lane1=kick+hat lane2=bass lane3=all says which channels each lane carries (channels joined by +, all for every channel). Only lanes that name channels come out; connecting a lane that carries nothing stops the block that reads it (AMP5251) instead of silently giving the mix. Change what a lane carries by changing one line. A lane can be rendered to a file on its own.
The workspace
Select the block and press Open the sequencer (or use the block menu). The channel rack lists the channels with colour, type, mute, solo, level, pan and the sample, loop or tone; the pattern tabs hold the patterns (New, Duplicate, Rename, Delete, bars, colour) with a step grid for each drum channel, a Piano roll for notes channels and a switch for loops; the playlist has tracks with clips (click an empty spot to put the selected pattern there, drag to move, drag the right edge to resize, Delete removes) and section bands; the lanes strip has a chip per channel for each lane and Render to file per lane. Apply and back writes the file; Undo and back leaves it as it was; the save control and Ctrl+S work as everywhere. Recording into a channel is the piano roll's MIDI in.
Playing in the workspace
The bar has a transport: Play (builds first when the file changed), Stop, Repeat (keep looping) and Live. Drag over the ruler of the playlist to choose the bars that repeat (a shaded band; the ✕ next to the label clears it), click a bar to start from there (also while it plays), press ⟳ on a section band to repeat that section, or ⟳ Play pattern next to the pattern tabs to repeat the first place of that pattern. The playhead runs over the playlist. The loop plays through an audio element (web/app/loopplayer.js) and jumps back within a few milliseconds. With Live on, a change made while it plays is built in the background after a short pause and the new take replaces the old one exactly when the loop comes round again, never in the middle of a bar. This is render-and-swap, not synthesis in the browser. Closing with Undo and back puts the file back as it was before the live builds wrote it.
Keeping the sequence in the song
The sequence can live in the song itself instead of a file: a sequence { ... } part of the block holds the text of a sequence file (every line indented two more than sequence {; the closing brace stands at the indent of the opening line):
It is used instead of file. In the sequencer workspace Keep in the song moves the sequence of the file into the block (the file stays where it is, the block just stops using it) and Extract to a file… writes it to a new .ampleseq file of the project and lets the block use that file again. The workspace opens and writes a sequence in the song the same way as a file. New sequence files get the extension .ampleseq; a .seq file of before still works.
Not built yet
Automation clips on the playlist, time-stretched loops (a loop plays at its own speed), playback markers that start playing at a point (the loop range is built).
Score import: from a MIDI, MusicXML, MuseScore/Guitar Pro or PDF/image score to a sequence
Status (2026-10-15): built (tool ample_import_score). For the assistant and for people.
A score that exists as MIDI (.mid) or MusicXML (.musicxml, .xml, or zipped as .mxl) becomes a sequence file that AMPLE can play and edit, without a conversion script. A MuseScore, Guitar Pro, Capella or Overture file (.mscz, .mscx, .gp, .gp3-.gp5, .gpx, .ptb, .ove, .cap, .capx, .bww) is converted to MusicXML with MuseScore when it is installed - see "MuseScore/Guitar Pro/Capella/Overture files" below. A score that only exists as a PDF or image (.pdf, .png, .jpg, .jpeg, .webp, .tif, .tiff) is recognized first with Audiveris (optical music recognition, OMR) when it is installed - see "PDF and image scores (OMR)" below.
What you get
ample_import_score { path, out?, bars_per_pattern? } writes a new .ampleseq in the proposal:
one channel per part: channel violin1 gm file=pack:musescore-general program=40 bank=0 vol=-6 pan=0 (the General MIDI program of the part is kept; a drum channel uses bank 128);
the notes as rows bar:beat:length:pitch:velocity in patterns of bars_per_pattern bars (default 8), named part-1, part-9, ...;
a playlist that plays the patterns in order (clip part-1 track=1 at=1 for=8).
The answer says how many parts, notes and bars there are, the tempo of the score (set the project tempo to it) and warnings for what could not be kept.
What it keeps and what it does not
Kept
Not kept (a warning says so)
Parts and their program, pitches, start and length in beats, ties (joined), chords, rests, simple repeats (a backward repeat jumps back once)
Time signatures other than 4/4 (a bar here is four beats), tempo changes (the first tempo is used), first and second endings (played in written order), dynamics (all notes at velocity 90 for MusicXML, the written velocity for MIDI), grace notes, ornaments and articulation
How to use it in a song
Put the score file in the project (or fetch it with ample_fetch_url; Mutopia and similar sites offer MIDI and MusicXML).
Set the project tempo to the tempo from the answer. A song is as long as the playlist; a part may reach bar 1024.
Change instruments, volumes and lanes in the sequence; make it sound the way the song needs (see sequencer).
MuseScore/Guitar Pro/Capella/Overture files
When path ends in one of MuseScore's own read formats, AMPLE runs MuseScore (<file> -o <file>.musicxml) and imports the result - this is a straight, machine conversion of the file's own note data (not recognition), so it is as reliable as the source file:
Install MuseScore (not bundled with AMPLE): the official installer works; a portable build (its .paf.exe accepts /S for a silent self-extraction next to itself, or use the PortableApps.com build) needs no admin rights. Set AMPLE_MUSESCORE to the resulting MuseScore4.exe, or put it on the PATH. ample doctor --env reports whether it is found.
A .mxl (zipped MusicXML) needs neither Audiveris nor MuseScore: AMPLE unzips it directly.
PDF and image scores (OMR)
When path ends in .pdf, .png, .jpg, .jpeg, .webp, .tif or .tiff, AMPLE runs Audiveris on it in batch mode and imports the MusicXML it recognizes:
Install Audiveris (not bundled with AMPLE): its Windows installer needs admin rights for a normal install; without them, extract it per-user with msiexec /a Audiveris-x.y.z-windowsConsole-x86_64.msi /quiet TARGETDIR="<a short path>" (a long TARGETDIR can fail with "Error writing to file" - Audiveris's own class files have long names). Set AMPLE_AUDIVERIS to the resulting Audiveris.exe, or put it on the PATH. ample doctor --env reports whether it is found.
It can be wrong. OMR reads printed symbols, not music: a scanned or low-quality source, unusual notation, or a lot of text can come out with missing, extra or mis-pitched notes. The answer's next note says to check the result; always look at the note count and a few bars against the source before trusting it.
Getting a PDF into the project first: a score fetched from the web lands outside ./project (ample_fetch_url's assets/web/); stage it into the project with ample_copy_file (source: "workspace/assets/web/<file>") before importing it.
Large or dense scores can take a few minutes; Audiveris is stopped after 3 minutes with a timeout message if it has not finished.
Tips
Long scores: more bars_per_pattern makes fewer, longer patterns; fewer makes it easier to rearrange sections.
The import never overwrites a file: give another out or change the file with ample_edit.
Release
Status (2026-10-03): chunk 98 is partly done. The parts that can be done and proven on the development machine are done; the rest needs a machine, a runner or a decision that was not there. This page says exactly which.
Release criteria
Version
Needs
State
v0.1
milestones M1 to M3 done
done (chunks 00 to 60 and the engine)
v1.0
M4 and M5 done, parity complete, no open critical risk, security checklist signed off, Linux suite seen green, release artefacts verified
not yet: see "Open items"
The spec version stays 0.1 (draft) on purpose: it becomes 1.0 only with v1.0.
What is built and measured
Item
Result
Parity
34 of 34 sketch features have an executable test (node tools/parity-report.mjs --check --require-complete, part of check-all)
Chaos
Category=Chaos: a crash injected at every mutating step of five kinds of command (file write, settings, dangerous act...) leaves either the old or the new file, a verifying black box and nothing unresolvable; clocks that jump back and forward 10 years; 3000 files through backup, verify and restore
Disk full and read-only media
the journal fails closed (AMP5230) and the command does not run (BlackBoxTests), a failing durable write leaves a truncated file that is never mistaken for a record
Performance gate
Category=Performance against docs/perf-budgets.json (below)
Packaging
tools/package.ps1: self-contained single-file zips for win-x64 and linux-x64 (with web/, spec/, docs/ and the real plugin next to the program; the test plugins are left out), the dotnet tool (Ample.Cli.0.1.0.nupkg), the VS Code .vsix, SHA256SUMS.txt, optional signatures (-Sign, see Security)
Installer smoke test
tools/smoke-package.ps1 unpacks the zip of this operating system into a clean folder outside the repository and checks --version, new, check, build, plugins list and that serve answers with the web UI and the help. Passed on Windows 11 (2026-10-03).
Performance baselines (measured 2026-10-03, development machine, Windows 11, not a CI runner)
Measurement
Baseline
Budget
parse a song of 2000 blocks
33 ms
500 ms
format it
21 ms
500 ms
1000 black box appends (each one made durable)
2359 ms
25 000 ms
read and verify 1000 records
26 ms
500 ms
build 41 blocks (preview)
1095 ms
15 000 ms
the same build again, all from the cache
161 ms
3000 ms
list 1500 takes
29 ms
1000 ms
back up 64 MiB in 64 files
591 ms
10 000 ms
verify that backup
650 ms
10 000 ms
a second, unchanged backup
285 ms
4000 ms
Denoise baseline (measured 2026-10-17, development machine, Windows 11)
Measurement
Before
After
Budget
auto denoise, 60 seconds of synthetic stereo audio at 44.1 kHz
10 812 ms
10 348 ms
120 000 ms
The optimized output is byte-identical to the previous renderer for both noise=auto and noise=start. The measured auto runtime improvement was about 4% on this run; this is a small gain, while flattening the stored spectra also removes thousands of per-frame array allocations. A known noise=start profile avoids analyzing every frame before the render pass.
The performance tests run alone (an xUnit collection that is not run in parallel): in the first full run, while other test classes used the same disk, the numbers were twenty times worse (23.7 s instead of 1.1 s for the build, 51 s instead of 2.4 s for the appends), which is why the gate must not share the machine with other tests. The budgets are about ten times the baseline (more for the disk-bound ones), so a slow runner passes and a real regression fails. Raise a budget only with a line here saying why. Not measured yet: the LSP under load, the web UI with a very large graph, a 10 GB take bank.
Package sizes and times (2026-10-03)
Artefact
Size
Time
ample-0.1.0-win-x64.zip
45 MB (the program is 105 MB, self-contained and not trimmed)
publish 24 s, zip 4 s
ample-0.1.0-linux-x64.zip
44 MB
publish 7 s, zip 3 s
Ample.Cli.0.1.0.nupkg
18 MB
21 s
ample-vscode-0.1.0.vsix
0.5 MB
52 s (with npm install)
Open items (why this is not v1.0)
The Linux test run has not been seen. The CI matrix (.github/workflows/ample.yml) runs the fail-closed full suite on Ubuntu and Windows, including installed grammar and browser-parity dependencies, but this machine cannot run Linux and nobody has looked at a Linux result in this build-out. The Linux zip is built and not run.
Golden audio hashes on Linux are therefore unproven (the Windows hashes are tested; the integer DSP is designed to be platform independent).
The VS Code extension is smoke-tested, but the .vsix is unsigned by a marketplace publisher. On 2026-10-03, a fresh package was installed into an isolated VS Code 1.140.0 profile; activation, commands and an LSP completion passed. The completion used a local test server fixture; the AMPLE CLI stdio handshake passed separately.
No clean-VM installer test; the smoke test ran on the development machine in a clean folder, which is close but not the same.
Not measured: the LSP under load, a huge graph in the browser, a 10 GB take bank, a long soak run (the nightly workflow runs the chaos and performance tests; nobody has seen it yet).
The release workflow (.github/workflows/release.yml) has never run: it needs a tag, and signing needs the owner's key.
How to make a release
pwsh ample/tools/check-all.ps1 -Full (or bash ample/tools/check-all.sh --full) is green on both systems. Full mode fails rather than silently omitting the VS Code grammar or browser-parity suites.
Raise <Version> in ample/Directory.Build.props and ample/editors/vscode/package.json; write the notes in CHANGELOG.
Tag ample-v<version>; the release workflow builds the artefacts on Windows, runs tools/smoke-package.ps1, and attaches the files and SHA256SUMS.txt to a draft release. Sign them with pwsh ample/tools/package.ps1 -Sign on the machine that has the signing key (ample keys generate once; the passphrase comes from AMPLE_VAULT_PASSPHRASE), and check with ample verify --file <artefact>.
The owner reads the draft, then publishes it.
Security review checklist
Status (2026-10-03): written in chunk 98 from what is built and tested. The owner has not signed it off yet (the acceptance item of chunk 98 needs that). Everything marked "not verified" is listed at the end.
Server (ample serve)
Defence
Proof
Listens on 127.0.0.1 only; a LAN address needs --allow-lan (documented as trusted network only)
docs/api.md Safety; the smoke test starts the packaged program and reaches it on 127.0.0.1
Host header must be localhost or 127.0.0.1 (DNS rebinding), AMP5282
ServerTests.Foreign_host_header_is_refused
Foreign Origin refused, AMP5283
ServerTests.Foreign_origin_is_refused
Session token on every /api call, AMP5284; the token travels in the URL fragment (never sent to a server); a cookie for GET only
Api_without_token_is_refused, Wrong_token_is_refused, the WebSocket test
Not a sandbox: a plugin is a normal process with the user's rights; the environment and path rules limit mistakes, not a malicious program
stated here; install only plugins you trust
Secrets and data at rest
See Security for the threat model, the vault, signing and the optional encryption (AES-256-GCM, PBKDF2, ECDSA P-256 from the .NET platform; no custom cryptography). Tests: the canary-secret scan of the project, the black box, a backup and an export; wrong-passphrase, tamper and key-loss paths.
Dependencies (audited 2026-10-03)
dotnet list package --vulnerable --include-transitive on the solution: no vulnerable packages (against the NuGet vulnerability feed of that day).
npm audit --omit=dev in ample/: 0 vulnerabilities.
npm audit in editors/vscode: 6 high findings, all in the chain @vscode/vsce > secretlint > globby > fast-glob > micromatch > braces (a denial of service by deeply nested glob patterns). They are in the packaging tool only, which runs on the developer's or the release machine on trusted input; nothing of it is shipped in the .vsix. The runtime dependency vscode-languageclient has no finding. Revisit when @vscode/vsce moves its dependency.
Re-run both audits before every release (tools/package.ps1 does not; the release workflow does).
Not verified, or left to the owner
Sign-off of this checklist by the owner.
No licence file is chosen: the repository and the extension say UNLICENSED. Picking a licence is the owner's decision.
No external penetration test; no fuzzing beyond the parser and format tests.
The server has not been run behind a reverse proxy or with --allow-lan on a real network.
Linux and macOS: the Linux package is built, but the Linux test run has not been seen yet (the CI matrix includes it; see Release).
Language support: compiler diagnostics, linter, editor tooling, docs site
Status (2026-10-03): the language server of section 6 is built (chunk 80); what it does today and what is still open is in lsp.md.
The normative language specification is now in language/; this page is background. Status (2026-10-02): design sketch from a brainstorm. Nothing here is built. The clickable sketch (sketch/index.html) mocks the parts marked "in the sketch": diagnostics with positions and quick fixes, a linter, completion, CLI-style output and a generated language reference.
A language people write by hand needs more than a parser. It needs good errors, a linter, editor support and documentation that cannot drift. All of them hang on one compiler front end, so a message looks the same in the editor, the terminal and a CI log.
1. One front end, many tools
text (.amplet) ──► lexer ──► parser (error-recovering) ──► syntax tree with source ranges
│
semantic analysis (names, ports, types, parameters, cycles)
│
linter (rules, configurable) ──► diagnostics ──► editor / CLI / CI / UI
│
apply to the working copy (graph)
Error recovery: the parser keeps going after an error (it resynchronises at the next block header or }), so one mistake does not hide the rest and the node editor can still show everything that parsed.
Source ranges everywhere: every token and node knows its file, line, column and end, in one convention (1-based lines and columns, columns in characters; the language server converts to UTF-16 for editors that need it).
Pure and fast: parsing and checking a whole song takes milliseconds, so the editor can check on every pause.
2. The diagnostic record
Every message has the same shape (in the sketch: shown in the problems list, the gutter, the squiggles and the CLI output):
| Field | Meaning | Example |
| --- | --- | --- |
| code | stable identifier, never reused | AMP2003 |
| severity | error (blocks apply), warning, info | error |
| file, line, column, endLine, endColumn | the exact range | song.amplet:43:3-9 |
| message | one sentence saying what is wrong | Unknown parameter "patern" for drums. |
| hint | what to do, often with the valid choices | Did you mean "pattern"? Known parameters: kit, pattern, bars |
| related | other places that matter | first used here pointing at line 12 |
| fixes | zero or more quick fixes with a label and an edit, marked safe or risky | Replace with "pattern" |
| explain | link to the long explanation | ample explain AMP2003 |
Rules for the messages themselves: say what is wrong and where, in plain words, without jargon; point at the smallest range that is wrong (the parameter name, not the whole line); suggest the likely fix; never blame.
3. Codes
AMP1xxx syntax (the text is not shaped right): missing project line, malformed header, bad class@version, bad placement, unclosed block, bad from or parameter line.
AMP2xxx meaning (the text is shaped right but says something impossible): unknown block type, duplicate block name, unknown parameter, no such block, no such output or input, type mismatch, input with two sources, a block reading itself, cycles.
AMP5xxx environment (depends on what is installed or reached): a plugin is not installed, a link is unreachable. Usually warnings.
Lxxx lint rules (see section 4).
Plugin-provided codes use the plugin's prefix (ACE1xxx), declared in its manifest, with the same record shape.
Codes are stable across versions; a code is never reused for something else. A retired code stays in the catalogue as "no longer produced".
4. The linter
Lint rules find things that compile but are probably not what you meant. Each rule has a code, a name, a default level (off, info, warning, error), an explanation with a wrong and a right example, and optionally a quick fix.
| Rule | Name | Default | Finds |
| --- | --- | --- | --- |
| L001 | unused-output | info | a block whose outputs nothing reads |
| L002 | missing-input | warning | a required input without a source |
| L003 | lock-and-refresh | warning | a block that is locked and also always-refetch |
| L004 | overlapping-parts | warning | two parts of one block overlap in time |
| L005 | dynamic-not-fixed | off | a dynamic block neither baked nor locked (results can change) |
| L006 | pinned-outdated | warning | a pinned block older than its template (the fix changes the sound, so it is risky) |
| L007 | no-path-to-master | warning | a block that makes audio but never reaches the master |
| L008 | name-case-clash | warning | two block names differing only by case |
| L009 | custom-seed | info | a dynamic block uses custom seeds instead of derived ones (fine and recorded; changing them later breaks reproduction of takes made with the old ones) |
Configuration: per rule in the project settings, saved in the song as one line (lint { L004 error L005 info }, only entries that differ from the default). A rule raised to error blocks Apply like any syntax error. Suppression: a comment above a block, # ample-ignore L004, L005, silences those rules for that block only (and the linter can warn about ignores that no longer match anything). In the .amplet source editor, right-click a lint diagnostic and choose Disable Lxxx for this block to add the comment. Release levels (see the quality module) can turn selected rules into errors for a release.
More rules to consider later: a part longer than the whole song, a tempo that disagrees with an imported audio file, a baked block whose recipe is no longer valid, credits that do not sum to 100%, an external data source with refresh in a sealed release.
5. Quick fixes and "Fix all"
A fix is a text edit tied to a diagnostic (replace a range, insert a line, append). safe fixes do not change meaning (typo replacement, add a missing }, clamp a value, remove a contradictory flag) and are applied together by Fix all; risky fixes change what the song sounds like (updating a pinned block) and are only ever applied one by one, on purpose. Fixes are applied bottom-up so positions stay valid.
6. Tooling
ample check song.amplet [--format text|json|sarif] [--lint] validate; exit code 1 on errors
ample lint song.amplet [--fix] lint rules; safe fixes with --fix
ample fmt song.amplet normalise the text
ample explain AMP2003 the long explanation of a code
ample docs [--out docs-site/] generate the language reference as a static site
ample lsp language server (stdio)
Output formats: human text (file:line:col, severity, code, message, a source excerpt with carets, hint, fix, more: line), JSON (one record per diagnostic, as in section 2) and SARIF for CI code-scanning integrations.
Language server (LSP): live diagnostics with ranges, hover (block type, ports, parameter docs, current cache state), completion (block types, parameters, block names, outputs after from x., inputs after ->, plugin-provided types), go to definition (from a from line to the block), find references, rename (a block and every reference), quick fixes, code lens (baked? cached? how many variants?), formatting, semantic tokens. VS Code first; any LSP client works. The web editor in AMPLE uses the same engine over its API.
In the sketch: Check, Apply, Fix all, the problems list with excerpts, hints and "Explain", squiggles and gutter markers, Ctrl+Space completion, the CLI output dialog (text and JSON) and the reference.
7. Documentation that cannot drift
The language reference (the "language support site") is generated, not hand-written:
Block types: from the templates (ports with types, parameters with defaults, update class and version, required plugin, an auto-built example). A template or plugin update regenerates its page.
Syntax, ports and types: from the grammar and the type table.
Diagnostics catalogue: from the code table (title, explanation, wrong example, right example), so ample explain AMP2003, the editor's Explain button and the site show the same text.
Lint rules: from the rule table, with the level selectors that edit the project's lint line.
Versioned: one site per language version; template and plugin pages carry their own versions; old versions stay readable.
Tested examples: every example in the docs (the wrong ones must produce the stated code, the right ones must compile cleanly) is compiled in the test suite, so an example can never rot.
Searchable and linkable: every block type, parameter, code and rule has a stable URL; diagnostics link straight to their page.
Offline: the app ships the generated site; no network needed (the sketch's Reference button is this, as an overlay).
8. Quality of the diagnostics themselves
Golden tests: a folder of small broken songs, each with the expected diagnostics (code, range, message) in a file next to it; any change in a message shows up as a diff in review.
Fuzz and recovery tests: random truncations and deletions of valid songs must never crash the parser and must keep reporting sensible errors.
Message reviews: wording changes are reviewed like code; the catalogue keeps a one-line "why" per code.
Performance budget: a check of a large song stays under a fixed time budget (tracked in CI).
9. Relation to the rest
Apply and leaving the source editor need zero errors (see Source and views); warnings and info never block.
Plugins contribute block types (so the reference, completion and checks know them) and may contribute diagnostics and lint rules; a missing plugin is AMP5001, never a silent skip.
Quality module: release levels can promote lint rules to errors for a release; the quality scorers are separate (they measure sound, the linter reads text).
History: diagnostics are not stored in revisions; a revision is only made from a song that compiles.
10. Open questions
Should the formatter keep comments and blank lines exactly (lossless), or is a normalised text acceptable?
Should lint levels also be settable per release level in the same lint line?
Do plugin-provided lint rules need a permission or a pinned version like plugins themselves?
Is SARIF worth the effort in v1, or are text and JSON enough?
How are diagnostics for the inner graphs of groups and templates shown, if those are node-editor-only?
Jargon (glossary for AMPLE)
Status (2026-10-02): design sketch from a brainstorm. Nothing here is built or tested.
AMPLE uses real studio and music terms. ample explain "<term>" explains them and plays an example.
Score (dynamics, tempo, articulation)
Term
Meaning
pp p mp mf f ff
very soft ... very loud (pianissimo ... fortissimo)
crescendo / diminuendo
gradually louder / softer
ritardando / a tempo
slow down / back to tempo
staccato / legato
short and detached / smoothly connected
fermata
hold the note longer
tacet
this part is silent
tutti
everyone plays
da capo / coda
back to the start / closing section
Song structure
intro, verse, pre-chorus, chorus, bridge, break, turnaround, vamp, tag (repeat at the end), outro.
Directing the band
Term
Meaning
lay back
play slightly behind the beat
push
play slightly ahead of the beat
in the pocket
tight, grooving on the beat
ghost notes
very soft in-between notes
fill
short flourish, usually on drums
stop-time
only accents, the rest silent
half-time
the groove feels half as fast
build / drop
build up to / suddenly release
Studio (behind the glass)
Term
Meaning
take
one recording attempt, numbered and kept
comp
assemble the best parts of several takes
punch in / out
re-record only a short section
overdub
record on top of what exists
double / stack
layer the same part several times
bus / send
shared channel / route to an effect
duck
automatically quieter while something else plays
pan
left-right placement
bounce / stem
render out / a separate group of tracks as audio
tighten
pull the timing in line with another part
Alias idea (tolerant parser): lay back = layback = behind the beat (Dutch: achter de tel). Files always store the canonical form.