OmniCam Director
Stop dictating camera moves to a prompt, keyframe them in a 3D viewport
- image
- video
- audio
- scene_3d
- solved_scene
- motion_scene
- playblast_video
- audio
You've been here: you write "slow dolly-in, camera orbits right" into a video prompt and get back something that sort of moved. Video models are terrible at taking dictation about motion - and they all want to be told differently anyway. Wan's camera-control path wants real extrinsics, LTX wants 2D motion tracks, MiniMax wants a reference clip. The OmniCam Director is the authoring end of a pack that treats that mess as a translation problem: you compose the shot once in a real 3D viewport, and it hands the result downstream as a model-independent MotionScene.
Think of it as a mini shot-layout tool living inside ComfyUI, not a typical plumbing node. You get a live 3D viewport, keyframable cameras, drawable motion layers over the frame, camera cuts, and a playblast recorder. Most of what it does happens in the frontend while you work; the Python execute() only fires when you queue, compiling the whole editor state into the canonical scene that travels between OmniCam nodes.
The inputs you actually set
Load it and you mostly leave the graph inputs alone - the interface manages the scary ones (state_json, recording_path, card_asset) for you. The fields that matter when you first drop it in:
width,height,fps,duration_seconds- your authoring canvas: 1280×720 @ 24fps, five seconds by default. This is the grid you compose on, not a locked output size; the Monitor resamples to whatever the target model wants.render_mode- how the viewport shows your scene (graybox,grid,wireframe,point_field,card_grid,beauty, andomni_reffor compositing over reference footage). Cosmetic, but pickomni_refwhen you're matching a real clip.image/video(optional) - reference stills or a proxy clip to compose against. Both sockets accept VIDEO or IMAGE, so a generator's IMAGE batch plugs straight in.solved_scene(optional) - connect an OmniCam Extractor here to import a recovered camera move and keep editing it. Only the playblast camera comes across, and it imports each new solve once, then leaves your edits alone; unplug the cable to freeze what you've imported.
Outputs
motion_scene(OMNICAM_MOTION_SCENE) - the whole authored scene: cameras, objects, motion layers, cuts, timeline. This is the wire to the OmniCam Monitor.playblast_video(VIDEO) - the recorded proxy of the shot. Reference-video profiles downstream need this; it's a motion reference, not a render.audio(AUDIO) - associated audio, passed through.
How authoring works
Compose your opening frame, press I to insert a camera keyframe, move the playhead, reposition, key again. Space previews the shot; when a reference-video workflow needs it, record a playblast. Every camera keeps its own keyframed track, and the whole thing serializes into the MotionScene rather than a pile of prompts. If the playblast is stale relative to the scene, the Monitor will read PLAYBLAST OUTDATED - re-record before you ship it.
Install
It ships in ComfyUI-Majoor-OmniCam (same author as the Majoor Assets Manager and ImageOps packs). Requires ComfyUI 0.31+; there are no required Python packages and no build step - the web bundle is committed. Easiest is Manager → Custom Nodes Manager → search Majoor OmniCam → Install, then restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/MajoorWaldi/ComfyUI-Majoor-OmniCam.git
Restart ComfyUI and the nodes land under Majoor › OmniCam. All three are flagged experimental, so don't harden a production workflow around a socket that hasn't frozen yet - the Director's Motion Tracks surface is the least settled part. scene_3d is a best-effort UI bridge that guesses your 3D node's mesh path from common widget names; if your 3D node isn't detected, it's the naming, not you.
Inputs (13)
| Name | Type | Default | Description |
|---|---|---|---|
| state_json | STRING | {} | — |
| recording_path | STRING | — | |
| card_asset | STRING | — | |
| width | INT | 128064–4096 | — |
| height | INT | 72064–4096 | — |
| fps | INT | 241–120 | — |
| duration_seconds | FLOAT | 5.000.25–120 | — |
| render_mode | COMBO | 7 options: omni_ref, graybox, grid, point_field, wireframe, card_grid, +1 | |
| imageopt | VIDEO,IMAGE | Reference stills, or a clip sampled for them. | |
| videoopt | VIDEO,IMAGE | A proxy clip, or an IMAGE batch read at the node fps. | |
| audioopt | AUDIO | — | |
| scene_3dopt | * | — | |
| solved_sceneopt | OMNICAM_MOTION_SCENE | A solved scene from OmniCam Extractor. Only its playblast camera is imported: motion layers, objects and cuts on the upstream scene are not merged. The Director imports each new extractor fingerprint once and then leaves your edits alone; disconnect the cable to freeze what you imported. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| motion_scene | OMNICAM_MOTION_SCENE | — |
| playblast_video | VIDEO | — |
| audio | AUDIO | — |