RheoclesREE-oh-kleez

Free · Open source · MIT

Every stream, its own file,
on one cue.

Rheocles records every camera, microphone, display and window on your Mac at once — each to its own file, started on one cue and stamped with time-of-day timecode, so an editor syncs them in one click.

No compositor. The layout is a decision for the edit, not for the moment the light goes red. Arm what you might want, cue once, cut later.

Loopback only. An HTTP and WebSocket API, one bearer token, zero clicks to pair.

Read the docsSource on GitHubApple Silicon · macOS 15 or later

coming soonbrew install --cask artisan-build/tap/rheocles

The whole product, basically. Six children, six cradles, one hand raised. Nobody gets swallowed.
take ep12cue 14:02:17:00stop 14:14:40:12files 5
Camera · FaceTime HD
14:02:17:00cam-facetime.mov
Display · Prompter
14:02:17:00display-prompter.mov
Microphone · SM7B
14:02:17:00mic-sm7b.wav
System audio
14:02:17:00system-audio.wav
Window · Keynote
14:06:17:00window-keynote.mov
cue
+4:00 · joins
stop
Each bar is one file. The stamp is the time-of-day timecode of its first written frame, so the late one lands at four minutes in any editor without the manifest — and the manifest agrees.

Why it exists

OBS records one stream.

A compositor decides the layout before you record and hands you one file afterwards. That is fine for a live stream, where the layout is the show.

It is the wrong shape for a take you are going to cut. Pteroprompter wanted the camera, the prompter screen, the microphone and the system audio as four files, started together, with no compositor in the way — so the edit could decide what goes where, and a scene change on the day would not be a decision baked into the footage.

Rheocles is that. Arm what you want, cue once, and every armed stream lands as its own file with its own time-of-day timecode. The camera writes at native resolution and rate; the microphone writes Broadcast Wave; nothing is scaled, mixed or laid out on the way to disk.

The distinction

It is not a compositor.

a compositorRheocles
Gives youone fileone file per stream
The layout is decidedbefore you recordin the edit
Files syncby eyeby timecode
A crash leavesa corrupt fileplayable files and a truthful manifest

If a composited stream is what you want, OBS is excellent and free, and you should use it. Rheocles is built around the moment after the take, not the moment during it.

Arming

Armed means live.

An armed stream has its capture session running — frames flowing, discarded — so that when the cue comes, the writer starts on frames that already exist rather than waiting hundreds of milliseconds for a device to spin up. That is the entire reason there is an arm button.

arm / disarm
Device live or not. Never stamps anything, never writes.
join / leave
While a take is active. Join starts that stream's writer now, adds its file to the manifest, and stamps it with the time it actually began. Leave finalises that file; the stream stays armed and the take continues for the others.
the cue
start on a take joins every armed stream at once. A stream that joins four minutes in is stamped four minutes in, and an editor places it at four minutes.

Armed costs you

Armed streams use CPU and hold their devices while armed. That is the trade, and the menu bar icon makes armed state obvious so nobody leaves six cameras live overnight by accident.

Late joining is not a script

Late joining is for saving CPU on a heavy stream you already know will not need edit flexibility. It is not a way to script a take. Arm everything you might want, record, and cut in the edit; a script that toggles windows in and out of a take mid-read has misunderstood the tool.

Timecode

Every file carries its own time.

A time-of-day timecode track in every video file. A Broadcast Wave time reference in every audio file. Both stamped from the host clock at the stream's first written frame.

So the files sync in an editor without the manifest, and the manifest agrees with the files. Time-of-day rather than take-relative because it is the production standard: everything from one day shares a timeline, and two NTP-synced capture boxes produce files that line up — the remote scenario, solved as a side effect.

Drift is measured, not assumed. Frames written × frame duration against host elapsed, per stream, in status events and in the manifest. An unmeasured drift is reported as absent, never as zero.

