Motion Controller ππ ‘π £π
Motion in Front of Your Camera Becomes a Number You Can Feed Into Anything
- image
- roi_chain
- FLOAT
- MASK
The signature demo for this pack is "wave your hand and the blur amount changes." Motion Controller is the node that makes that real. It watches a video frame for motion, and every time something moves it updates a FLOAT output you can plug into any numeric parameter - denoise, CFG, blur, strength, whatever. It's frame-differencing motion detection turned into a control voltage, and it's the closest thing in the pack to "the action was 'fire weapon'" made literal.
The mechanism is classic computer vision, the kind you'd write in an afternoon with OpenCV. Each execution it converts the frame to grayscale, blurs it with a Gaussian kernel sized by blur_size, and compares it to the previous frame. The difference gets thresholded (threshold, 0β1) and masked to the regions you care about. If enough pixels changed in a region, that's motion. Then - and this is the interesting part - how the value changes is up to the ROI you feed it.
Motion Controller doesn't just detect motion; it executes a per-region action defined by an upstream ROINode. Each region of interest carries an action: trigger (jump to max once per motion event), momentary (max while moving, min when still), toggle (flip between min and max), counter (count events), or a math op like add/subtract/multiply/divide/set. So you can have one region that toggles a light on and another that increments a counter - different zones, different behaviors, all from one controller.
The inputs that matter
- roi_chain - the
ROIfrom an ROINode. This defines where motion counts and what it does. Without it, there's nothing to act on. - image - the frame to analyze. Feed it your webcam stream.
- threshold - how much change counts as motion (0β1). 0.1 is sensitive; crank it up if your camera noise keeps tripping it.
- blur_size - Gaussian kernel for smoothing before differencing. Higher blurs ignore tiny high-frequency noise.
- minimum_value / maximum_value - the range the output lives in. This is your mapping, so think of it as "min motion value" to "max motion value."
Outputs: FLOAT (the current value) and MASK (where motion was detected, per frame - handy for visualization or downstream effects).
Install
Part of ryanontheinside/ComfyUI_RealtimeNodes. ComfyUI Manager, or:
cd ComfyUI/custom_nodes
git clone https://github.com/ryanontheinside/ComfyUI_RealtimeNodes
cd ComfyUI_RealtimeNodes
pip install -r requirements.txt
OpenCV is in the requirements, so the heavy lifting is covered.
The gotchas
This node is stateful - it keeps the previous frame and the ROI state between executions, so it only works when it runs every frame. That means a continuous/real-time loop, not a one-shot batch. The classic failure is feeding it a single still image and wondering why nothing happens: there's no prior frame, so there's no motion, by definition. Also remember the output clamps to min/max - a trigger reads max instantly, it doesn't animate there, so if you want easing, drive a control node's parameters from this instead. And if your threshold is too low, webcam noise will jitter the value constantly; that's the #1 tuning complaint. Get those two right, though, and you've got hands-free control over any parameter in the graph.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| always_execute | BOOLEAN | true | When enabled, the node updates every execution |
| image | IMAGE | β | |
| roi_chain | ROI | β | |
| threshold | FLOAT | 0.100β1 | Motion detection threshold |
| blur_size | INT | 51β21 | Size of Gaussian blur kernel |
| minimum_value | FLOAT | 0.00 | Minimum output value |
| maximum_value | FLOAT | 1.00 | Maximum output value |
| starting_value | FLOAT | 0.00 | Initial output value |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| FLOAT | FLOAT | β |
| MASK | MASK | β |