Mesh Boolean
Mesh Boolean without a modeling app
- model
- model_b
- model
- image
Mesh Boolean is the node you reach for when a generated or imported mesh needs a hole cut through it, or two shapes need to become one. Drilling a window into a wall, subtracting one prop from another, fusing a base onto a figure - that's boolean geometry, and it's the kind of thing you'd normally open a full 3D app for. ComfyTV does it on the canvas, with the two meshes positioned live in the node body's viewport.
ComfyTV is the canvas-style app layer on ComfyUI where every operation is its own node. This sits in the 3D geometry workshop next to Mesh Ops, Mesh Primitive and Mesh Bake Maps - and it slots between them: Mesh Primitive makes the shapes, Mesh Boolean combines/carves them, Mesh Ops cleans the result, Mesh Bake Maps textures it.
How it works. The boolean is computed over a voxelized SDF (signed distance field) of both meshes, at a resolution you choose. Each mesh gets its own transform - position, rotation, scale - set with the gizmo in the node body (that's the transform_a / transform_b JSON). The operation runs on the SDFs: union keeps both volumes, difference removes model B's volume from model A, intersect keeps only the shared volume. The result is meshed back out and optionally smoothed with smooth_iters of Taubin smoothing. The preview viewport snapshot becomes the node's image output, so you can see what you carved.
The inputs that matter. The force_run_token, project_id, parent_output_id, transform_a and transform_b inputs are internal plumbing (gizmo-driven). The real set:
model/model_b- the two meshes. Both required; the node errors if either is missing.operation-union,difference(B's volume out of A), orintersect.resolution- 32–1024, default 256. Higher = sharper cut but way more faces; the tooltip's advice is worth taking: follow with a decimate for an exact count.smooth_iters- 0–20 Taubin smoothing passes on the result.
Outputs are model (the boolean result, feeding Mesh Ops/Bake or a render stage) and image (the viewport preview).
Install. Install the pack once:
cd ComfyUI/custom_nodes
git clone https://github.com/jtydhr88/ComfyTV
Restart ComfyUI (full restart, not a browser refresh) and the stage appears under ComfyTV → 3D. ComfyUI Manager - search "ComfyTV" - works too. Zero Python dependencies (the pack's pyproject.toml dependency list is empty) and no model files. Desktop/multi-install users: clone into the running instance's path from the startup log or the node won't register.
Where people get burned. The biggest one is expecting a sharp, clean cut from a low resolution. SDF booleans are voxel-based: at 256 you'll get visible faceting on the cut face - crank resolution up for a cleaner edge and accept the face explosion, then decimate. Second: the two meshes must actually overlap in the way the operation expects; difference with B entirely outside A just returns A, which reads as "nothing happened." And remember the gizmo positions matter - the booleans are computed on the transformed meshes, so re-positioning B in the viewport and forgetting to re-run means a stale result. One genuinely useful pattern: difference with a simple Mesh Primitive cylinder is the fastest way to drill a clean hole through any flat mesh.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| force_run_token | INT | 00–2147483647 | Internal — bumped on Run to invalidate ComfyUI's input cache. |
| project_id | STRING | Internal — populated by the projectStore on the frontend. | |
| parent_output_id | INT | 00–2147483647 | Internal — lineage parent set by spawn handlers on the frontend. |
| operation | COMBO | union | difference removes model B's volume from model A. |
| resolution | INT | 25632–1024 | SDF voxel grid resolution. Higher = sharper cut, more faces; follow with a decimate for an exact count. |
| smooth_iters | INT | 00–20 | Taubin smoothing iterations on the result. |
| transform_b | STRING | Internal — TRS of model B (position/quaternion/scale JSON) set by the gizmo in the node body. | |
| captured_image | STRING | Internal — /view? URL of the preview-viewport snapshot. Written by the 3D preview in the node body; becomes the `image` output. | |
| transform_a | STRING | Internal — TRS of model A (position/quaternion/scale JSON) set by the gizmo in the node body. | |
| modelopt | COMFYTV_MODEL | — | |
| model_bopt | COMFYTV_MODEL | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| model | COMFYTV_MODEL | — |
| image | COMFYTV_IMAGE | — |