RheoclesREE-oh-kleez

Free · Open source · MIT

Every available stream,
in its own file,
on one cue.

Rheocles records the cameras, microphones, displays and windows you choose, all at once — each in its own file, started on one cue and stamped with time-of-day timecode, so they line up in your editor in one click.

The Rheocles menu bar popover. Displays, windows, cameras and microphones are listed with a switch each; the Elgato 4K X camera and the Scarlett 2i2 microphone are armed, the BenQ display and two windows are listed but not. The take bar is idle: a Record button, a take name field, and an unticked Also save a single file.

Separate files for your edit, or one file to send straight away — you choose per take. Arm what you might want, cue once, cut later.

Local only (for now). An HTTP and WebSocket API, one bearer token, zero clicks to pair.

Download for MacRead the docsSource on GitHubv0.1.3 · Apple Silicon · macOS 15 or later

orbrew install --cask artisan-build/tap/rheocles

Rhea, in a blue flounced dress, stands behind a bench of six cradles with one finger raised to count in; six infants watch her hand. Kronos sulks, arms folded, in a doorway at the left.
The whole product, basically. Six children, six cradles, one hand raised. Nobody gets swallowed.
take episode-12cue 14:02:17:00stop 14:14:40:12files 5
Camera · Elgato 4K X
14:02:17:00elgato-4k-x.mov
Display · Prompter XL
14:02:17:00prompter-xl.mov
Microphone · Scarlett 2i2
14:02:17:00scarlett-2i2-usb.wav
System audio
14:02:17:00system-audio.wav
Window · Keynote
14:06:17:00window-com.apple.iWork.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 your 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 your 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 your 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.

manifest.json — the shape docs/openapi.yaml pins
{
"id": "20260911T210217-fd9q", "name": "Episode 12", "state": "recording",
"created": "2026-09-11T21:02:09.412Z", "started": "2026-09-11T21:02:17.004Z",
"outputRoot": "/Users/len/Movies/Rheocles",
"destination": "takes/2026-09-11/140217-episode-12",
"streams": [
{ "id": "camera:0x2300000fd9009c", "kind": "camera", "name": "Elgato 4K X",
"path": "elgato-4k-x.mov", "codec": "hevc",
"started": "2026-09-11T21:02:17.004Z", "timecode": "14:02:17:00",
"framesWritten": 21540, "events": [ { "t": 0, "type": "join" } ] },
{ "id": "window:11597", "kind": "window", "name": "Keynote",
"path": "window-com.apple.iWork.Keynote.mov", "codec": "hevc",
"started": "2026-09-11T21:06:17.021Z", "timecode": "14:06:17:00",
"framesWritten": 7200, "events": [ { "t": 240.017, "type": "join" } ] }
],
"markers": [ { "t": 38.7, "label": "cold-open out" } ],
"settings": { "codec": "hevc", "expectedDuration": 1800 }
}

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":"Episode 12"}'
# → 201, the manifest: id, destination, a path per stream. Nothing is recording.
T=localhost:7447/takes/20260911T210217-fd9q
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. Local only (for now): 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.

Rhea hands a swaddled rock to a delighted Kronos, straight-faced. A small baby peeks out from behind her skirt.
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.

Rhea reclines on a couch with a cup; six infants in a row of cradles each hold a small tablet. The sixth cradle sits a little apart on a green footstool, its baby waving.
Nobody in this picture is being swallowed. Which is, in fairness, the goal.