recorderbox
recorderbox · output · schema v1Video + audio RECORDER sink. Patch a picture into IN and a stereo soundtrack into A·L / A·R, type a filename, hit RECORD, and RECORDERBOX captures it to a HIGH-QUALITY, CRASH-RECOVERABLE H.264 MP4. Model: like OUTPUT, it monitors its video input (live preview + an OUT pass-through so you can chain it inline) while ALSO recording. Encoding runs through WebCodecs (H.264 video at ~14 Mbps VBR, AAC stereo audio) muxed by Mediabunny (MPL-2.0) into a FRAGMENTED MP4 (fastStart:"fragmented") streamed to OPFS scratch via a dedicated Worker holding a FileSystemSyncAccessHandle — the only browser API that writes real disk synchronously AND survives a tab crash. The OPFS scratch is named after your sanitized filename + a .partial marker + the start epoch, so the partial carries your intended name. Controls: an editable FILE field (node.data.filename, synced to rack-mates), a SIZE / quality selector (HIGH / BALANCED / SMALL), and a RECORD ON/OFF toggle. The SIZE selector trades file size against quality: HIGH is the original ~14 Mbps H.264 (the DEFAULT — no change for existing racks); BALANCED and SMALL PREFER a more efficient modern codec (AV1, then VP9, capability-probed at the actual recording resolution via VideoEncoder.isConfigSupported) at a lower bitrate, longer keyframe interval, and lower audio bitrate, falling back to a lower-bitrate H.264 where no modern codec encodes. The codec stays inside the same fragmented MP4 container, so the extension (.mp4) and crash-recovery semantics never change — only the bytes inside get smaller. SMALL is typically ~70-80% smaller than HIGH; BALANCED ~55-65% smaller, both at near-imperceptible quality cost on synth/visualizer output. The chosen tier syncs to rack-mates (node.data.quality) and is locked while a take is in progress. NO "SAVE AS" PROMPT (folder model): on Chromium, pressing RECORD picks a destination FOLDER ONCE via showDirectoryPicker (a valid user gesture); thereafter the recording auto-writes into that folder using the FILE box directly — no per-save dialog — and the folder is remembered so the next record needs no prompt at all. The ONLY remaining prompt is an OVERWRITE confirm when a file with the target name already exists (cancelling the folder picker, or declining the overwrite, does NOT start recording). The chosen FileSystemDirectoryHandle is persisted into the recovery manifest. On STOP the finished MP4 is remuxed to a flat (NLE-importable) container and STREAMED into the folder (never read whole into memory — a long 4K take can be GBs). Firefox/Safari (no directory picker) record to OPFS and download each file via <a download> with the correct name. GOPRO CHUNKING: a long take rolls to a NEW file every ~10 min with a 5-SECOND AUDIO OVERLAP (the last 5 s of chunk N is duplicated as the start of chunk N+1), named FILENAME-CHUNK#-DATETIME.mp4 (RECORDING-001-…, RECORDING-002-…) — unique + Finder-sortable; a take under ~10 min is a single RECORDING-001-<datetime>.mp4. CONSTANT FRAME RATE: video frames are encoded on an even index/fps PTS grid (not jittery wall-clock time), which fixes the macOS Preview/QuickTime "slow-motion" artifact that an irregular variable-rate PTS stream produced. Inputs: in (video, polymorphic), audio_l + audio_r (audio — a NEW cross-domain audio→video audio-input bridge connects each upstream audio source straight into a MediaStreamAudioDestinationNode this module owns; the audio is TAP-ONLY and is NOT monitored through your speakers). Output: out (video pass-through of in). CRASH RECOVERY: because the file is fragmented and each fragment is flushed to disk, a take is playable from whatever reached disk even if the tab dies before you press STOP — on mount the card scans an IndexedDB manifest for any in-flight recording and offers "Recover unsaved recording?" with Save / Discard. If the destination FOLDER was persisted, Save re-requests write permission and streams the partial straight back into it under the chunk's FILENAME-CHUNK#-DATETIME name (no re-picking); if the handle is gone or permission is denied it falls back to a picker/download suggesting the right filename. Recovery is THIS-MACHINE/BROWSER ONLY (OPFS is origin-local and does NOT sync to collaborators). Defaults: 30 fps, ~14 Mbps; locked but overridable later. Graceful degrade: where the runtime has no OS H.264 encoder (headless CI, some platforms) the RECORD button is disabled with a clear "no H.264 encoder available" badge — it never crashes.
the faceplate
inputs
| id | cable | what it does |
|---|---|---|
in | video | The picture to record and monitor — a polymorphic video input (video / mono-video / image are upcast, like OUTPUT.in). It is drawn into the card's preview, into the hidden full-resolution capture canvas the encoder reads, and passed through unchanged to the `out` jack. With nothing patched the card shows a slow dark-crimson idle sweep. RGB video stream |
audio_l | audio | Left channel of the soundtrack to record. An audio-typed input on a video module (the cross-domain audio→video bridge): the upstream audio source feeds a gain that is summed into channel 0 of a stereo merger, captured as the left side of the AAC track. Capture is tap-only/inaudible — recorded but not monitored to the speakers. audio signal |
audio_r | audio | Right channel of the soundtrack to record. Like audio_l but summed into channel 1 of the merger (the right side of the stereo AAC track the recorder encodes). Patch a stereo mixer/VCO across A·L and A·R for a stereo MP4; tap-only, so route the source to AUDIO OUT separately to hear it. audio signal |
outputs
| id | cable | what it does |
|---|---|---|
out | video | Pass-through of the `in` picture (input → framebuffer → out), so RECORDERBOX can be chained inline anywhere in a video patch without breaking the downstream signal — it monitors and records while the video keeps flowing. RGB video stream |
source
recorderbox.ts on GitHub.