Architecture
GAS turns a score into requests for a music model, and streams the model’s audio back to your application:
score → language → timeline → session → renderer → connector → modelYour application loads the score into a session, chooses a connector for the model, and supplies provider access. The renderer schedules the score’s intent against a clock and sends it through the connector. Audio comes back the same way to the host’s playback engine: Web Audio in a browser, or whatever output another host supplies.
Runtime boundaries
| Component | Responsibility |
|---|---|
| Language | Parse, validate, and compile a score into a timeline |
| Protocol | Shared payload types and schemas |
| Core | Validate commands and derive effective state through the cascade |
| API | The session your application uses: loading, commands, playback, and events |
| Renderer | Convert musical time to real time, schedule intent, and manage generation |
| Connector | Translate intent into one model’s requests, and stream its audio back |
| Browser host | Combine the runtime with Web Audio playback and an audible playhead |
Everything model-specific lives in the connector. The score, timeline, session, and renderer describe intent the same way whichever model plays it.
Core works from input state alone, with no clock, network, or model. You can test commands and layering without generating audio.
Dependencies point toward the shared contracts. Protocol and highlight depend on nothing else in GAS. Language, Core, notation, and each connector depend on protocol. The renderer uses Core and notation, and the API uses language and Core. Nothing depends on a connector: the host creates one and passes it in.
Packages and imports
Install the umbrella package to get a set of packages tested together:
npm install --save-exact @luna-estelar/gas@0.2.0Import each part from its subpath of @luna-estelar/gas:
| Subpath | Surface | Individual package/version |
|---|---|---|
Root and /api |
Session, source helpers, errors | gas-api 0.2.1 |
/browser/session |
Browser session and Web Audio composition | gas-browser 0.3.0 |
/browser/audio |
ChainedSourceEngine, context and support helpers | gas-browser 0.3.0 |
/browser/capture |
PcmCapture and encodeWave | gas-browser 0.3.0 |
/browser/compile |
Compile, highlight, and preview live input | gas-browser 0.3.0 |
/browser/timeline |
Timeline view models and clock formatting | gas-browser 0.3.0 |
/browser/inspect |
Effective state inspection without the parser | gas-browser 0.3.0 |
/language |
Compile documents and parse live fragments | gas-language 0.1.3 |
/renderer |
Scheduling against a host clock, generation, audio delivery | gas-renderer 0.3.0 |
/lyria |
Lyria connector, capabilities, configuration helpers | gas-connector-lyria 0.2.1 |
/protocol |
Shared types and helpers | gas-protocol 0.3.0 |
/protocol/validation |
Precompiled timeline payload validation | gas-protocol 0.3.0 |
/core |
Session semantics, cascade, musical-time conversion | gas-core 0.2.1 |
/notation |
Supported Alda subset, note events and MIDI encoding | gas-notation 0.1.3 |
/highlight |
Syntax-presentation helpers and token classes | gas-highlight 0.1.3 |
The individual packages are published too. If you install them separately, use a matched set rather than mixing versions, and import browser features from matching subpaths such as @luna-estelar/gas-browser/session.
The umbrella also includes gas-cli 0.1.3, which supplies the gas compile command:
npx @luna-estelar/gas@0.2.0 compile song.gas --out song.timeline.jsonInstall @luna-estelar/gas-cli on its own if you only need the command. Diagnostics describes its error output.
Everything is ESM, with bundled TypeScript declarations. Browser applications need a bundler; there is no script-tag build. The umbrella, CLI, parser, API, browser, and highlight packages require Node 22. Protocol, Core, notation, the renderer, and the Lyria connector declare Node 20.
Imports only load what they need. Only /language and /browser/compile load the parser directly; the session loads it on demand when it compiles source or live text, and loadTimeline never needs it. Only /lyria loads Google’s SDK, so import it only where you create the connector.
Connector interface
A connector translates effective state into model requests, streams audio chunks to an AudioSink, and declares how it handles each kind of intent. It does not parse scores or schedule events.
The Connector interface in /protocol has:
| Method | Responsibility |
|---|---|
describe() |
Identify the model, audio format, and capabilities |
open(settings) |
Open with credentials and settings |
prepare(initialState, config) |
Prepare the initial effective state |
start(sink, timing, runId) |
Start streaming a run |
update(update, requestedBoundary) |
Apply intent and report its applied boundary |
stop(runId) |
End a run |
close() |
Release the connector |
setGenerationPaused?(paused) |
Optional generation flow control |
validateConfig?(config) |
Optional configuration validation |
A connector’s capabilities table rates every intent as supported, approximated, or unsupported. The session warns when a score uses intent the model will approximate or ignore. Only the session can produce these warnings; the compiler doesn’t know which model will play the score.
Connector failures are classified as auth, network, quota, provider, or internal, with the raw WebSocket close code when there is one. Connectors sanitise provider error text before passing it on. Close codes from 4000 to 4999 belong to whoever operates the endpoint, so the host interprets them.
The interface is in-process TypeScript and still evolving, with no conformance suite or frozen wire contract. The public Lyria connector is the reference implementation, and Supported models describes how it behaves. To discuss a connector for another model, get in touch.
What stays the same between runs
The timeline is the same every time a score compiles: the same tracks, section spans, and intent at the same musical positions. The audio is not. The model chooses the notes, phrasing, and sound, so each run is a new performance, and GAS has no model-independent way to repeat one. To keep a performance, record it with /browser/capture, which supplies PCM capture and WAV encoding.
Test your application’s commands and musical structure separately from the audio a model produces.
Versions and stability
Packages release independently, and their 0.x APIs can change in a minor release. Pin the umbrella version and commit your lockfile.
The timeline and session schemas have their own format version, currently 1.0, separate from the package versions. Timeline format covers the schema.
FMOD integration and C++ or Python ports are not released or scheduled. If a model or host matters to your project, tell us what you need.
Terms
| Term | Meaning |
|---|---|
| Score, document | GAS text containing globals, tracks, sections, and calls |
| Arrangement | The section calls in playback order; a reprise is a repeated call |
| Layer, build | A track’s contribution; a build introduces layers or density over time |
| Timeline | Compiled intent placed in musical time |
| Effective state | Values and track activation derived for a position |
| Override, cascade | A covering value, and the precedence used to resolve it |
| Session | The application interface for loading, playback, commands, and events |
| Renderer | Scheduling and generation against a clock |
| Connector, capabilities | A model adapter, and its declared support for each kind of intent |
| Wiring | The host’s composition of a session, renderer, and connector |