# Audio as Code > An open-source Python framework (MIT) for AI agents and developers to compose editable music for videos, games and presentations, render it offline to WAV with instruments synthesized entirely from code, export MIDI, and read back signal measurements. Version 0.1.0, early prototype. Distribution `audio-as-code`, import `audio_as_code`, command `aac`. ## Start here - [Full agent documentation](https://audioascode.com/llms-full.txt): this index plus the published guides in one text download. For smaller context windows, follow the individual links below. - [Portable composition skill](https://audioascode.com/skills/audio-as-code/SKILL.md): read this first for setup, brief interpretation, original composition, validate/render/inspect/revise, and delivery of WAV, MIDI, editable score, composer source and report. Plain instructions for a shell-capable coding agent; no vendor integration assumed. - [Source download](https://audioascode.com/source.html): actual source ZIP, size and SHA-256. Follow this page's download link or use [the GitHub repository](https://github.com/joaoCarvalho1000/audio-as-code). Website handoff prompts include the current site's exact source URL. No registry package is published. - A person describes the music and requests revisions in ordinary language. The agent makes artistic decisions and runs the tool locally. The human does not need to write Python; the agent needs a shell, writable project and Python 3.10+. Facts an agent should rely on: - Install from a local copy of the source folder: `uv sync --locked`, then prefix commands with `uv run`. Without uv, create `.venv` and use its Python to `-m pip install -e .` and `-m audio_as_code`; activation is optional. The package is not published to PyPI or any registry. - There is no hosted API, MCP server, in-browser renderer, bundled AI model or required provider API key. Audio on the website was pre-rendered locally by the Python synthesizers. To hear an edit, render locally. - Every `aac` command (except `--help`, which prints plain text) prints one JSON object: stdout and exit 0 on success, stderr and exit 2 on failure (`invalid_score` with `issues[].path`, or `operation_failed` with `message`). - Scores are JSON, `"schema_version": "1"`. Time is in quarter-note beats; without a tempo map, seconds = beats * 60 / bpm; optional `tempo_map` entries are ordered `{beat,bpm}` steps. Pitch is MIDI 0-127 or a name such as "C4" (= 60). Notes must end by `beats`. No extra fields. - 49 playable instruments, all code-generated approximations (no recorded samples, SoundFonts, or measured impulse responses). Tone controls are per track and only those listed in each instrument's `tone_controls` are accepted. - Optional expression: ordered step tempo changes; track gain/pan and song master_gain automation with linear/step points; note/track release_seconds; ordered track/song delay and generated reverb effects. Read the installed schema for parameters. - Limits: 1-64 tracks, 100,000 notes, 300-second WAV renders including automatic tails; no tempo ramps, sections or instrument articulation switches. Piano supports binary Track.pedal events. MIDI carries tempo changes and piano CC64 but omits tone controls, automation, effects and release tails (at most 15 melodic tracks; shared drum channel). Stems omit master effects. - Render reports (peak, RMS, clipping, silence, gain_applied, warnings) are signal checks, not a measure of musical quality or realism. - Rendering is deterministic for the same score, seed, software versions, and platform. ## Add to a creative project A score is the editable recipe for music: instruments, notes, timing, dynamics and tempo. Python composition code builds the score; the renderer turns it into WAV audio. MIDI exports its musical events for another synthesizer. For an existing uv project with Python 3.10+ and Git installed: ```sh uv add "audio-as-code @ git+https://github.com/joaoCarvalho1000/audio-as-code.git" uv run --locked aac instruments uv run --locked aac schema uv run --locked aac demo -o output/song.json uv run --locked aac inspect output/song.json uv run --locked aac render output/song.json -o output/song.wav --report output/report.json uv run --locked aac midi output/song.json -o output/song.mid ``` Keep the resolved Git commit and dependencies in the project's uv.lock. Read the skill from the same revision when pinning an older version; installed CLI discovery and schema take precedence. Download the source workspace instead when you need the bundled composition examples. For a video, game or presentation, deliver the WAV into that project's audio folder and retain the editable score and composer for revisions. ## Docs - [Quickstart](https://audioascode.com/docs/quickstart.md): give the framework to an agent; optional manual source install, demo, Python and JSON - [Agent guide](https://audioascode.com/docs/agents.md): agent handoff and revision prompts, compose/validate/render/inspect/revise loop, exact commands, strict JSON score, errors and recovery - [Creative integrations](https://audioascode.com/docs/integrations.md): Codex and Claude Code skill setup, a Hyperframes soundtrack handoff, game/presentation cues, and a runnable local CLI-to-artifact bridge - [Composition guide](https://audioascode.com/docs/composition.md): beats and tempo, pitch, Pattern, chords, motifs, form, tracks, gain and pan, Tone controls, drums, MIDI caveats - [Reference](https://audioascode.com/docs/reference.md): every CLI command, score field, Python function, report field, and MIDI rule ## Machine-readable - Before synthesis, run `aac inspect score.json` (Python: `inspect_score(song)`) for track facts and static WAV/MIDI readiness. Inspection can exit 0 with blocked exports: check `readiness.render.ready`, `readiness.midi.ready` and structured `issues`. It does not measure audio quality or check output paths. - [Score JSON Schema, version 1](https://audioascode.com/schemas/song-v1.schema.json): output of `aac schema`; runtime validation adds cross-field rules - [Instrument catalog](https://audioascode.com/instruments.json): output of `aac instruments --all`; families, engines, instruments, tone_controls, defaults, MIDI mappings ## Examples - [01_first_score.py](https://audioascode.com/docs/examples/01_first_score.py): patterns, tracks, WAV, MIDI, report - [02_motif_and_progression.py](https://audioascode.com/docs/examples/02_motif_and_progression.py): motif transposition, I-vi-IV-V voicings, bass line, accents - [03_song_form.py](https://audioascode.com/docs/examples/03_song_form.py): sections table, dynamics, arrangement density, stems - [04_expressive_controls.py](https://audioascode.com/docs/examples/04_expressive_controls.py): Tone controls, velocity, pan, drum_machine pitch map - [05_agent_loop.py](https://audioascode.com/docs/examples/05_agent_loop.py): CLI-only validate, render, error recovery, measured revision, determinism check - [agent-score.json](https://audioascode.com/docs/examples/agent-score.json): a complete valid score used by the agent loop ## Classic and reimagined listening pairs - The website pairs five familiar public-domain scores with new arrangements by an AI agent. **The classic** follows the credited source score; **Reimagined** changes its musical treatment. Both versions are synthesized by Audio as Code and have editable scores. Neither is a sampled recording. - [Score credits](https://audioascode.com/docs/classic-showcase.md): source editions, completeness and arrangement notes. A complete standalone arrangement of *Ode to Joy* is not the full symphonic movement; use the credited score's scope. - [classic_showcase.py](https://audioascode.com/music/classic_showcase.py): inspectable generator; use the complete [source download](https://audioascode.com/source.html) for its runnable project and supporting files. - Preserved classic IDs: `classic-fur-elise`, `classic-cello-prelude`, `classic-turkish-march`, `classic-greensleeves`, `classic-ode-to-joy`. Read the manifest for the paired variant IDs and actual instrumentation; do not infer filenames. - [Listening manifest](https://audioascode.com/music/manifest.json): the published listening program, with duration, instruments and artifact paths. Attribute historical composition and agent-written arrangement separately. - Four additional original examples remain in `examples/full_compositions.py` inside the source project; they are separate from the website lineup. Run `python examples/full_compositions.py [output] [--scores-only]` from the source folder. Default output is `output/full-compositions/`. - Preserved original example IDs: `lanterns-on-the-water`, `copper-street`, `night-signal`, `clockwork-garden`. ## Optional - License: MIT, in the `LICENSE` file of the source folder - Run the examples from the source folder root, for example `python docs/site/examples/01_first_score.py`; outputs go under `output/docs-examples/` --- Source: https://audioascode.com/docs/quickstart.md # Make your first piece The quickest route is to give Audio as Code to your coding agent. On the website, choose **Copy agent prompt**, paste it into the agent, and describe the music. The copied instructions include the actual source download and portable skill. Your agent sets up the framework locally and delivers the audio and editable project. You do not need to learn Python first. Start with the website's listening pairs: **The classic** follows a credited public-domain score; **Reimagined** gives it a new arrangement by an AI agent. Compare the instruments, rhythm and mood, then open the editable scores. Both versions are synthesized from code. See the [score credits](https://audioascode.com/docs/classic-showcase.md) and [generator](https://audioascode.com/music/classic_showcase.py). For example: "My puzzle game's mushroom shop needs a 20-second cue: curious marimba, a sleepy bass and one very important bell. Keep the score so we can change it later." After listening: "Keep the melody, lose half the bells, and give the ending another second." The [agent guide](https://audioascode.com/docs/agents.md) explains the handoff and revisions. If you already have the source, tell your agent to read `skills/audio-as-code/SKILL.md` in that folder and compose your brief. If it cannot fetch files, use [Download source](https://audioascode.com/source.html), extract the ZIP, and give it the folder. You receive WAV, MIDI, an editable JSON score, composer source when used, and a render report. The coding agent needs shell access and Python 3.10+. First-time dependency installation needs network access or cached packages; rendering is then offline. The framework has no bundled AI model and needs no provider API key. ## Developer route Prefer to work directly? The rest of this page walks through the same local framework. Audio as Code synthesizes instruments entirely from code: no recorded samples or SoundFonts. The website plays pre-rendered examples; Python does not run in your browser. The package is not published to a registry. Add it from Git to an existing Python project, or use the [source download](https://audioascode.com/source.html) for a standalone composition workspace. ## Add to an existing Python project From an existing project managed by uv, with Git installed: ```sh uv add "audio-as-code @ git+https://github.com/joaoCarvalho1000/audio-as-code.git" uv run --locked aac --version uv run --locked aac instruments uv run --locked aac schema ``` Keep `pyproject.toml` and `uv.lock` with your project. The lock records the resolved Git commit and dependency versions; `uv run --locked` reuses that environment. To select a specific revision, append `@FULL_COMMIT_SHA` to the `.git` URL, replacing the placeholder with a verified full commit. This is a source install, not a registry package. It requires network access on first installation. Give your agent this brief once installed: ```text Use the Audio as Code dependency in this project. Run uv run --locked aac instruments and schema to discover its current contract. Compose an original 12-second exploration game loop with marimba, electric piano and bass. Save the composer and editable JSON score in a fresh output folder. Validate and inspect export readiness, then render WAV and export MIDI with their reports. Check the actual duration and loop join. Tell me whether you could listen to it. Keep the seed, melody and duration fixed when I request a revision. ``` Continue at step 2, using `uv run --locked` before the `aac` and Python commands. Save your composer in your own project. The [portable skill](https://github.com/joaoCarvalho1000/audio-as-code/blob/main/skills/audio-as-code/SKILL.md) has the complete composition and delivery workflow. When using a pinned revision, read the skill at that same revision; the installed CLI and schema are authoritative. The Git dependency installs the Python library and CLI; use the source workspace below if you also want the bundled tutorial scripts and examples. ## 1. Install from the source folder Open a terminal in the folder that contains `pyproject.toml`. **With uv (recommended)** ```sh uv sync --locked uv run aac --version uv run aac instruments ``` This uses `uv.lock`, creates a local environment and installs the development tools. Prefix each command below with `uv run`, for example `uv run aac demo -o output/song.json` or `uv run python first.py`. **Without uv** ```sh python -m venv .venv ``` On Windows, run `.venv/Scripts/python.exe -m pip install -e .`; on macOS/Linux, run `.venv/bin/python -m pip install -e .`. No activation is necessary. Replace `aac` below with `.venv/Scripts/python.exe -m audio_as_code` on Windows or `.venv/bin/python -m audio_as_code` on macOS/Linux. Use that same environment's Python to run composer scripts. The install downloads NumPy, Pydantic and mido. Editable installation means source changes take effect without reinstalling. `aac instruments` returns JSON with playable IDs, supported controls and model limitations. ## 2. Render the built-in demo ```sh aac demo -o output/song.json aac validate output/song.json aac inspect output/song.json aac render output/song.json -o output/song.wav --report output/report.json aac midi output/song.json -o output/song.mid ``` Open `output/song.wav` in an audio player: it is an eight-bar arrangement with chords, bass, melody and drums. `output/song.mid` takes the notes into a DAW; `output/song.json` keeps the score editable. The render report records duration and level checks. ## 3. Write your first score in Python Save this as `first.py` in the source folder and run `python first.py`: ```python from audio_as_code import Pattern, Song, Track, export_midi, render melody = Pattern.sequence(["C4", "E4", "G4", None], step=0.5).repeat(4) kick = Pattern.sequence([36, None], step=1, gate=0.3).repeat(4) song = Song( title="My first loop", bpm=110, beats=8, seed=42, tracks=[ Track(name="Melody", instrument="pluck", notes=melody.notes, gain=0.5), Track(name="Kick", instrument="kick", notes=kick.notes, gain=0.7), ], ) song.save("output/loop.json") report = render(song, "output/loop.wav") export_midi(song, "output/loop.mid") print(report["wav"]["duration_seconds"], report["warnings"]) ``` What the pieces mean: - **`beats=8`** is the song length in quarter-note beats: two bars of 4/4. At 110 BPM that is 8 × 60 / 110 ≈ 4.36 seconds. Notes must end by beat 8. - **`Pattern.sequence(steps, step=0.5)`** places one item per half beat (an eighth note). A string or integer is a pitch, a list is a chord, `None` is a rest. - **`gate`** is the fraction of each step a note sounds (default 0.8). - **`.repeat(4)`** repeats the phrase end to end, keeping trailing rests. - **`seed`** fixes the generated noise in drums and string excitation, so the same score renders the same audio in the same environment. ## 4. Or write the score as JSON Any language can produce this file. Save it as `output/phrase.json`: ```json { "schema_version": "1", "title": "A small phrase", "bpm": 120, "beats": 4, "seed": 7, "tracks": [{ "name": "Lead", "instrument": "pluck", "gain": 0.6, "notes": [ {"pitch": "C4", "start": 0, "duration": 0.8}, {"pitch": "E4", "start": 1, "duration": 0.8}, {"pitch": "G4", "start": 2, "duration": 1.5} ] }] } ``` ```sh aac validate output/phrase.json aac render output/phrase.json -o output/phrase.wav ``` If validation fails, its message identifies the score problem to fix before rendering. The [agent guide](https://audioascode.com/docs/agents.md#errors-and-recovery) explains the full error format for automated workflows. ## 5. Run the tutorial examples Six scripts sit next to these pages in `examples/`, starting with [`examples/01_first_score.py`](https://audioascode.com/docs/examples/01_first_score.py). From the source folder they are at `docs/site/examples/`. The first five write to their own folders under `output/docs-examples/`, or to a directory you pass as the first argument. The handoff script takes an input score and a new output directory. | Script | Teaches | Output length | | --- | --- | --- | | [`01_first_score.py`](https://audioascode.com/docs/examples/01_first_score.py) | Patterns, tracks, WAV + MIDI, report | 10 s | | [`02_motif_and_progression.py`](https://audioascode.com/docs/examples/02_motif_and_progression.py) | Motif transposition, chord voicings, bass line, accents | 18 s | | [`03_song_form.py`](https://audioascode.com/docs/examples/03_song_form.py) | Sections, dynamics, arrangement density, stems | 89 s | | [`04_expressive_controls.py`](https://audioascode.com/docs/examples/04_expressive_controls.py) | `Tone` controls, pan, velocity, drum kit pitches | 12 s | | [`05_agent_loop.py`](https://audioascode.com/docs/examples/05_agent_loop.py) with [`agent-score.json`](https://audioascode.com/docs/examples/agent-score.json) | CLI-only validate → render → revise loop | 10 s per candidate | | [`06_agent_handoff.py`](https://audioascode.com/docs/examples/06_agent_handoff.py) | Inspect export readiness, render and deliver files to another creative tool | Matches the input score, including tails | ```sh python docs/site/examples/01_first_score.py ``` Rendering is offline and CPU-bound. On the machine used to write these pages, the 89-second form example took about two minutes; short sketches take seconds. ## Where to go next - [Composition guide](https://audioascode.com/docs/composition.md): time, pitch, patterns, harmony, form, controls, and mixing. - [Agent guide](https://audioascode.com/docs/agents.md): a copyable prompt and the exact tool-call loop for AI agents. - [Reference](https://audioascode.com/docs/reference.md): every CLI command, Python function, score field, and report field. - [`llms.txt`](https://audioascode.com/llms.txt): a machine-readable index of these resources. The project is MIT licensed; see the `LICENSE` file in the source folder. --- Source: https://audioascode.com/docs/composition.md # Composition guide A score is a `Song`: a tempo, a length in beats, and named tracks of notes. This guide covers the musical model and the Python helpers for building it. Every fact here matches the 0.1 source; field limits are collected in the [reference](https://audioascode.com/docs/reference.md). ```text Song ── bpm, tempo_map, beats, seed, sample_rate, master_gain, automation, effects └─ Track ── name, instrument, gain, pan, tone, release_seconds, automation, effects └─ Note ── pitch, start, duration, velocity, release_seconds ``` ## Time is counted in beats All positions and lengths are **quarter-note beats** from zero. With constant tempo, convert beats to seconds as follows: ```text seconds = beats × 60 / bpm ``` | bpm | 1 beat | 4 beats (one 4/4 bar) | 64 beats (16 bars) | | --- | --- | --- | --- | | 60 | 1.00 s | 4.00 s | 64.0 s | | 96 | 0.625 s | 2.50 s | 40.0 s | | 120 | 0.50 s | 2.00 s | 32.0 s | - `Song.beats` is the full length, including any silence you want at the end. Every note must end at or before it; validation rejects a note that runs past. - `Song.seconds` gives the score duration, integrating any tempo changes. `Song.render_seconds` includes automatic release/effect tails. A WAV render is limited to 300 seconds including those tails. - Tempo is 20–300 BPM. Optional `tempo_map` entries change it in ordered steps (see below). There is no meter field or swing setting. Bars are a convention you keep in code (`BAR = 4`). Swing is written by moving off-beat notes by an explicit offset. Common durations: whole note 4, half 2, quarter 1, eighth 0.5, sixteenth 0.25, eighth-note triplet 1/3. ## Pitch A pitch is either a MIDI integer 0–127 or a name like `C4`, `F#3`, `Bb2`. Middle C is `C4` = 60, and `A4` = 69 = 440 Hz in equal temperament. Accidentals are a single `#` or `b`. ```python from audio_as_code import midi_pitch midi_pitch("C4") # 60 midi_pitch("Bb2") # 46 ``` Very high notes can fall above the Nyquist frequency at low sample rates and become silent; very short notes (under about 3 samples) can disappear. The render report warns about the second case. ## Notes and velocity ```python from audio_as_code import Note Note(pitch="E4", start=2, duration=1.5, velocity=0.7) ``` - `start` ≥ 0 and `duration` > 0, both in beats. - `velocity` is in (0, 1]. It scales loudness and, on modeled instruments, excitation brightness. There is no zero-velocity note: leave a note out to make a rest. - By default, each voice fades to zero inside its duration. A long tone `decay_seconds` alone does **not** extend a short note. Set track `release_seconds` (0–10 seconds) to add a release after note-off; a note can override it, including with zero. Releases may extend past the song beat length and are included in the rendered tail. ## Patterns: phrases you can move around `Pattern` builds notes without touching playback. Transformations return new patterns; `.at()` returns placed notes. ```python from audio_as_code import Pattern riff = Pattern.sequence(["A3", None, "C4", ["E4", "A4"]], step=0.5, gate=0.7, velocity=0.75) riff.beats # 2.0: four steps of half a beat, trailing rests included riff.repeat(4) # 8 beats riff.transpose(5) # up a perfect fourth (semitones, integers only) riff.then(other) # riff followed by other riff.at(16) # tuple of Notes placed at absolute beat 16 ``` | Method | Returns | Use | | --- | --- | --- | | `Pattern.sequence(steps, *, step=1, gate=0.8, velocity=0.8)` | `Pattern` | One item per step: pitch, list/tuple chord, or `None` rest | | `.repeat(times)` | `Pattern` | Loop end to end | | `.transpose(semitones)` | `Pattern` | Shift every pitch; result must stay in 0–127 | | `.then(other)` | `Pattern` | Concatenate phrases | | `.overlay(other, offset=0)` | `Pattern` | Layer another phrase at a beat offset, preserving both lengths and all notes | | `.stretch(factor)` | `Pattern` | Scale note positions, durations and phrase length; `2` doubles the length | | `.scale_velocity(factor)` | `Pattern` | Multiply velocities; results must stay in (0, 1] | | `.at(beat)` | `tuple[Note, ...]` | Place on the song timeline for a `Track` | | `.notes`, `.beats` | data | The notes (pattern-relative) and the length | `Pattern.sequence()` starts with a shared velocity and gate. A pattern can contain notes with different velocities and durations. For accents or varied lengths, rebuild the notes: ```python accented = [ Note(**{**n.model_dump(), "velocity": 0.9 if n.start % 2 == 0 else 0.5}) for n in riff.repeat(4).notes ] ``` ## Chords and harmony To turn a motif into a quiet, faster answer, use `answer = riff.transpose(12).stretch(0.5).scale_velocity(0.6)`, then layer it with `riff.overlay(answer, offset=1)`. Stretch changes beat timing; explicit release times remain in seconds. Overlay retains duplicates and same-pitch overlaps, which can raise levels or block MIDI export. Run `aac inspect` on the resulting score before rendering. Use separate tracks for different instruments or effects. There is no chord object. A chord is several notes with the same start, either a list inside `Pattern.sequence` or a helper: ```python def chord(pitches, start, duration, velocity=0.55): return [Note(pitch=p, start=start, duration=duration, velocity=velocity) for p in pitches] ``` Practical voicing habits that work well with these synthesizers: - Keep chord voicings in a close middle register (roughly C3–C5) and move each voice by step between chords instead of shifting the whole shape. - Put the root in the bass track rather than doubling it low in the chord track; low clusters get muddy. - End sustained chords slightly before the next chord (`3.9` instead of `4`). Same-pitch notes that overlap on one track are rejected by MIDI export. [`02_motif_and_progression.py`](https://audioascode.com/docs/examples/02_motif_and_progression.py) works through a I–vi–IV–V progression with stepwise voicings, a bass line with approach notes, an arpeggio, and a transposed motif. ## Motifs and development A motif is a short `Pattern` you reuse with changes. Useful operations, all plain Python: - **Sequence it**: `motif.transpose(-3)` restates it a minor third lower over a new chord. - **Answer it**: follow it with a contrasting phrase that resolves, `motif.then(answer)`. - **Displace it**: `motif.at(start + 0.5)` shifts it off the beat. - **Thin or thicken it**: play it on one track in the verse and double it an octave up (`transpose(12)`) on another in the chorus. Patterns transpose chromatically by semitones. Keeping a motif inside a key (diatonic transposition) is up to your code. ## Form: sections are your own table The score has no section, clip, or marker objects. Keep a list of sections and compute absolute beats from it: ```python FORM = [("intro", 4), ("verse", 8), ("chorus", 8), ("bridge", 4), ("chorus", 8), ("ending", 2)] sections, beat = [], 0 for name, bars in FORM: sections.append({"name": name, "start": beat, "end": beat + bars * 4}) beat += bars * 4 song_beats = beat ``` Write each part as a function of a section and add its notes with `.at(section["start"])`. Contrast comes from density and register: which tracks play, busier or sparser rhythms, velocity changes, and a cadence (for example a dominant chord) before each return. [`03_song_form.py`](https://audioascode.com/docs/examples/03_song_form.py) builds an 89-second intro / verse / chorus / bridge / chorus / ending piece this way and writes a `sections.json` with beat and second positions. ## Tracks, mixing, and pan ```python Track(name="Bass", instrument="bass_guitar", gain=0.65, pan=-0.1, notes=bass_notes) ``` - `name` must be unique in the song (1–80 characters). The renderer derives each note's noise seed from the song seed, track name, and note index, so renaming a track changes its generated noise. - Each sample is `voice × velocity × track.gain × song.master_gain`, then panned. `gain` and `master_gain` are linear, 0–1. - `pan` is −1 (left) to 1 (right) with equal-power panning. - The renderer only **reduces** the whole mix if its peak exceeds 0.95; it never boosts a quiet mix. If the report shows `gain_applied` below 1, lower your gains rather than relying on it. - A song holds 1–64 tracks and up to 100,000 notes. ## Choosing instruments `aac instruments` lists the 49 playable voices with family, engine, supported controls, and defaults. All are generated from code: modal resonances, a damped string loop, harmonic source/filter models, and electronic oscillators. They are approximations, not calibrated replicas; read each entry's `description`. | Family | Instrument IDs | | --- | --- | | Plucked strings | `guitar`, `electric_guitar`, `bass_guitar`, `harp`, `ukulele`, `banjo` | | Bowed strings | `violin`, `viola`, `cello`, `double_bass` | | Keyboards | `piano`, `electric_piano`, `organ`, `harpsichord` | | Woodwinds | `flute`, `clarinet`, `saxophone`, `oboe`, `bassoon` | | Brass | `trumpet`, `trombone`, `french_horn`, `tuba` | | Drums and percussion | `kick`, `snare`, `hat`, `toms`, `cymbal`, `congas`, `bongos`, `tambourine` | | Pitched percussion | `marimba`, `xylophone`, `vibraphone`, `glockenspiel`, `bell`, `timpani` | | Electronic | `sine`, `triangle`, `pluck`, `bass`, `pad`, `synthesizer`, `drum_machine`, `theremin` | **Drums.** Individual drum IDs (`kick`, `snare`, `hat`, `toms`, `cymbal`, `congas`, `bongos`, `tambourine`) ignore the written pitch. `drum_machine` uses the pitch to pick a generated voice and rejects other pitches: | Pitch | 36 | 38 | 42 | 45 | 49 | 54 | 60 | 64 | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Voice | kick | snare | closed hat | tom | cymbal | tambourine | bongo | conga | `timpani` is pitched and follows the written note. ## Tone: instrument-specific controls `Tone` sets generated-model parameters for a **whole track**. An instrument accepts only the fields in its `tone_controls`; anything else is a validation error, and voices with no controls (`sine`, `triangle`, `pluck`, `bass`, `pad`, `kick`, `snare`, `hat`, `drum_machine`) accept no `tone` at all. | Field | Range | Accepted by | | --- | --- | --- | | `brightness` | 0–1, default 0.5 | Every instrument that has controls | | `decay_seconds` | 0.1–20 | Plucked strings, piano, electric piano, harpsichord, celesta, pitched percussion, toms, cymbal, congas, bongos, tambourine | | `pluck_position` | 0.05–0.45 | Plucked strings, harpsichord | | `breath` | 0–1 | Woodwinds, brass, organ | | `vibrato_depth_cents` | 0–100 | Bowed strings, woodwinds, brass, theremin | | `vibrato_rate_hz` | 0.1–12 | Same as vibrato depth | | `glide_semitones` | −24–24 | Theremin | | `detune_cents` | 0–40 | Synthesizer (side-oscillator offset), mandolin (total uncoupled pair separation) | Omitted optional fields use the catalog default (`default_tone`, `default_decay_seconds`). Ask the catalog in code with `get_instrument("violin").tone_controls`. ```python from audio_as_code import Tone, Track Track( name="Violin", instrument="violin", tone=Tone(brightness=0.55, vibrato_depth_cents=25, vibrato_rate_hz=4.5), notes=notes, ) ``` Piano supports binary sustain through `Track.pedal`, an ordered list of `PedalEvent(beat=..., down=True/False)` values. The pedal extends notes released while it is down; its final release can extend the WAV. See the [complete timing and MIDI rules](https://audioascode.com/docs/piano-sustain.md). Instrument articulation switches and half-pedaling are not implemented. For two tone configurations of one instrument, use two tracks with different `Tone` values. Gain/pan automation, tempo changes, releases and effects are described below. [`04_expressive_controls.py`](https://audioascode.com/docs/examples/04_expressive_controls.py) compares pluck positions, vibrato, breath, glide, detune, and the kit. ## Tempo, movement, and space Use expression to serve the phrase. For example, slow the answer slightly, move a mallet part across the stereo field, and let its last note release into a small generated reverberation: ```python from audio_as_code import ( Automation, AutomationPoint, Delay, Note, Reverb, Song, TempoChange, Track, export_midi, render, ) song = Song( title="Space and breath", bpm=120, beats=8, seed=23, tempo_map=[TempoChange(beat=4, bpm=90)], automation=[ Automation( parameter="master_gain", points=[ AutomationPoint(beat=0, value=0.7), AutomationPoint(beat=8, value=0.5), ], ) ], effects=[Reverb(mix=0.12, decay_seconds=0.8)], tracks=[ Track( name="Mallets", instrument="marimba", gain=0.4, release_seconds=0.4, automation=[ Automation( parameter="pan", points=[ AutomationPoint(beat=0, value=-0.3), AutomationPoint(beat=8, value=0.3), ], ) ], effects=[Delay(time_seconds=0.2, repeats=2, mix=0.1)], notes=[ Note(pitch=p, start=i * 2, duration=1.5) for i, p in enumerate(["C5", "E5", "G5", "C5"]) ], ) ], ) song.save("output/space/score.json") report = render(song, "output/space/song.wav") export_midi(song, "output/space/song.mid") print(song.seconds, song.render_seconds, report["warnings"]) ``` The score lasts about 4.67 seconds; the WAV reserves about 6.03 seconds including the release and effects. `tempo_map` contains ordered step changes. Automation values replace the corresponding static value; they are not multipliers. Linear interpolation is in beats; choose `interpolation="step"` for sudden changes. Endpoint values hold before the first and after the last point. For a fade across a release tail, arrange sufficient beat-space before the score ends; automation points cannot extend past `beats`. Track effects run before master gain; song effects run on the summed mix. The renderer preserves finite tails automatically, and the entire WAV must stay at or below 300 seconds. MIDI exports the tempo changes but omits the automation, effects and releases. Stems contain track processing and master gain but omit master effects, so they do not reconstruct a master-processed mix. The [reference](https://audioascode.com/docs/reference.md#tempo-automation-and-effects) lists supported ranges. ## Full pieces to study The website pairs familiar public-domain scores with new arrangements by an AI agent. Listen to **The classic**, then **Reimagined**, and compare how a change of instruments, rhythm or mood affects the same musical material. Both versions are synthesized from editable scores. Read the [score credits](https://audioascode.com/docs/classic-showcase.md) and inspect the [generator](https://audioascode.com/music/classic_showcase.py); the complete [source download](https://audioascode.com/source.html) includes the runnable project. Four additional original compositions remain in the source project at `examples/full_compositions.py`. They are separate from the website's paired listening program. Each uses a shared `FORMS` table for note placement and section metadata, showing how these techniques scale to a whole piece. | ID | Build function | Form and ensemble | Tempo, beats, length | | --- | --- | --- | --- | | `lanterns-on-the-water` | `lanterns_on_the_water()` | D-major 3/4 chamber waltz: piano, flute, clarinet, violin, cello | 84 BPM, 144 beats, 102.86 s | | `copper-street` | `copper_street()` | E-dorian groove: bass guitar, electric piano, electric guitar, saxophone, trumpet, trombone, kick/snare/hat | 104 BPM, 208 beats, 120 s | | `night-signal` | `night_signal()` | F-minor electronic piece: pad, pluck, sine, bass, synthesizer, theremin, triangle, drum kit | 120 BPM, 224 beats, 112 s | | `clockwork-garden` | `clockwork_garden()` | 7/8 grouped 2+2+3: marimba, harp, vibraphone, glockenspiel, xylophone, bell, timpani, bongos | 112 BPM, 182 beats, 97.5 s | Meter is a convention in code: a 3/4 bar is 3 beats and a 7/8 bar is 3.5 quarter-note beats. MIDI export carries the tempo but not a time signature. Reproduce them from the source folder: ```sh python examples/full_compositions.py # WAV, MIDI, JSON, reports, manifest.json in output/full-compositions/ python examples/full_compositions.py output/my-pieces # another destination python examples/full_compositions.py output/my-scores --scores-only # JSON + MIDI only, much faster ``` Full rendering takes several minutes on a CPU. Seeds are fixed (2101–2104), so a rebuild in the same environment matches; a later synthesis change can alter the sound of the same score. Each manifest entry lists the piece's sections as beat ranges, and its `code_excerpt` is a fragment of the builder that depends on surrounding variables, so run the full source rather than the excerpt. Like everything here, these are code-generated approximations; levels and signal reports are numerical checks, not a judgment of realism or quality. ## Editing an existing score Models are frozen and validated. Edit through data so the result is checked again: ```python from audio_as_code import Song data = Song.load("output/song.json").model_dump(mode="json") data["tracks"][0]["gain"] = 0.4 revised = Song.model_validate(data) revised.save("output/song-v2.json") ``` Avoid `model_copy(update=...)` and `model_construct()`: they skip validation. Rendering and MIDI export revalidate anyway, so errors would surface later and further from the edit. ## Rendering, stems, and MIDI ```python from audio_as_code import export_midi, render report = render(song, "output/song.wav", stems_dir="output/stems") export_midi(song, "output/song.mid") ``` - WAV: 16-bit stereo PCM at the song's `sample_rate` (22050, 44100, or 48000 Hz; default 44100). Stems are named `01.wav`, `02.wav`, … in track order. - MIDI is a score approximation for a DAW or hardware synth: notes, tempo, programs, volume, and pan. It carries step tempo changes but not these synthesizers, `Tone` settings, automation, effects, releases, audio tails or normalization. Drums share channel 10 with no per-track pan; their events are grouped in the first percussion track. Export rejects more than 15 melodic tracks, more than one track per drum ID, and overlapping same-pitch notes on one channel. - The report's measurements (peak, RMS, clipping, silence) are signal checks. They say nothing about whether the music is good. Listen to the result. ## Reproducibility The same score, seed, software versions, and platform produce byte-identical WAV files. Different NumPy versions, platforms, or library releases may change the bytes. Keep the JSON score, the lock file, and the render report (which records `score_sha256`, `engine_version`, `numpy_version`, and `seed`) beside any audio you need to reproduce. --- Source: https://audioascode.com/docs/agents.md # Make music with your agent Tell your coding agent what the scene needs: a video reveal, a game cue, or a presentation entrance with a little too much confidence. Audio as Code gives it the instruments and renderer. Your agent writes the music, delivers the WAV, and keeps the score ready for your next change. It needs a local shell, Python 3.10+ and a writable project; you can give direction in ordinary language. ## Start from the website Use **Copy agent prompt** on the website for a handoff that includes the current site's exact source download and skill URLs. Paste it into your coding agent, then add your musical brief. The agent downloads and extracts the source, reads the bundled [Audio as Code skill](https://audioascode.com/skills/audio-as-code/SKILL.md), and sets up its local environment. If your agent cannot download files, use [Download source](https://audioascode.com/source.html), extract the ZIP locally, and give it that folder. The website's previews are real, pre-rendered framework output. Composition and rendering run locally through the agent's tools. There is no hosted generation API, MCP server, or browser Python runtime. The framework supplies no AI model and requires no provider API key; use the coding agent you already have. The website pairs familiar public-domain music with new agent-written arrangements. **The classic** follows the credited source score; **Reimagined** changes its musical treatment. Both are synthesized by this framework and keep editable scores. The [score credits](https://audioascode.com/docs/classic-showcase.md) identify the historical works and editions; the [generator](https://audioascode.com/music/classic_showcase.py) shows the arrangement code. Use the complete source download to run it. Attribute the historical composition separately from the new arrangement, and never describe either render as a sampled recording. ## Already have the source? Paste this into your agent in the extracted source folder: ```text Read skills/audio-as-code/SKILL.md and use this Audio as Code checkout to make an original 30-second warm, playful instrumental with electric piano, marimba, bass and light drums. Develop a short motif, vary the second half, and give it a gentle ending. Set up the local environment, validate, render and inspect. Deliver WAV, MIDI, editable JSON score, composer source and render report. Tell me whether you were able to listen to the result. ``` The skill is plain Markdown with portable instructions. An agent can read it directly; no vendor-specific plugin or automatic skill discovery is required. Initial installation needs network access or cached dependencies; rendering then runs offline. Install from the downloaded source folder. The package is not published to PyPI. ## Describe the music, then revise it A brief can be as simple as "a quiet, curious 20-second puzzle-game cue with plucked strings and a clear ending." Add duration, mood, instrument preferences, or intended use when they matter. The agent can choose key, tempo, harmony and form. These are also useful starting points: - "Create a 45-second nocturnal electronic cue with a sparse opening, a stronger middle and a resolved ending. Use a recurring three-note idea." - "Write a six-second bright identity sting for marimba and electric piano. Keep it simple, with a memorable final interval." - "Create a gentle 30-second harp and flute loop for a reading app. Keep the texture steady and check the join if you can listen." After hearing the result, ask for a specific change: ```text Keep the melody and length. Make the drums softer, leave more space in the second half, and let the final chord ring longer. Save a new version with WAV, MIDI, score, source and report so I can compare it with the first. ``` Expect actual file links, a short description, duration and relevant warnings. MIDI uses your receiving synthesizer's sounds and will not sound identical to the WAV. All framework voices are procedural approximations. Peak, RMS and clipping checks cannot tell whether a composition sounds good; the agent must say if it could not audition the audio. ## The working loop 1. Read the portable skill, locate or install the source, and discover playable voices and score fields through `aac instruments` and `aac schema`. 2. Interpret the brief and compose original material with a motif, development appropriate to its length, and an intentional ending or loop seam. 3. Save the score and any composer source; validate and correct reported errors. 4. Render WAV with a report, analyze it, and listen when possible. 5. Revise when the brief or evidence calls for it; preserve earlier candidates. 6. Export MIDI and deliver WAV, score, source, report, and requested stems. For executable CLI integration, [`05_agent_loop.py`](https://audioascode.com/docs/examples/05_agent_loop.py) uses [`agent-score.json`](https://audioascode.com/docs/examples/agent-score.json) to demonstrate error recovery and a measured revision. For musical decisions and Python building blocks, see [the composition guide](https://audioascode.com/docs/composition.md). ## Tool calls, exactly Run from the source folder with the package installed (see the [quickstart](https://audioascode.com/docs/quickstart.md)). With uv, prefix each command with `uv run`. `python -m audio_as_code` works wherever `aac` is not on `PATH`. | Step | Command | stdout on success | | --- | --- | --- | | Installed version | `aac --version` | `{"version": "0.1.0"}` | | Playable voices | `aac instruments` | `catalog_version`, `synthesis_policy`, `families`, `engines`, `instruments`, `counts` | | One family | `aac instruments --family woodwinds` | Same, filtered | | One engine | `aac instruments --engine modal` | Same, filtered | | Include planned | `aac instruments --all` | Same; today all 49 entries are available and `counts.planned` is 0 | | Schema | `aac schema` or `aac schema -o schema.json` | The JSON Schema, or `{"output": ..., "schema_version": "1"}` | | Starter score | `aac demo -o demo.json` | `{"output": ..., "title": ...}` | | Validate | `aac validate score.json` | `valid`, `schema_version`, `title`, `bpm`, `beats`, `duration_seconds`, `render_duration_seconds`, `tracks`, `notes` | | Inspect the score | `aac inspect score.json` | Track timing, pitch ranges, polyphony, `readiness` and structured `issues`; no synthesis or file writes | | Render | `aac render score.json -o song.wav --report report.json` | The render report | | Render + stems | `aac render score.json -o song.wav --stems stems` | Report with a `stems` list | | Unnormalized | `aac render score.json -o song.wav --no-normalize` | Report; clipped samples are flagged | | MIDI | `aac midi score.json -o song.mid` | `output`, `tracks`, `ticks_per_beat` (480), `duration_seconds`, `warnings` | | Measure a WAV | `aac analyze song.wav` | `sample_rate`, `channels`, `frames`, `duration_seconds`, `peak`, `rms`, `peak_dbfs`, `rms_dbfs`, `full_scale_samples`, `silent` | Output files are overwritten. The score, WAV, report, and stem paths in one command must all be different. Stale files in a reused directory are not removed, so use a fresh directory per candidate. A typical session: ```sh aac instruments --family pitched_percussion aac validate output/agent-run/v1/score.json aac inspect output/agent-run/v1/score.json aac render output/agent-run/v1/score.json -o output/agent-run/v1/song.wav --report output/agent-run/v1/report.json aac analyze output/agent-run/v1/song.wav ``` ## A strict score This is valid as written (`aac validate` prints `"valid": true`). This example uses the basic fields; `sample_rate`, `seed`, `master_gain`, `pan`, `velocity`, and `tone` are optional. Optional `tempo_map`, `automation`, `effects` and note/track `release_seconds` are documented in the [reference](https://audioascode.com/docs/reference.md). ```json { "schema_version": "1", "title": "Two bars", "bpm": 100, "beats": 8, "sample_rate": 44100, "seed": 5, "master_gain": 0.8, "tracks": [ { "name": "Keys", "instrument": "electric_piano", "gain": 0.4, "pan": -0.2, "tone": {"brightness": 0.4, "decay_seconds": 3}, "notes": [ {"pitch": "A3", "start": 0, "duration": 3.9, "velocity": 0.6}, {"pitch": "C4", "start": 0, "duration": 3.9, "velocity": 0.6}, {"pitch": "E4", "start": 0, "duration": 3.9, "velocity": 0.6}, {"pitch": "G3", "start": 4, "duration": 4, "velocity": 0.6}, {"pitch": "B3", "start": 4, "duration": 4, "velocity": 0.6}, {"pitch": "D4", "start": 4, "duration": 4, "velocity": 0.6} ] }, { "name": "Bass", "instrument": "bass_guitar", "gain": 0.7, "notes": [ {"pitch": "A1", "start": 0, "duration": 3.5, "velocity": 0.85}, {"pitch": "G1", "start": 4, "duration": 3.5, "velocity": 0.85} ] }, { "name": "Kit", "instrument": "drum_machine", "gain": 0.5, "notes": [ {"pitch": 36, "start": 0, "duration": 0.4}, {"pitch": 38, "start": 2, "duration": 0.3, "velocity": 0.7}, {"pitch": 36, "start": 4, "duration": 0.4}, {"pitch": 38, "start": 6, "duration": 0.3, "velocity": 0.7} ] } ] } ``` Rules the JSON Schema cannot express, enforced by `aac validate`: unique track names, notes ending inside `beats`, tone fields supported by the chosen instrument, `drum_machine` pitches from the kit map, and at most 100,000 notes. ## Reading the render report | Field | What to do with it | | --- | --- | | `warnings` | Act on each. Possible messages: silent render, mix attenuated, mix exceeds full scale (with `--no-normalize`), notes too short for the sample grid, a clipped stem | | `gain_applied` | 1 means untouched. Below 1, the mix peaked above 0.95 and was turned down; multiply your `master_gain` by about this value | | `score_duration_seconds`, `tail_seconds` | Score duration and reserved release/effect tail; their total must fit the 300-second render limit | | `before_gain` | Float mix measurements before that attenuation | | `audio` | Float measurements after attenuation: `peak`, `rms`, `peak_dbfs`, `rms_dbfs`, `clipped_samples`, `silent`, `duration_seconds` | | `wav` | The same measurements read back from the saved 16-bit file, with `full_scale_samples` | | `stems` | One entry per track: `track`, `path`, `audio` measurements. A near-zero stem RMS means that part is inaudible | | `score_sha256`, `seed`, `engine_version`, `numpy_version` | Keep these to reproduce the render | `peak_dbfs` and `rms_dbfs` are `null` for digital silence. A low RMS or a high peak is not a musical error by itself. None of these numbers measure realism, taste, or whether the piece works. If your environment cannot play audio, say so in your result instead of implying you listened. ## Errors and recovery | stderr `error` | Typical cause | Recovery | | --- | --- | --- | | `invalid_score` | Malformed JSON (`type` `json_invalid`), field out of range, unknown instrument, unsupported tone field, note past the end | Read each item in `issues`: `path` points into the JSON, `message` says why. Fix and validate again | | `operation_failed` | Missing file, bad command arguments, render over 300 s, MIDI limits, duplicate paths, unreadable WAV | Read `message`; shorten, split, rename paths, or fix the file | ```json {"error": "invalid_score", "issues": [{"path": [], "message": "Value error, track 'Lead', note 9: ends at beat 20.5; song ends at 16.0", "type": "value_error"}]} ``` Error objects may also carry a `hint` string with a suggested next step; treat it as advice, and key your logic on `error`, `issues`, and `message`. Things worth knowing when you parse issues: - Cross-field problems (a note past the end, duplicate names) have an empty `path`. The track and note index are in the message. - A track that fails validation can also produce a second issue, `["tracks"]` "Tuple should have at least 1 item after validation". Fix the first issue; the second goes away. - `validate` passing does not guarantee every backend accepts the score. Rendering fails over 300 seconds including release/effect tails. MIDI export fails with more than 15 melodic tracks, two tracks of the same drum ID, notes shorter than one MIDI tick (1/480 beat), or overlapping same-pitch notes on one channel. Shorten or split the notes, or merge drum parts into one `drum_machine` track. ## Limits to plan around - Tempo changes are ordered steps; there are no continuous tempo ramps, meter/swing fields, sections, clips or instrument articulation switches. Piano supports binary `Track.pedal` events; see the [pedal rules](https://audioascode.com/docs/piano-sustain.md) before targeting an exact duration. - Track gain/pan and song master gain support linear/step automation. Delay and generated reverb can run on tracks or the master; optional note releases extend past note-off. MIDI exports tempo changes but omits these audio controls. Stems omit master effects. - Tone controls are per track. Use another track for another articulation. - 1–64 tracks, up to 100,000 notes, `beats` up to 65,536, WAV renders up to 300 seconds including tails. Long renders use hundreds of MB of RAM; keep iteration renders short. - Rendering is offline and takes real CPU time; orchestral voices are slower than the electronic ones. - Instruments are code-generated approximations. Do not describe them as recordings or as indistinguishable from acoustic instruments. - Renders are deterministic for the same score and environment, not across every NumPy version or platform. ## Python instead of the CLI An agent that can run Python can use the same operations in-process: ```python from audio_as_code import Song, analyze_wav, instrument_catalog, render catalog = instrument_catalog() # same data as `aac instruments` song = Song.load("output/agent-run/v1/score.json") # raises pydantic.ValidationError report = render(song, "output/agent-run/v1/song.wav") print(report["gain_applied"], report["warnings"], analyze_wav(report["output"])["rms"]) ``` See the [reference](https://audioascode.com/docs/reference.md) for every function, and the [composition guide](https://audioascode.com/docs/composition.md) for musical techniques. --- Source: https://audioascode.com/docs/integrations.md # Put the music in your project Make a reveal land, give the boss fight some nerve, or let the last slide take a bow. Your agent composes with Audio as Code, brings the WAV into your creative project, and keeps the score for revisions. These recipes connect your tools through a portable skill, local commands and ordinary audio files. Audio as Code supplies instrumental music and musical cues. Use your project's other tools for narration and sound effects. | Your creative workflow | Try this brief | What the next tool receives | | --- | --- | --- | | Video with Hyperframes | "A 12-second launch for a very serious moon-cheese company. Marimba curiosity, a bass entrance at second 4, a confident ending." | WAV on the video timeline; score/source retained for revisions | | Game with a coding agent | "A tiny dragon thinks this is the final boss fight. Give me a 16-second battle cue and a separate 2-second victory sting." | Two WAV assets for the game's existing audio system, plus editable scores | | Presentation with an agent | "A quarterly report with a villain arc: a 6-second ominous opening and a 4-second triumphant reveal." | Separate WAV cues placed on the relevant slides; playback configured by the presentation tool | For looping game music, ask for a loop candidate and audition the join in the game. Matching endpoints and managing release/effect tails need deliberate work; a render is not automatically a seamless loop. For narration-heavy videos or presentations, request sparse music and adjust its level in the consuming tool. The source project includes an executable set of original creative briefs: ```sh uv run --no-dev python examples/creative_workflows.py output/creative-workflows ``` Run it from the extracted source folder after setup. It delivers a 30-second video cue with a reveal at 20 seconds, a 12-second game loop plus two-cycle preview, and a nine-second presentation sting. Each has an initial version and a revision that preserves specified material, with WAV/MIDI/JSON, composer, inspection, reports and measured checks. Use a fresh output folder. Add `--scores-only` for quick editing or `--brief game --version v2` for one candidate. The loop uses short articulated rests and no effect tails; it is not a general wet-loop exporter. Full rendering checks duration, finite stereo output, clipping, reproducibility and the PCM join, and takes several minutes. Listening remains a separate check. The source guide `docs/creative-workflows.md` explains the method. ## Start in your creative project Use the website's **Copy agent prompt**, or download the full [source project](https://audioascode.com/source.html) and give your agent the extracted folder. Ask it to read [the portable skill](https://audioascode.com/skills/audio-as-code/SKILL.md), then describe the music and where it will go. The agent needs a shell and Python 3.10+. If setting it up directly, run these in the folder containing `pyproject.toml`: ```sh uv sync --locked --no-dev uv run --no-dev aac instruments uv run --no-dev aac schema ``` Use the [quickstart](https://audioascode.com/docs/quickstart.md) for the pip/virtual-environment alternative. Audio as Code is not published to a package registry. Its renderer needs no model or provider key; your chosen agent has its own account and runtime requirements. ## Codex: keep the composer beside the project Open the project in Codex and ask it to read `skills/audio-as-code/SKILL.md` from the extracted Audio as Code folder. That direct instruction requires no skill installation. For repeat use, copy the skill folder into your creative project's `.agents/skills/audio-as-code/`. Codex discovers repository skills there; in Codex CLI or the IDE extension, invoke it as `$audio-as-code`. [Official Codex skill instructions](https://learn.chatgpt.com/docs/build-skills). ```text Use the Audio as Code skill and the local source checkout. Compose an original 12-second moon-cheese launch cue: curious marimba, bass enters at second 4, confident ending. Put v1 in output/moon-cheese/v1/. Deliver WAV, MIDI, score, composer source and report. Check the actual WAV duration including tails. ``` Keep the source folder path in the brief if the creative project and framework are separate. To revise: "Keep the motif and the 12-second cut. Make the opening more suspicious, then brighten the final chord. Save v2." ## Claude Code: the same skill, another host Claude Code can also read the portable file directly. For project discovery, copy `skills/audio-as-code/` into `.claude/skills/audio-as-code/` in the creative project, then invoke `/audio-as-code` with the same musical brief. Claude Code's documented project skill directory and slash invocation support this layout. [Official Claude Code skill instructions](https://code.claude.com/docs/en/skills). The agent still composes and calls the local CLI. Copying the skill does not install Python or the renderer. Preserve WAV, JSON and source files in the project so either agent can continue the arrangement later. ## Hyperframes: music becomes a video asset Make the score in Audio as Code and the visuals in Hyperframes. The connection is a local WAV file: Hyperframes places it on its composition timeline and mixes it into the rendered video. Its official example uses an `