Cam Shot Toolkit: Camera Spec
Describe a pose-render camera in feet and degrees, not vibes
- camera
- camera_info_json
- interactive_state
If you've ever tried to fake a camera in a 3D scene by nudging sliders until it "looks right," this node is the antidote. Camera Spec is where the Cam Shot Toolkit's PoseRig group stops guessing and starts describing a camera the way a rig would: height above the floor, tilt below horizontal, distance from the subject, all in feet and degrees. And because the pose-reference scenes this pack generates are meant to line up with real capture hardware, those units are the point - you're not picking a nice-looking angle, you're reproducing a real one.
It sits at the start of the scene-assembly chain: Camera Spec makes a POSE_CAMERA, Assemble Scene uses it to check whether actors fit the frame, and Render Clay Grid uses it to actually draw the image. It's also the one PoseRig node that talks to the pack's browser viewer - more on that below.
How it works
The base mount is dead simple: the camera sits standoff_ft in front of the scene origin on +Z, height_ft above the floor, lateral_ft off to one side, and looks toward the action. Feet are converted to metres internally (0.3048), and everything - position, pose matrix, intrinsics - comes out of the node as a full camera dictionary, so what you describe is exactly what renders.
Two aiming styles cover most needs. normal is a fixed tilt_deg below horizontal - mount-and-pitch, like a security cam on a wall. lookat aims at a point target_height_ft above the origin, like a human operator tracking a subject. mixed rolls a random one per sample according to lookat_weight, so a single flow can train on both modalities without you rebuilding the graph.
The inputs that actually matter
hfov_deg- horizontal field of view. The defaults are calibrated to real cameras: 95 = OAK-D Pro W RGB, 127 = its IR mono sensor. If your pose data will eventually pair with that hardware, leave it.height_ft,tilt_deg,standoff_ft- the mount. 10 ft up, 40° down, 9 ft back are the sensible shipped defaults.mode/lookat_weight/target_height_ft- the aiming behavior above.- All the
jitter_*fields - the fun part. Each is a uniform ±range in feet or degrees; 0 = locked, so everything is deterministic by default.jitter_center_biasregrades draws toward the center (0 = uniform, 1 = triangular).jitter_x_steps_ftand friends let you snap to discrete cells first - e.g.-3,-1.5,0,1.5,3- so you can place the camera on an explicit ladder then wobble it. seed- bind it to your sampler seed. Same seed, same camera, every time.
Outputs
camera(POSE_CAMERA) - wire this into Assemble Scene and Render Clay Grid.camera_info_json- the full camera dict: intrinsics (fx/fy/cx/cy), pose, position in metres, and every jitter draw that actually happened. Invaluable if you're logging per-sample camera state to a dataset manifest.interactive_state- a small JSON (camera_pose+ distance) that the pack's Render Offset View browser viewer honors, so you can scout a camera in the Three.js preview and have the backend rasterizer reproduce it faithfully.
Install and gotchas
ComfyUI Manager (search sam3d-body-comfyUI-camshottoolkit) or:
cd ComfyUI/custom_nodes
git clone https://github.com/EnviralDesign/sam3d-body-comfyUI-camshottoolkit
cd sam3d-body-comfyUI-camshottoolkit
python -m pip install -r requirements.txt
Then restart ComfyUI. Requirements are heavy (torchvision, transformers, timm, pytorch-lightning…) because the pack also runs the SAM3D model - but note the amusing split: the Camera Spec math itself is pure NumPy, no GPU, no pyrender. It still insists the pack-wide requirements are installed, because ensure_runtime_dependencies checks them on every run. If you skip requirements.txt, you'll get a clear error listing exactly what's missing and the pip command to fix it.
Two things to watch. First, mixed mode and every jitter knob default to no-ops, so if you're reproducing an existing pipeline's camera policy, leave everything at zero and change one axis at a time. Second, don't expect Camera Spec to do anything visual on its own - it's a description node. Nothing renders until it's fed into the rest of the posing group.
Inputs (28)
| Name | Type | Default | Description |
|---|---|---|---|
| render_width | INT | 128064–4096 | — |
| render_height | INT | 72064–4096 | — |
| hfov_deg | FLOAT | 95.010–170 | Horizontal field of view. 95 = OAK-D Pro W RGB, 127 = IR mono. |
| height_ft | FLOAT | 10.00–60 | Camera height above the floor, feet. |
| tilt_deg | FLOAT | 40.0-89–89 | Pitch below horizontal (mode normal). |
| standoff_ft | FLOAT | 9.00–200 | Horizontal distance from the scene origin, feet (camera sits on +Z). |
| lateral_ft | FLOAT | 0.0-100–100 | Sideways offset of the mount, feet. |
| mode | COMBO | normal | normal: fixed tilt. lookat: aim at target_height above the origin. mixed: roll one of the two per sample (see lookat_weight). |
| lookat_weight | FLOAT | 0.700–1 | mode mixed only: probability this sample uses lookat (the rest are straight-ahead). |
| target_height_ft | FLOAT | 4.30–20 | lookat target height above the floor, feet. |
| offset_x_ft | FLOAT | 0.00-100–100 | Additive position offset, world X (feet). |
| offset_y_ft | FLOAT | 0.00-100–100 | Additive position offset, world Y (feet). |
| offset_z_ft | FLOAT | 0.00-100–100 | Additive position offset, world Z (feet). |
| offset_pitch_deg | FLOAT | 0.0-180–180 | Additive pitch after the base look-at (positive = further down). |
| offset_yaw_deg | FLOAT | 0.0-180–180 | Additive yaw after the base look-at. |
| offset_roll_deg | FLOAT | 0.0-180–180 | Additive roll after the base look-at. |
| jitter_x_ft | FLOAT | 0.000–20 | Uniform ±range on X (feet). 0 = locked. Stage-2 parity: 0.25. |
| jitter_y_ft | FLOAT | 0.000–20 | Uniform ±range on Y (feet). 0 = locked. |
| jitter_z_ft | FLOAT | 0.000–20 | Uniform ±range on Z (feet). 0 = locked. |
| jitter_yaw_deg | FLOAT | 0.00–90 | Uniform ±range on yaw. 0 = locked. |
| jitter_pitch_deg | FLOAT | 0.00–90 | Uniform ±range on pitch. 0 = locked. |
| jitter_roll_deg | FLOAT | 0.00–90 | Uniform ±range on roll. 0 = locked. |
| jitter_center_bias | FLOAT | 0.000–1 | Re-grade every jitter draw toward the centre: 0 = uniform, 1 = triangular (dense near 0, zero density at the range edge). |
| jitter_x_steps_ft | STRING | Discrete X cells added before the continuous jitter, e.g. '-3,-1.5,0,1.5,3' or weighted '0:0.5,-1:0.25,1:0.25'. Empty = continuous only. | |
| jitter_y_steps_ft | STRING | Discrete Y (mount height) cells, feet. Empty = continuous only. | |
| jitter_z_steps_ft | STRING | Discrete Z cells, feet; negative = forward off the wall, toward the subjects (e.g. '0,-1,-2'). Empty = continuous only. | |
| seed | INT | 00–18446744073709550000 | Jitter seed (bind to the sample seed). |
| jitter_keyopt | STRING | Optional string seed overriding `seed` (e.g. the legacy '<gesture>:<mode>:<ix>:<iy>' cell key). |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| camera | POSE_CAMERA | — |
| camera_info_json | STRING | — |
| interactive_state | STRING | — |