MediaPipe detects everyday objects - cups, phones, bottles, people - and reports their bounding boxes each frame. This recipe extracts the centroid of each detected object and leaves a glowing comet trail behind it as it moves.
Operator Families in this Recipe
- COMPs: MediaPipe plugin for object detection.
- DATs / Script CHOP: Extracting centroid coordinates from detection results.
- TOPs: Particle trails as a texture feedback loop.
Before You Start
You need the free MediaPipe plugin (mediapipe.tox) from github.com/torinmb/mediapipe-touchdesigner.
This recipe uses a small Python Script CHOP to compute the centroid. The script is provided in full below.
Part 1: MediaPipe Object Detection
- Add a Video Device In TOP (webcam) → Null TOP named
CAM. - Drag
mediapipe.toxinto the network. - Connect
CAMto the first input ofmediapipe1. - Set Model →
ObjectDetectionand enable detection.
Click the Detections table output. With an object in frame, rows appear with columns: label, confidence, x_min, y_min, x_max, y_max. Row 0 (below header) is the highest-confidence detection.
Part 2: Extract Object Centroid
- Add a Script CHOP. Set its DAT to a new Text DAT and paste:
import td
def cook(scriptOp):
scriptOp.clear()
scriptOp.appendChan('cx')
scriptOp.appendChan('cy')
scriptOp.appendChan('detected')
det = op('mediapipe1').op('Detections')
if det is not None and det.numRows > 1:
try:
x_min = float(det[1, 'x_min'])
x_max = float(det[1, 'x_max'])
y_min = float(det[1, 'y_min'])
y_max = float(det[1, 'y_max'])
scriptOp['cx'][0] = (x_min + x_max) * 0.5
scriptOp['cy'][0] = (y_min + y_max) * 0.5
scriptOp['detected'][0] = 1.0
except:
scriptOp['cx'][0] = 0.5
scriptOp['cy'][0] = 0.5
scriptOp['detected'][0] = 0.0
else:
scriptOp['cx'][0] = 0.5
scriptOp['cy'][0] = 0.5
scriptOp['detected'][0] = 0.0- Connect to a Null CHOP named
OBJ_POS.
Finding the Detections path
op('mediapipe1').op('Detections')navigates inside the MediaPipe COMP. If your plugin version names this DAT differently, click insidemediapipe1and find the output DAT name, then update the path.
Part 3: Build the Trail Canvas
-
Add a Circle TOP at your webcam resolution (
1280 x 720).- Radius →
0.03, Fill Color1, 1, 1, Background0, 0, 0. - Right-click Center X → Expression →
op('OBJ_POS')['cx'] - 0.5. - Right-click Center Y → Expression →
-(op('OBJ_POS')['cy'] - 0.5). - Right-click Radius → Expression →
op('OBJ_POS')['detected'] * 0.03. Circle vanishes when nothing is detected.
- Radius →
-
Build the feedback trail:
- Feedback TOP (Target set at the end).
- Add TOP: Input 0 = Feedback, Input 1 = Circle TOP.
- Blur TOP (Filter Size:
3). - Level TOP (Brightness 1:
0.93). - Null TOP named
TRAIL_OUT. - Set Feedback TOP Target →
TRAIL_OUT.
Part 4: Colorize the Trails
- HSV Adjust TOP on
TRAIL_OUT→ Saturation Multiplier1.5, Hue OffsetabsTime.seconds * 20. - Bloom TOP (Threshold:
0.3, Intensity:0.9). - Null TOP named
TRAILS_COLOR.
Part 5: Composite Over Webcam
Add the trails directly onto the webcam with an Add TOP: Input 0 = TRAILS_COLOR, Input 1 = CAM. The trail layer is already black outside the glowing marks so no mask is needed.
Over vs. Add
Add TOPworks cleanly here becauseTRAILS_COLORis black outside the trails. Switch to anOver TOPif you want proper alpha blending and the trail to be semi-transparent.
Part 6: Detection Label Overlay
- Add a Script DAT:
import td
def cook(dat):
dat.clear()
det = op('mediapipe1').op('Detections')
if det is not None and det.numRows > 1:
dat.appendRow([str(det[1, 'label'])])
else:
dat.appendRow(['No detection'])- Connect to a Text TOP (Font Size
24, Color white) positioned at the bottom of frame. - Over TOP to layer the label onto the composite.
- Null TOP named
OUT.
Troubleshooting
- “Detections table is always empty.” - Hold a clear object (phone, bottle, cup) in good light. The COCO model covers 80 classes. Confirm ObjectDetection is selected and enabled in
mediapipe1. - “Trails sit in center at (0.5, 0.5).” - That’s the fallback when nothing is detected. Get an object in front of the camera.
- “NoneType error in Script CHOP.” - The path
op('mediapipe1').op('Detections')doesn’t match your plugin version. Click insidemediapipe1to find the correct DAT name. - “Trails too faint.” - Raise Level TOP Brightness 1 to
0.98or reduce Blur filter size to1.
Next Steps
- Multi-object tracking: Loop through all rows in the Detections table and paint a differently-colored circle per object using a
Script TOP. - Bounding box outline: Extract all four corners and drive a
Rectangle TOPfor a live bounding box overlay. - Object-specific effects: Check
det[1, 'label']in the Script CHOP and branch into different visuals per class - phone triggers a burst, cup triggers a ripple.
Parameter Tuning & Behavior
| Parameter | Behavior |
|---|---|
| Circle Radius | Larger = fat trail head; Smaller = pinpoint comet tip. |
| Level Brightness (decay) | Closer to 1.0 = long persistent trail; Lower (0.88) = rapid fade. |
| Blur Filter Size | Higher = soft cloud trail; Lower = crisp streak. |
| Hue Offset Speed | Higher = fast color cycle; Lower = slow ambient shift. |
Network Architecture
Video Device In ──▶ CAM ──▶ mediapipe1 (ObjectDetection)
│
Detections DAT
│
Script CHOP → OBJ_POS (cx, cy, detected)
│
Circle TOP (positioned at centroid)
│
▼
Add TOP ◀── Feedback TOP (target: TRAIL_OUT)
│
Blur TOP ──▶ Level TOP ──▶ TRAIL_OUT
│
HSV Adjust ──▶ Bloom ──▶ TRAILS_COLOR
│
Add TOP (+ CAM) ──▶ Text label ──▶ OUTSources:
- Object Tracking in TouchDesigner with MediaPipe - Derivative
- MediaPipe TouchDesigner Plugin - GitHub
(y) Return to Recipes & Projects | (y) Return to TouchDesigner | (y) Return to Home