Recording a take
A take is created, cued, and stopped. Creating reserves the paths and writes the manifest before a single frame; starting is the cue.
A take is three calls. The first does everything that can be done before recording — snapshots the armed set, resolves the destination, pre-flights the disk, reserves every file name, writes the manifest — so that by the time the cue comes there is nothing left to decide.
Create, start, stop
| call | does | state after |
|---|---|---|
POST /takes |
snapshot the armed set, reserve paths, pre-flight the disk, write the manifest | created |
POST /takes/{id}/start |
the cue: every stream’s writer starts on frames that are already flowing | recording |
POST /takes/{id}/stop |
finalise every writer and the manifest | complete |
→ { "name": "Episode 12", "expectedDuration": 3600 }← { "take": { "id": "20260912T040433-fd9q", "name": "Episode 12", "state": "created", "created": "2026-09-12T04:04:33.235Z", "outputRoot": "/Users/len/Movies/Rheocles", "destination": "takes/2026-09-11/210433-episode-12", "streams": [ { "id": "display:F65F9C53-…", "kind": "display", "name": "Prompter XL", "path": "prompter-xl.mov", "codec": "hevc", "framesWritten": 0, "events": [] }, { "id": "microphone:…", "kind": "microphone", "name": "Scarlett 2i2 USB", "path": "scarlett-2i2-usb.wav", "codec": "pcm_s24le", "framesWritten": 0, "events": [] } ], "markers": [], "settings": { "codec": "hevc", "expectedDuration": 3600 } }, "warnings": [] }The answer is the manifest, with state: created. Nothing is recording
after create. This is the call Pteroprompter makes to learn where the
footage will live before frame one, so the cue log can name files that do
not exist yet. A created take can sit as long as you like; start is a
separate, deliberate call.
POST /record is create followed by start in one call, with the same body
and the same answer. It is what the popover’s Record button does. Use it
when you do not need the paths ahead of time.
Where the files go
Every path is relative: the take folder to the output root, each file to the take folder. Join them if you need an absolute path; store them as given.
| default | override | |
|---|---|---|
| take folder | takes/<yyyy-MM-dd>/<HHmmss>[-<name slug>], local date and time |
destination, relative to the output root |
| each file | the stream’s name, slugged — prompter-xl.mov, scarlett-2i2-usb.wav; windows are window-<bundle id>.mov; -2, -3 on a collision |
files, a map of stream id → file name |
The same destination twice is a 409 conflict, unless overwrite: true. A destination is taken if the folder exists and is not empty.
Rheocles never silently suffixes a path; a client that named a path meant
it, and a file that quietly became -2 is how a take goes missing.
Disk pre-flight
Create refuses if there is not room. The armed set’s bitrates ×
expectedDuration (seconds; default 1800) gives an estimate — rough until
the codec tiers are measured — and:
- free space short of the estimate →
507 insufficient_storage, nothing created; - free space under twice the estimate → created, with a line in
warnings.
If a write fails mid-take — disk full, volume unmounted — the take stops
cleanly, every file is finalised as far as it got, and the manifest is
marked incomplete with the reason. Nothing is deleted.
One at a time
While a take is recording, POST /takes and POST /record answer
409 take_active, naming it. Stop it first.
A take that was created but never started is superseded by the next
create: its manifest is rewritten incomplete with reason
superseded before start, since nothing but the manifest exists on disk.
Concurrent takes with disjoint stream sets — a background screen recording
spanning several camera takes — are conceivable and deliberately deferred.
POST /takes with no armed streams is a 400.
What lands on disk
Rheocles/ ← output root, one field on GET /└── takes/2026-09-11/210433-episode-12/ ← destination ├── manifest.json ├── prompter-xl.mov HEVC, native resolution and rate, timecode track ├── elgato-4k-x.mov ├── scarlett-2i2-usb.wav Broadcast Wave, 48 kHz 24-bit, bext time reference └── system-audio.wav| kind | container | codec |
carries its time as |
|---|---|---|---|
| camera, display, window | QuickTime .mov, fragmented |
hevc (default) or prores — ProRes 422, one setting for the whole take |
a tmcd track, time-of-day |
| microphone, system audio | Broadcast Wave .wav |
pcm_s24le, 48 kHz |
bext TimeReference, samples since midnight |
Video files carry no audio track; audio is always its own file. Streams
record at the format the device is delivering when armed — active on the
stream — and per-stream codec, resolution and rate are deliberately not
exposed.
Crash safety
Every MOV is written with a movie fragment interval, so a process that dies mid-take leaves a file that plays up to the last fragment. The manifest is rewritten atomically — temp file, then rename — on every state change, so it is never half-written. A dead process leaves playable files and a truthful manifest; that is a design constraint, not a recovery feature.
Broadcast Wave stops at 4 GB. At 48 kHz, 24-bit, that is 144 kB a second per channel: about 8.3 hours mono, about 4.1 hours stereo. It is the format’s limit and Rheocles does not work around it.
No pause
There is no pause. Stop, and start a new take. A paused file would need either a gap in its timecode or a lie in it, and neither is worth a button.