Midnight. A stream that joins after midnight into a take that started before it gets a time-of-day reference near zero, and an editor syncing by timecode will place it before the take rather than after. Every camera and field recorder shares this; there is no clean fix inside the files. The manifest records absolute UTC and is unaffected, and is the authoritative source across midnight. Broadcast Wave files stop at 4 GB — many hours at 48 kHz, 24-bit, and said here so it is not a surprise.

The manifest

The manifest is the take.

manifest.json in the take folder, written at create and rewritten atomically — temp file, then rename — on every state change. At any instant it is either the previous complete version or the next. A dead process leaves playable files and a truthful manifest.

ep12/manifest.json — the shape the spec describes; Engine finalises the names
{
"take": { "id": "tk_7f3a", "name": "ep12", "state": "recording",
"created": "2026-09-11T14:02:09Z", "started": "2026-09-11T14:02:17Z" },
"streams": [
{ "id": "cam-1a2b", "kind": "camera", "name": "FaceTime HD Camera",
"path": "ep12/cam-facetime.mov", "codec": "hevc",
"started": "2026-09-11T14:02:17.004Z", "timecode": "14:02:17:00",
"frames": 21540, "driftMs": -3 },
{ "id": "win-9c0d", "kind": "window", "name": "Keynote",
"path": "ep12/window-keynote.mov", "codec": "hevc",
"started": "2026-09-11T14:06:17.021Z", "timecode": "14:06:17:00",
"frames": 7200, "driftMs": null }
],
"markers": [ { "t": 38.7, "label": "cold-open out" } ]
}

Everything downstream consumes the manifest; nothing guesses from a folder listing. Take IDs are the handle, paths are the answer, and every path is relative to the output root.

The API in one glance

Create, cue, stop.

POST /takes reserves every file path and writes the manifest before frame one — so a client knows where the files will live. start is the cue.

localhost:7447 — three calls, one take
TOKEN="$(cat ~/Library/Application\ Support/Rheocles/token)"
H="Authorization: Bearer $TOKEN"
curl -s -H "$H" -X POST localhost:7447/takes -d '{"name":"ep12"}'
# → { "id": "tk_7f3a", "files": { "cam-1a2b": "ep12/cam-facetime.mov", … } }
# paths reserved, manifest written. Nothing is recording.
T=localhost:7447/takes/tk_7f3a
curl -s -H "$H" -X POST $T/start # the cue
curl -s -H "$H" -X POST $T/markers -d '{"label":"cold-open out"}'
curl -s -H "$H" -X POST $T/stop
// the same command set, both transports live at once
const es = new EventSource('http://127.0.0.1:7447/events') // SSE
const ws = new WebSocket('ws://127.0.0.1:7448/') // full duplex

A bearer token, always, on every transport. It is provisioned to a file — mode 0600 — so any app running as you is paired with zero clicks; anything that cannot read the file gets it as a pairing code in the popover. Loopback only in the MVP: the threat is a web page in your browser talking to localhost:7447, and a token is the defence.

The name

Known for the flow.

ῥέω

Rheō is "to flow". It comes down from the Proto-Indo-European root *srew-, which came through Germanic as the English word stream. So rheo- does not resemble "stream"; it is "stream", two thousand years upstream.

The -cles is kleos, renown — the same suffix as its sibling Sonocles. Known for the flow.

Rhea, the Titaness, is attached to rheō by Plato, and Plato was making a pun. We are borrowing it, not asserting it. Both halves are Greek this time, which Sonocles could not say, and will hear about.

The stone. Kronos swallowed his children so there would only ever be one of him. Rhea handed him a rock in a blanket and kept the sixth one separate. Roughly our position on compositing.

Anyway

Here is a Titaness
with a rig.

Six of them, each in their own cradle, all started on the same word. The one on the end arrived four minutes late and has been placed, correctly, four minutes in.

Nobody in this picture is being swallowed. Which is, in fairness, the goal.