A standalone TouchDesigner Custom Operator TOP for DWPose whole-body pose detection. Outputs an OpenPose-format stick-figure suitable for ControlNet conditioning, runs entirely on the GPU via TensorRT 10.

Body skeleton, both hands (21 keypoints each), and 68 face
landmarks are detected per person, multi-person scenes work out of the
box, and the rendered stick-figure matches controlnet_aux's output
byte-for-byte so any SD ControlNet OpenPose model can consume it
directly.
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.2\).
Older CUDA 12.x will require small CMake edits (replace
cudart64_13 references with cudart64_12).C:\src\TensorRT-10.16.1.11).
Set %TENSORRT_ROOT% to that path before building.-G Ninja by default.From a regular PowerShell or cmd.exe window in the repo root:
:: 1. Set TensorRT path (adjust to your install location)
set TENSORRT_ROOT=C:\src\TensorRT-10.16.1.11
:: 2. Configure + build the plugin
build.cmd configure
build.cmd build
:: 3. Stage runtime DLLs into ./plugin/ (CUDA + TensorRT)
stage.cmd "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.2\bin\x64" "%TENSORRT_ROOT%"
After a successful build + stage, plugin/ contains:
td_dwpose_top.dll — the TouchDesigner plugin entry pointdwpose_worker.dll — the TensorRT/CUDA worker (loaded by the entry
point with restricted DLL search; see Architecture)nvinfer_*.dll, nvonnxparser_10.dll, cudart64_13.dll, … —
CUDA + TensorRT runtime libraries staged from your installbuild.cmd clean wipes build/ and reconfigures from scratch. Use
this when you change CMake options or update CUDA/TRT versions.
The DLLs you stage from %TENSORRT_ROOT%\bin\ must come from the same
TensorRT distribution that built the engines on disk. Mixing a
pip-installed tensorrt wheel with a standalone TRT install is the
single most common cause of Serialization assertion stdVersionRead == kSERIALIZATION_VERSION failed at engine load — even when both report
the same 10.x.y.z version string, the actual serialization bytes can
differ. If this happens, delete your *.engine files and let the
plugin rebuild them with the runtime that's actually loaded.
After building, point TouchDesigner at plugin/:
plugin/
folder, or copy plugin/* to your user plugins directory:
%USERPROFILE%\Documents\Derivative\Plugins\.Tab to open the OP Create dialog. Under
Custom → TOP you should see DWPose.If you don't see it, check the Textport for plugin load errors and see Troubleshooting.
Movie File In TOP,
webcam via Video Device In TOP, NDI feed, etc.).C:/Users/you/td-dwpose-engines. (Engine setup instructions are
pending verification — open an issue if you'd like to use this
before they land.)OP parameter to the DWPose TOP
to monitor status, performance, and per-frame keypoints. Wait for
status == 3 (ready).The DWPose TOP's output is a drop-in replacement for any
controlnet_aux.OpenposeDetector(...)-rendered conditioning image.
The body skeleton uses the canonical 18-point CMU OpenPose color
palette; hand and face landmarks follow the
controlnet_aux/dwpose/util.py convention. Any SD ControlNet
checkpoint trained on lllyasviel's annotator (lllyasviel/control_v11p_sd15_openpose,
thibaud/controlnet-sd21-openposev2-diffusers, and similar) can
consume the output directly.
| Parameter | Type | Default | Notes |
|---|---|---|---|
| Engines Folder | Folder path | (empty) | Required. Writable directory where the runner stores yolox.engine, dwpose.engine, and the cached *.onnx source models. Set this to a stable per-machine location; engines are GPU + TRT version specific so don't share across machines. |
| Reload | Pulse | — | Re-runs engine discovery + load. Pulse this after editing files in the engines folder, swapping models, or after a TRT version change. |
| Ordered Draw | Toggle | OFF | When ON, body limbs draw in controlnet_aux's outer-by-limb order so cross-person arm overlaps are depth-consistent. When OFF (default), all limbs dispatch in a single CUDA pass — slightly faster (~1–2 ms savings on 4K with multiple bodies), at the cost of non-deterministic overlap z-order between people. The default favors throughput; flip ON only if you have multi-person scenes where arm-over-arm depth matters visually. |
| Channel | Meaning |
|---|---|
status | Runner state: 0 idle · 1 downloading ONNX · 2 building engine · 3 ready · 4 error |
progress | 0.0–1.0 within the current status phase |
num_persons | Detected persons in the current frame |
infer_ms | YOLOX + DWPose inference time, milliseconds |
ordered | Live mirror of the Ordered Draw toggle |
lastrender_ms | Stick-figure rasterizer time, milliseconds (includes a cudaStreamSynchronize so it's end-to-end render cost) |
kp00x … kp17x | OpenPose body keypoint X coords for person 0 (image-pixel space) |
kp00y … kp17y | OpenPose body keypoint Y coords for person 0 |
Body keypoint indices follow the OpenPose 18-point convention: 0 nose,
1 neck, 2-4 right arm, 5-7 left arm, 8-10 right leg, 11-13
left leg, 14-15 eyes, 16-17 ears. Channels report 0.0 for any
keypoint below the renderer's confidence gate (so what the CHOP
reports matches what the TOP draws).
The stick-figure render also includes hand (21 kp × 2) and face (68 landmarks) keypoints internally, but those are not currently surfaced on the Info CHOP — only the body 18 are. Open an issue if you need them exposed.
td-dwpose is built as two DLLs:
td_dwpose_top.dll (thin TouchDesigner plugin shim)
↓ loads via LoadLibraryExW with restricted search
dwpose_worker.dll (owns all TensorRT, CUDA, ONNX-parser linkage)
The TOP shim has zero direct imports of nvinfer_10.dll,
nvonnxparser_10.dll, or any TRT symbol. It calls LoadLibraryExW
with LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32
to load the worker, which restricts Windows' DLL dependency resolver
to the plugin/ folder + System32 — explicitly excluding
TouchDesigner's bin/ folder.
This is necessary because TouchDesigner ships its own
nvinfer_10.dll, and Windows' default DLL search would pick TD's
copy over the plugin's, which is often a different TensorRT version. The
restricted load forces our staged TRT runtime to win the search
deterministically.
A C ABI (runner/dwpose_worker_c.h) is the boundary between the
two DLLs. The TOP-side DWPoseRunner class is a thin facade over
this ABI, which is delay-loaded (/DELAYLOAD:dwpose_worker.dll) so
no TRT symbols resolve until after the manual LoadLibraryExW has
pinned the worker.
The worker contains:
Measured on RTX 4090 Laptop, Windows 11, single input with 4+ people, inference time is sub-millisecond. Mileage may vary.

MIT — see LICENSE.
The vendored TouchDesigner Custom Operator SDK headers under
third_party/derivative/ remain Derivative Inc.'s property and are
distributed under their Shared Use License.
yzd-v/DWPose — the ONNX
model release used by this plugincontrolnet_aux
by HuggingFace — the reference Python renderer this plugin's
CUDA rasterizer matches byte-for-byte2 commits
C++
72.4%
Cuda
13.7%
CMake
5.5%
Batchfile
4.4%
C
3.9%
A standalone TouchDesigner Custom Operator TOP for DWPose whole-body pose detection. Outputs an OpenPose-format stick-figure suitable for ControlNet conditioning, runs entirely on the GPU via TensorRT 10.

Body skeleton, both hands (21 keypoints each), and 68 face
landmarks are detected per person, multi-person scenes work out of the
box, and the rendered stick-figure matches controlnet_aux's output
byte-for-byte so any SD ControlNet OpenPose model can consume it
directly.
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.2\).
Older CUDA 12.x will require small CMake edits (replace
cudart64_13 references with cudart64_12).C:\src\TensorRT-10.16.1.11).
Set %TENSORRT_ROOT% to that path before building.-G Ninja by default.From a regular PowerShell or cmd.exe window in the repo root:
:: 1. Set TensorRT path (adjust to your install location)
set TENSORRT_ROOT=C:\src\TensorRT-10.16.1.11
:: 2. Configure + build the plugin
build.cmd configure
build.cmd build
:: 3. Stage runtime DLLs into ./plugin/ (CUDA + TensorRT)
stage.cmd "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v13.2\bin\x64" "%TENSORRT_ROOT%"
After a successful build + stage, plugin/ contains:
td_dwpose_top.dll — the TouchDesigner plugin entry pointdwpose_worker.dll — the TensorRT/CUDA worker (loaded by the entry
point with restricted DLL search; see Architecture)nvinfer_*.dll, nvonnxparser_10.dll, cudart64_13.dll, … —
CUDA + TensorRT runtime libraries staged from your installbuild.cmd clean wipes build/ and reconfigures from scratch. Use
this when you change CMake options or update CUDA/TRT versions.
The DLLs you stage from %TENSORRT_ROOT%\bin\ must come from the same
TensorRT distribution that built the engines on disk. Mixing a
pip-installed tensorrt wheel with a standalone TRT install is the
single most common cause of Serialization assertion stdVersionRead == kSERIALIZATION_VERSION failed at engine load — even when both report
the same 10.x.y.z version string, the actual serialization bytes can
differ. If this happens, delete your *.engine files and let the
plugin rebuild them with the runtime that's actually loaded.
After building, point TouchDesigner at plugin/:
plugin/
folder, or copy plugin/* to your user plugins directory:
%USERPROFILE%\Documents\Derivative\Plugins\.Tab to open the OP Create dialog. Under
Custom → TOP you should see DWPose.If you don't see it, check the Textport for plugin load errors and see Troubleshooting.
Movie File In TOP,
webcam via Video Device In TOP, NDI feed, etc.).C:/Users/you/td-dwpose-engines. (Engine setup instructions are
pending verification — open an issue if you'd like to use this
before they land.)OP parameter to the DWPose TOP
to monitor status, performance, and per-frame keypoints. Wait for
status == 3 (ready).The DWPose TOP's output is a drop-in replacement for any
controlnet_aux.OpenposeDetector(...)-rendered conditioning image.
The body skeleton uses the canonical 18-point CMU OpenPose color
palette; hand and face landmarks follow the
controlnet_aux/dwpose/util.py convention. Any SD ControlNet
checkpoint trained on lllyasviel's annotator (lllyasviel/control_v11p_sd15_openpose,
thibaud/controlnet-sd21-openposev2-diffusers, and similar) can
consume the output directly.
| Parameter | Type | Default | Notes |
|---|---|---|---|
| Engines Folder | Folder path | (empty) | Required. Writable directory where the runner stores yolox.engine, dwpose.engine, and the cached *.onnx source models. Set this to a stable per-machine location; engines are GPU + TRT version specific so don't share across machines. |
| Reload | Pulse | — | Re-runs engine discovery + load. Pulse this after editing files in the engines folder, swapping models, or after a TRT version change. |
| Ordered Draw | Toggle | OFF | When ON, body limbs draw in controlnet_aux's outer-by-limb order so cross-person arm overlaps are depth-consistent. When OFF (default), all limbs dispatch in a single CUDA pass — slightly faster (~1–2 ms savings on 4K with multiple bodies), at the cost of non-deterministic overlap z-order between people. The default favors throughput; flip ON only if you have multi-person scenes where arm-over-arm depth matters visually. |
| Channel | Meaning |
|---|---|
status | Runner state: 0 idle · 1 downloading ONNX · 2 building engine · 3 ready · 4 error |
progress | 0.0–1.0 within the current status phase |
num_persons | Detected persons in the current frame |
infer_ms | YOLOX + DWPose inference time, milliseconds |
ordered | Live mirror of the Ordered Draw toggle |
lastrender_ms | Stick-figure rasterizer time, milliseconds (includes a cudaStreamSynchronize so it's end-to-end render cost) |
kp00x … kp17x | OpenPose body keypoint X coords for person 0 (image-pixel space) |
kp00y … kp17y | OpenPose body keypoint Y coords for person 0 |
Body keypoint indices follow the OpenPose 18-point convention: 0 nose,
1 neck, 2-4 right arm, 5-7 left arm, 8-10 right leg, 11-13
left leg, 14-15 eyes, 16-17 ears. Channels report 0.0 for any
keypoint below the renderer's confidence gate (so what the CHOP
reports matches what the TOP draws).
The stick-figure render also includes hand (21 kp × 2) and face (68 landmarks) keypoints internally, but those are not currently surfaced on the Info CHOP — only the body 18 are. Open an issue if you need them exposed.
td-dwpose is built as two DLLs:
td_dwpose_top.dll (thin TouchDesigner plugin shim)
↓ loads via LoadLibraryExW with restricted search
dwpose_worker.dll (owns all TensorRT, CUDA, ONNX-parser linkage)
The TOP shim has zero direct imports of nvinfer_10.dll,
nvonnxparser_10.dll, or any TRT symbol. It calls LoadLibraryExW
with LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32
to load the worker, which restricts Windows' DLL dependency resolver
to the plugin/ folder + System32 — explicitly excluding
TouchDesigner's bin/ folder.
This is necessary because TouchDesigner ships its own
nvinfer_10.dll, and Windows' default DLL search would pick TD's
copy over the plugin's, which is often a different TensorRT version. The
restricted load forces our staged TRT runtime to win the search
deterministically.
A C ABI (runner/dwpose_worker_c.h) is the boundary between the
two DLLs. The TOP-side DWPoseRunner class is a thin facade over
this ABI, which is delay-loaded (/DELAYLOAD:dwpose_worker.dll) so
no TRT symbols resolve until after the manual LoadLibraryExW has
pinned the worker.
The worker contains:
Measured on RTX 4090 Laptop, Windows 11, single input with 4+ people, inference time is sub-millisecond. Mileage may vary.

MIT — see LICENSE.
The vendored TouchDesigner Custom Operator SDK headers under
third_party/derivative/ remain Derivative Inc.'s property and are
distributed under their Shared Use License.
yzd-v/DWPose — the ONNX
model release used by this plugincontrolnet_aux
by HuggingFace — the reference Python renderer this plugin's
CUDA rasterizer matches byte-for-byte2 commits
C++
72.4%
Cuda
13.7%
CMake
5.5%
Batchfile
4.4%
C
3.9%