obs_scene_item_transform
Read or modify the full transform of an OBS scene item—position, scale, rotation, alignment, crop, and bounds—by specifying scene and item IDs. Only provided fields change, enabling targeted adjustments.
Instructions
The complete transform: position, scale, rotation, alignment, crop and bounds. obs_set_source_transform covers position, scale and crop only; this one reaches the rest. Call with just sceneName and sceneItemId to READ the current transform, including the read-only width/height/sourceWidth/sourceHeight that tell you how big the thing actually is on canvas right now.
Only the fields you pass change; everything else keeps its current value.
BOUNDS ARE INERT WITHOUT boundsType. boundsWidth, boundsHeight and boundsAlignment are ignored while boundsType is OBS_BOUNDS_NONE, which is the default, so setting a bounding box and nothing else accomplishes nothing at all and reports success. Set boundsType in the same call. Bounds are the right tool for a slot of fixed size - OBS_BOUNDS_SCALE_INNER fits the source inside the box keeping its aspect ratio, which is how you drop a camera of unknown resolution into a layout without doing the arithmetic. Once bounds are active they drive the size and scaleX/scaleY stop being the thing to adjust.
ALIGNMENT IS A BITMASK, NOT AN ENUM: 0 centre, 1 left, 2 right, 4 top, 8 bottom, added together. 5 is top-left and is what every item on the reference machine uses. It sets which point of the source positionX/positionY actually refers to, so changing alignment alone appears to teleport the source even though position never changed.
Crop is measured in SOURCE pixels, before scaling, and cuts in from each edge. width/height/sourceWidth/sourceHeight cannot be written - to resize, set scale, or use bounds.
A source moved off the canvas is still ENABLED and still renders and, more to the point, its audio still plays. Parking something at x=2600 on a 1920 canvas does not silence it. That is what doubled both voices on day one, from a phone parked out of frame but unmuted; it is also used deliberately here, because the mic and music sources are off-canvas precisely so they stay audible without taking up pixels. Hiding a source with obs_set_source_visible DOES cut its audio, which is why those are parked rather than hidden. Choose the one you mean.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| scaleX | No | Horizontal scale. 1 = native size. Negative flips. | |
| scaleY | No | Vertical scale. 1 = native size. Negative flips. | |
| cropTop | No | Source pixels cut from the top. | |
| cropLeft | No | Source pixels cut from the left, 0 to 100000. | |
| rotation | No | Degrees clockwise, -360 to 360. | |
| alignment | No | Bitmask: 0 centre, 1 left, 2 right, 4 top, 8 bottom. 5 = top-left. | |
| cropRight | No | Source pixels cut from the right. | |
| positionX | No | Canvas x of the alignment point. | |
| positionY | No | Canvas y of the alignment point. | |
| sceneName | Yes | Scene holding the item. | |
| boundsType | No | Required for any bounds field to take effect. | |
| cropBottom | No | Source pixels cut from the bottom. | |
| boundsWidth | No | Bounding box width, 1 to 90001. Needs boundsType. | |
| sceneItemId | Yes | Item id from obs_list_sources_in_scene. Unique within this scene only. | |
| boundsHeight | No | Bounding box height, 1 to 90001. Needs boundsType. | |
| cropToBounds | No | Clip anything overflowing the bounding box. Needs boundsType. | |
| boundsAlignment | No | Same bitmask as alignment; where the source sits inside the box. 0 = centred. |