Records every source you tick to its own video file, all frame-aligned, in a single recording pass. The video equivalent of OBS's multi-track audio.
One recording produces Face Cam_20260810-194846.mkv and
Screen Share_20260810-194846.mkv alongside OBS's own recording — same length,
same start frame, ready to drop straight onto separate tracks in an editor. Your webcam is captured full-size and uncropped, no matter how it is
masked or scaled in the scene.
A quick walkthrough of installing the script and making your first recording. Go at your own pace — click Next when you are ready.
That is the whole workflow. Everything below is detail, for when you want it.
Each source gets its own private canvas and its own encoder. All the encoders join a single encoder group, which is what keeps the files in lockstep — measured at 0 frames apart.
INSTALL.bat. It checks what you have, asks before installing
anything, copies the script into OBS's scripts folder and points OBS at a compatible Python.
Use the .bat, not the .ps1 — Windows often has no
"Run with PowerShell" option at all, and the default RemoteSigned policy blocks scripts that
arrived in a zip. The .bat clears both.
+ → pick multivideo_recording.py.ℹ Log status first. It reports your OBS version, which audio tracks OBS
records and streams on, and which are free.| OBS 31.1 or newer obsproject.com/download | Hard floor — the script uses the canvas API, which does not exist before 31.1. Verified on 32.2.1. |
|---|---|
| Python 3.8 – 3.10 python.org → 3.10.11 | Windows OBS is built against 3.8...<3.11. 3.11+ will silently fail to load. Check under Python Settings: it should say Loaded Python Version: 3.10. |
| ffmpeg gyan.dev/ffmpeg/builds | Optional, and rarely needed — only as a fallback for "No audio" layers when every mixer track is occupied. The installer fetches all three for you — these links are only for doing it by hand. |
OBS: Advanced output, mkv → .mkv, encoder obs_nvenc_hevc_tex
Turns everything off without losing your source selection. Useful when you want a plain OBS recording for once.
Named sets of everything on this panel — folder, format, quality, triggers, and every ticked source with its audio choice. Switch between a podcast setup and a gaming setup from one dropdown.
Presets live inside the current scene collection and are tied to the script's file path, so moving or re-adding the script loses them. Export to a file before you move anything, or to hand a setup to someone else.
Leave it blank and it uses OBS's own recording folder. The log prints
WRITING TO: at the start and SAVED TO: at the end, so there is never any doubt.
Use OBS's own is the default: format, encoder and quality all come from your OBS recording settings, so the layers look exactly like OBS's file. The grey line underneath shows what was detected.
Switch to Custom and you get File format, Video encoder, Quality and Encoder speed of your own.
Records the finished mix as its own file. Unlike OBS's recording it sits inside the encoder group, so it is frame-exact with your layers — drop everything on a timeline and it lines up with no nudging.
Recording through OBS gives you OBS's file as well, so tick this only if you want the aligned copy.
Leave both trigger boxes ticked. Then OBS's normal Start Recording and Start Streaming buttons drive everything and you never open this panel again. If you stream without recording and the streaming box is unticked, nothing arms.
Each layer is a full encoder at its source's native size. A 4K capture costs four times what a 1080p webcam costs, and OBS's own recording runs on top of all of it. This is the one setting that can tip a machine over.
| Quality-based (CQP/CRF) | Targets a quality level, so each layer gets the bitrate its resolution needs. The right choice when layers are different sizes. Lower number = better = bigger. |
|---|---|
| Match OBS's settings | Reads your recording encoder config and applies it verbatim. |
| Fixed bitrate | The same kbps regardless of resolution — which starves a 4K layer while wasting bits on a webcam. |
| Encoder speed | Fastest → Best. Defaults to Balanced. This is the first knob to turn if OBS says Encoding overloaded. |
OBS telling you the encoder cannot keep up. The script prints its own estimate at the start of
every recording — Encoder load: 3 layer(s), about 8.0x 1080p in total — and
warns when that gets heavy.
In order: lower Encoder speed, untick a layer (the 4K one costs the most), or raise the quality number. Switching to Use OBS's own settings also helps, since those are tuned for one encoder rather than several.
Will record 2 layer(s): Screen Share ('Desktop Audio' isolated on track 5) | Face Cam ('Microphone' isolated on track 6)
The Show sources filter keeps the list short — default is the current scene only. Anything you have ticked stays visible whatever the filter says.
| Source Clones | Resolve to the original. Face Cam Clone → Face Cam, recorded once. |
|---|---|
| Single-item groups | Collapse to the source inside. Face Cam Group → Face Cam. These are almost always positioning wrappers. |
| Multi-item groups | Stay whole — one file for the whole composite. A Graphics group of five images becomes one graphics layer. |
| A source in 10 scenes | Listed once, produces one file, recorded continuously across scene changes. |
A source with its eye toggled off in the Sources list — or one sitting in a scene you are not currently showing — still records to its own layer file.
This is deliberate. Each layer renders on its own private canvas with the ACTIVATE flag, and
the script never reads the scene item's visibility. The eye icon controls what appears in your
program output, not what the layer captures.
Why it is useful: park a second camera off-screen and it is captured clean for the edit without ever appearing on stream. Hide the graphics group mid-show and you still get an unbroken graphics track. Cut to a screen share while the webcam keeps rolling in the background.
The flip side: ticking a source you never show still costs a full encoder and writes a full-size file. Untick what you do not need.
OBS encodes audio per mixer track, never per source. Two files can only carry different audio if their sources sit on different tracks. Everything below follows from that.
By default OBS puts every new source on all six tracks, which is why picking "track 2" and "track 3" by hand gives you the same mix twice. The script handles this for you: at record start it borrows a mixer track that OBS itself does not record or stream on, puts your chosen source alone on it, and restores your routing when recording stops.
| No audio | Gives the file a silent audio track. libobs refuses to start a video-only output at all, so the encoder is pointed at an empty mixer track instead. If every track is occupied it falls back to recording the full mix and stripping it with ffmpeg afterwards. |
|---|---|
| Everything (full mix, track 1) | The complete mix, exactly what OBS's own recording gets. |
| A named source | Just that source, isolated. The label tells you the truth up front — clean, track 5 or shares track 3 with Media, Browser. |
OBS's mute is a source-level gain of zero applied to all six tracks. A muted source is silent everywhere, including in its own layer file. There is no way around it.
Use "keep this audio OUT of the live mix" instead. It routes the source away from every track OBS records and streams, while its layer file still captures it clean. Stream and main recording lose it; your edit keeps it.
For live control mid-stream, bind Settings → Hotkeys → "MultiVideo: toggle live mix for layer-only audio".
Two things look odd but are correct: the source stays unmuted in the Audio Mixer with meters moving (it is routed away, not silenced), and if you monitor it you will still hear it locally. Neither reaches your audience.
| ⟳ Rescan sources | Pick up scenes or sources added since the panel was opened. |
|---|---|
| ℹ Log status | Start here when something looks wrong. Prints the script version and the file it was loaded from, the output folder, what is ticked, what each audio choice resolves to, which tracks OBS uses, which are free, and any muted sources. |
| 💾 Save diagnostic log | Writes a full report to a .txt — settings, presets, every timestamped OBS event, per-file results and the whole script log. Send this when asking for help. It also writes itself to multivideo-last-session.txt in your output folder after every recording, so a log exists even if OBS closes. |
That is the intended way to use this, and the only way you should record anything real. With the trigger boxes ticked, OBS's Start Recording starts your layers too, its Stop Recording stops them, and its Pause is mirrored so every file ends up the same length.
You also get OBS's own record timer and red indicator in the main window — the reliable at-a-glance confirmation that everything is rolling.
The script has no start button of its own, deliberately. There is nothing to press twice, and nothing to leave running by accident.
Use OBS's own record timer and red indicator in the main window — that is your confirmation that everything is rolling, layers included. The script deliberately has no start button of its own, so there is no second thing to remember to press or to leave running by accident.
For detail while it runs, the Script Log prints a heartbeat every five seconds:
Progress in the Script Log:
MULTIVIDEO START (recording) - 3 layer(s), obs_nvenc_h264_tex, 10000 kbps each
WRITING TO: B:/OBS Recordings/multi video layer
Isolating audio sources onto their own tracks:
'Desktop Audio' -> borrowed track 5 (OBS uses [1, 2, 3, 4]); restored on stop
● REC 00:35 3 layers 123 MB
|| paused
> RESUMED
SAVED TO: B:/OBS Recordings/multi video layer
Files are named <Source name>_<date>-<time>.mkv (or .mp4, depending on the format you picked), so every file from one take shares a timestamp.
python check_sync.py "B:/OBS Recordings/multi video layer"
Compares frame counts and start times across the newest take. You want PASS — all streams within 2 frames.
Only multivideo_recording.py changes between versions. Replace that one file, then
in OBS press the ↻ refresh button next to +.
Settings and presets are stored per file path. Removing the script and adding it again — especially from a different folder — starts you from scratch, with nothing ticked and no presets.
The panel shows version and loaded from: at the top. After refreshing, check the version changed. If it did not, you replaced a different copy than the one OBS is running, and the path on that line tells you which one to overwrite.
Watch out for Windows saving repeat downloads as multivideo_recording (1).py,
(2).py and so on. Keep exactly one copy, in
%APPDATA%\obs-studio\scripts.
| No status inside the script panel | OBS gives scripts no way to refresh their own properties panel, so a live clock there is impossible. OBS's own recording timer and the Script Log heartbeat cover it. |
|---|---|
| Encoder count | Each layer is a full encoder. NVENC has a concurrent session limit; the log says so plainly if you hit it. |
| Disk throughput | Bitrate × layers, all at once. NVMe recommended. |
| Crash mid-recording | Audio routing is not restored. Normal stop, script unload and OBS exit all restore correctly — after a crash, check Advanced Audio Properties. |
| vs OBS's own file | Your layers are aligned with each other, not with OBS's recording — OBS's encoder is not in the group. OBS’s own file may start a fraction of a second off; align on a clap or cut from the layers. |
| Long sessions | Not yet soak-tested for drift over many hours. Run check_sync.py after a 60+ minute recording before trusting it with something you cannot re-shoot. |
| Presets are per collection | They live in the scene collection and are keyed by the script's path. Export them to a file to move or share them. |
| Renaming a source | Layers are keyed by source name, so renaming one drops it from existing presets. Re-save the preset afterwards. |
Prototype OBS script — not a released plugin. Requires OBS 31.1+ and Python 3.8–3.10.