Nodes/ComfyDL/Heatmaps to 3D
ComfyUI Node

Heatmaps to 3D

Extrude a tensor into a mesh you can actually orbit

By Cynthia-lxx·Created 2 months ago·Updated a day ago· 6
Heatmaps to 3D
  • matrices
  • model_3d
cmapReds
opacity0.60
thickness0.15
max_samples262144
dimsauto
axis_reducemean
auto_selectlast_n
auto_n0
on_errorfallback_first_n

This node takes a tensor - an attention matrix, an activation map, a per-class loss grid, whatever you've been building in the graph - and writes it out as a real .obj mesh you can spin in ComfyUI's viewport. It's not a 3D generator and has nothing to do with Hunyuan3D-style image-to-3D; it's data as geometry. If you're here to make pretty pictures you're in the wrong node.

Why it exists at all

ComfyUI's frontend binds its 3D canvas to the hard-coded node id Preview3D. A custom node can never render 3D itself - it can only build a file and hand it over. So that's the design: mesh in code, hand to Preview3D, orbit.

Worth knowing what pack you installed. ComfyDL wraps the d2l (Dive into Deep Learning) codebase as ComfyUI nodes - you train a small CNN or poke at attention by wiring instead of writing Python. Most of its 109 nodes are for learning, not for images.

How it works

The front end is the same one ComfyDL's Show Heatmaps nodes use, deliberately. Your tensor is strided down to max_samples elements (a view, so peak memory is bounded by the budget, not the input size), then one to three axes are kept - the ones you name in dims, or auto-picked - and every other axis is collapsed by axis_reduce. The result is always 1-D, 2-D or 3-D, and that decides the geometry:

  • 1-D → a flat, barcode-like colour ribbon with real thickness, captioned "1D".
  • 2-D → the same in two dimensions: a slightly thick coloured plate, captioned "2D".
  • 3-D → a translucent cube: six outer faces plus three orthogonal mid-planes so the interior stays readable.

That last one is a smart bit of cheating: a real voxel volume would be cap³ polygons, so you get the shell plus three slices instead - 9 × cap² quads - with the mid-planes double-sided, since a single-sided plane vanishes from half the orbit. The caps are hard: 256 cells for a ribbon, 64 per axis for a plate, 32 for a cube.

Colour is quantised into 64 OBJ materials, and alpha lives in the .mtl's d field - the only place a three.js viewer reads transparency from, since OBJ vertex colours have no alpha. The OBJ and MTL text is written by hand; no 3D library is involved. The fiddly part: Preview3D renames whatever it gets to preview3d_<uuid>.obj, so a plain file object would leave the .mtl behind and take all the colour with it. ComfyDL subclasses the 3D file type so the materials follow.

Inputs and output that matter

You only really set three things:

  • matrices (TENSOR) - the tensor to render. Note the type: an IMAGE or LATENT won't connect directly. Run it through ComfyDL's Value → Tensor node (CdlValueToTensor, in utilities/conversion), which accepts IMAGE/MASK/LATENT/AUDIO/SIGMAS and emits a plain tensor.
  • cmap - any matplotlib colormap name, default Reds. An unknown name doesn't error; it falls back to Reds and logs a note. Quietly typo-tolerant.
  • opacity (0.05–1.0, default 0.6) and thickness (default 0.15) - material alpha and slab thickness in cell units.

The rest you can ignore until something looks wrong: max_samples (element budget, 0 disables sampling), dims (auto, or indices like 0,1 / -2,-1), axis_reduce (mean/max/first/mid), auto_select + auto_n (which axes auto mode keeps), and on_error.

Output: model_3d (FILE_3D_OBJ) - the OBJ plus its sibling MTL. Wire it to Preview3D. That node has no outputs; the preview travels through its UI payload and the file lands in output/ as preview3d_<uuid>.obj.

Install

ComfyUI Manager → search ComfyDL → install → restart. Or manually:

cd ComfyUI/custom_nodes
git clone https://github.com/Cynthia-lxx/ComfyDL ./ComfyDL
pip install -r ./ComfyDL/requirements.txt

The requirements are light: matplotlib, IPython, matplotlib-inline. No model downloads, nothing for models/. You do need a reasonably current ComfyUI - this node imports comfy_api.latest.Types and the pack's conversion nodes use the V3 node API, so a stale build fails at import and you get no nodes at all. There's a GUI fork, ComfyDL_UI, if you'd rather skip custom_nodes.

Where it goes wrong

A flat, colourless plate in the viewer. You're probably looking at a moved or missing .mtl. That file pair is the colour - keep them together.

The cube looks like mush or a solid brick. That's opacity, not the mesh. 1.0 makes the interior opaque; below ~0.2 the nine layers blur together. 0.4–0.7 is the usable band.

dims errors that never appear. dims='0,3' on a 2-D tensor raises a real spec error, as do more than three axes or a duplicate - but on_error defaults to fallback_first_n, which swallows it and quietly renders the leading axes instead. Set on_error to error while you're chasing an axis problem, and read the log.

A slow run on a big tensor. The mesh caps protect the polygon count, not the read. Keep max_samples at its default (262144) unless you really want the whole thing sampled. And axis_reduce defaults to mean - averaging away a batch dimension is rarely what you wanted to see, so try first or max when the picture looks oddly smooth.

Categoryd2l/Visualization

Inputs (10)

NameTypeDefaultDescription
matricesTENSOR
cmapSTRINGReds
opacityFLOAT0.600.05–1
thicknessFLOAT0.150.01–1
max_samplesoptINT2621440–1073741824
dimsoptSTRINGauto
axis_reduceoptCOMBOmean4 options: mean, max, first, mid
auto_selectoptCOMBOlast_n4 options: first_n, last_n, most_informative_n, least_informative_n
auto_noptINT00–3
on_erroroptCOMBOfallback_first_n2 options: error, fallback_first_n

Outputs (1)

NameTypeDescription
model_3dFILE_3D_OBJ