Session
The Session view is the studio’s clip-launching surface: a grid of scenes,
one per section, that you fire live instead of following the linear timeline.
Where the Arrange view plays song.arrange([...]) top to
bottom, the Session view lets you loop any single song.section(...) on its own
and jump between them by hand — quantized to the next bar so nothing falls out
of time.
There is no separate scene data to author. Sections are the scenes. The same bodies the Arrange view dry-runs are the things you launch here; the only difference is when and how long they play.
What it projects
Section titled “What it projects”- One scene per section, in declaration order — every
song.section(name, bars(n), ...)becomes a launchable cell. The scene’s bar length is the section’s own length. - The live scene, highlighted while it loops. This is the runtime’s
getLoopSection()— the section currently looping in place of the arrangement. - A queued scene, shown as pending until the bar boundary. Launch a scene
while playing and it doesn’t cut in mid-bar; the runtime holds it as
getQueuedScene()and swaps at the nextonBar, so the highlight moves the instant the loop turns over. - The same bar-clock playhead as every other view, ticking through the looping section.
A song to launch scenes from is just a normal song — each section is a cell:
import type { Song } from "@barline/runtime";import { bars, beats, notes, r } from "@barline/core";
export const config = { bpm: 148, key: "F minor" };
export default function (song: Song) { const kick = song.track({ name: "kick", output: { kind: "synth", synth: { id: "kick909" } }, }); const bass = song.track({ name: "bass", output: { kind: "synth", synth: { id: "bass" } }, });
// Each section is a scene you can launch from the Session grid. song.section("verse", bars(4), () => { kick.play(r`x . . . x . . . x . . . x . . .`); bass.play(notes(["F1", null, "C2", null], beats(0.5))); });
song.section("drop", bars(4), () => { kick.play(r`x . x . x . x . x . x . x . x .`); bass.play(notes(["F1", "F1", "C2", "Eb1"], beats(0.25))); });
// The linear order — what Arrange plays, and what Session falls back to. song.arrange([ { section: "verse", repeat: 2 }, { section: "drop", repeat: 4 }, ]);}How it writes back
Section titled “How it writes back”Like Arrange, the Session view is performance, not authoring — launching a scene never touches your code. It drives transport state that resets with the runtime:
- Click a scene to launch it. While playing it queues to the next bar;
while stopped it goes live immediately, so the next
play()starts on that section instead of the arrangement. - Stop the scene (or launch
null) to release back to the linear arrangement — the timeline picks up fromsong.arrange([...])again at the next boundary. - Double-click a scene to jump the editor to that section’s code, the same hand-off the Arrange blocks make to the Clip detail panel.
To change what a scene plays — its pattern, length, or which tracks it
touches — edit the matching song.section(...); the cell re-derives on the next
compile. To change the fallback order, edit song.arrange(...).
From the AI
Section titled “From the AI”The same scene launching is exposed to the MCP-connected AI through the
transport tool: launchScene { section: string | null } queues a section as
the live scene, quantized to the next bar, with null releasing back to the
linear arrangement — identical semantics to clicking a cell. That lets the AI
audition a single section in a loop while it edits, then drop back into the full
arrangement when it’s done.