Skip to the content.

PALETTE — Claude Code context

What this is

A 4-slot serial multi-effect for Ableton Move, inspired by the Hologram Electronics Chroma Console. Keeps the Chroma’s reorderable-module soul + DRIFT instability philosophy, but drops the rigid one-category-per-module rule: there are 4 generic serial slots, each loading any of 24 effects from a shared palette (or Off). This dissolves the Chroma’s #1 documented limitation (no delay+reverb at once) by construction.

Schwung audio FX module. API: audio_fx_api_v2. Language: C (+ C++ for the Mutable-derived effects). Repo: filliformes/palette-move. Author/attribution: Filliformes (never the real name). License: MIT (all sources are MIT-compatible — Mutable, Airwindows, siblings).

Processes audio_inout (the chain signal at this slot), stereo, 44100 Hz, 128 frames/block.

Repo structure

The palette — 24 effects (+ Off)

Grouped only for LED-colour theming; slots are generic. Each effect gets Amount / Macro / Drift. PFX_* ids and FX_NAMES[] order in palette.c is authoritative — keep module.json enum options identical.

★ = the 4 new originals (not in the Chroma): FOLD (West-Coast wavefolder), SHIFT (Bode linear frequency shifter), BLOOM (granular shimmer reverb), HALO (Karplus harmonic resonator pad, Qi/Dark-Star vibe).

DSP sourcing map — AS BUILT

vendor/SOURCES.md is the authoritative per-effect record. Summary of what each effect actually uses (13/24 on genuinely fetched/ported OSS, 11 original C):

UI — 4 pages (api_v2 ui_hierarchy in module.json; DSP mirrors via get_param)

Opens to Presets (root level = Page 4, Ambiotica-style landing).

Effect uniqueness — SKIP policy

Each of the 24 effects occupies ≤1 slot at a time; Off is exempt. Selecting a taken effect skips it (the encoder walks past, clamps at the ends — no wrap) to the next free effect, in the scroll direction. Implemented in resolve_select()/set_slot_select(); get_param("fxN_select") returns the landed index so the Shadow UI re-syncs. Randomizers and preset-load write the 4 selects atomically with distinct pre-validated values (sampling without replacement — pick_distinct_effects()), bypassing the per-step skip walk.

Meta-features

Presets — DECISION: factory-only in v1

25 factory presets, browseable via the Current Preset knob; the 6 randomizers mutate the live patch. No user Save in v1 (Page 4 has no Save knob — confirmed with Vincent’s layout). A preset snapshots: 4 FX Selects + per-slot Amount/Macro/Drift + global Mix/Input Volume/FX Reorder. Still implement get_param("state")/set_param("state") round-trip so the host persists the live patch per Set (separate from preset save). User-savable slots = v2. Author the 25 in presets.md showing combos the Chroma can’t do (delay→reverb, FUZZ→PHASER→SPACE, FOLD→SPACE, FREEZE→REELS). load_preset() must snap all values (no slow interpolation) and reset slot buffers.

Critical Schwung rules (from /move + /move-schwung + /scaffold-audio-fx)

CPU / RAM budget

CPU bounded by 4 active effects (one per slot), not 24. Worst case = 4 spectral/granular engines at once (BLOOM+FREEZE+SPACE+COLLAGE) — keep each lean; consider preset guidance of ≤2 spectral effects. RAM: budget up to 4 long stereo-float delay/reverb buffers. Skeleton allocates a 2 s stereo delay line + RNG seed per slot once in create_instance.

Coding plan (Stage 4)

  1. Add get_param("ui_hierarchy") to palette.c (mirror module.json) — gates page navigation.
  2. Implement effects in batches, sibling-first: DRIVE→(real super-boom voicing), REELS, CASSETTE, SPACE, then the 4 originals (FOLD, SHIFT, BLOOM, FREEZE), then the rest.
  3. When the first C++ (Mutable) effect lands: refactor palette.c to include the shared headers instead of inline structs, split effects into TUs, confirm the multi-TU build links.
  4. Wire load_preset() + the 25 presets + state round-trip.
  5. /dsp-review → /move-build → test on device → /move-review → release.

Build & deploy

./scripts/build.sh          # Docker ARM64 cross-compile (needs Docker Desktop running)
./scripts/install.sh        # flat SCP to move.local; then power-cycle

Pipeline

Use /move to advance stages. First release also needs a module-catalog.json PR to charlesvestal/schwung (catalog entry: component_type audio_fx, github_repo filliformes/palette-move, default_branch master, asset palette-module.tar.gz).