Repository navigation
v0.2.0
0.1.0 could be told what to say and how to say it. This release is most of what a broadcast needs around that turn: the sound under it, the script it comes from, the motion it moves with, the window an operator frames it in, and the file it is recorded to.
The boundary is unchanged. There is still no provider SDK in the dependency tree, no API key and no prompt state, and everything below is reachable from the control API, the MCP tools, the CLI and the panel alike.
Background music
MP3 and FLAC from a local show directory, played under one page-owned audio graph, with its own level, looping, a bounded crossfade and its own effects. Reachable from the panel's BGM tab, the bgm MCP tool, yarn ctl bgm, and inline in a line as [@bgm play]. A different-track play crossfades; the first play uses only the fade-in.
Scripts on disk
A run of turns, written down as a file. A line is a TurnRequest and a setup entry is a Command — the wire vocabulary reused rather than re-described, so a script cannot become a second dialect. The CLI runs one, the server serves the roster, and the panel picks one onto the queue.
Motions off disk
Keyframe motions load from the show directory beside the built-in procedural table without joining it. A loaded motion may not take a built-in's name and is never drawn by the idle autopilot: what the performance table names has to keep meaning what it means, on every machine.
Recording
A take written to a file by the renderer that is already on air, with no compositor in the way.
The stage window and the native shell
A monitor surface showing exactly the frame a browser source sees, an address composer for pasting into OBS, and yarn shell — the panel and the stage as one native application. Whether a renderer makes a sound is still decided by its URL and by nothing else.
Typed inline cues
[@perform id], [@expression id], [@gesture id], [@hop id], [@camera …], [@slide n] and [@bgm …] are ordinary line text and travel through speak and script queues alike, checked in the panel before the line is said.
Speech on a socket
The sidecar binds no port at all. It answers on a UNIX socket in a directory it makes 0700: the control server is its only caller, so there is no browser for it to be reachable from, and loopback would have left it open to every process on the machine.
Several renderers at once
The panel's preview, the stage window and whatever OBS has open are three renderers on one queue, which is the normal case rather than the odd one. One line ending said by three renderers is one event, and one line wanted by three is one synthesis.
Fixes
Arms are posed in the character's frame rather than world X; five gestures no longer drive joints to their stops; fingers fan in the palm plane so a V reads as a V; the upper shot stays below the bust edge on a short torso; a queue held for framing survives an avatar swap; a room changed mid-document no longer paints over the page; and a script whose filename is stored decomposed now opens on Linux as well as on macOS.
Development
The tree is now verified on GitHub Actions — lint, typecheck, the suite and the bundle — with coverage reported for the layers that run without an avatar.
The avatars and the voice are not in the box and are not ours to redistribute: a checkout is the runtime, not the characters it was built against. See the README for what to bring.