Three View
A real 3D scene inside a ComfyUI node — that hands you ready-made ControlNet maps
- image
- lines
- depth
- normal
The Three View node from lo-th's Comfyui_three_js pack puts an actual three.js renderer inside a ComfyUI node, and it's the only one that does what you'd actually want: it renders a 3D model from the exact camera angle you pick, then hands you four image tensors - the color render, an edge map, a depth map, and a normal map - ready to feed straight into a ControlNet.
Why that's useful: normally you get a depth or normal map by running a preprocessor (Depth Anything, MiDaS, Normal BAE) on a 2D image and hoping it guesses the geometry right. Three View skips the guessing. You load a GLB, orbit the camera to the framing you want, and the depth and normals come straight from the actual mesh. That's pixel-accurate geometry with zero estimation error, and it's yours before you've generated a single diffusion image. If you've ever wanted to compose a scene, render it, then regenerate it "in style" while keeping the structure - this is the missing link.
How it works - the browser does the rendering
Here's the part that surprises people: nothing here is Python-side rendering. The node works because your browser renders the scene. When the node is created, the frontend swaps the plain text widget for a live WebGL canvas with orbit controls, four renderers running four passes - color, lines (a toon/outline shader), depth (MeshDepthMaterial), and normal (MeshNormalMaterial). When you hit Queue, the JS serializes each canvas to a PNG, uploads it to ComfyUI via the /upload/image endpoint, pings the server when done, and only then does the Python side read the four files back and convert them to IMAGE tensors. So it's a round trip: browser → server → tensor. The mechanism matters, because it explains the single biggest constraint - see troubleshooting below.
The inputs and outputs that matter
The only required input, imageThreejs, is a comma-separated list of PNG filenames - and you will never type it. The frontend rewrites it automatically with per-node, timestamped filenames and then hides the widget. Treat it as plumbing and ignore it.
The four outputs are where it pays off, and they wire into exactly what you think:
- image - the color render, camera-lit and tone-mapped (ACES). Feed it to VAE Encode and prompt around it, or use it as a ControlNet reference.
- depth - a real depth pass from the mesh. Perfectly aligned by definition, so it makes a crisp depth ControlNet condition.
- normal - surface orientation from
MeshNormalMaterial. Great for lighting/material-aware generation, and pairs with IC-Light-style relighting if that's your thing. - lines - a toon/outline edge map. This is effectively a free Canny/sketch ControlNet condition with zero preprocessing.
In practice you'll usually wire image into the sampler (via VAE Encode) and depth or normal into a ControlNet, using the camera to nail the composition before generation.
Installing it
Both standard routes work. Easiest is ComfyUI Manager → search Comfyui_three_js → Install. Or, from your ComfyUI/custom_nodes directory:
git clone https://github.com/lo-th/Comfyui_three_js
Then restart ComfyUI. There's no model download step and no Python dependencies - pyproject.toml lists an empty dependency array, and all the three.js library code ships bundled in the node's js/ folder. That's genuinely rare in this ecosystem and worth appreciating.
Where people get burned
- It needs a real browser session. Since the browser renders the images, running this node headless or via the API-only mode produces nothing - there's no canvas to render or upload. If you're on a server you access through a browser (ComfyUI runs locally or on a box you can open in a tab), you're fine.
- GLB only. The model scanner in
three_view.pyfilters to.glbfiles, period. Drop your model inComfyUI/input/ThreeViewModels/(picked up as "loaded") or use the bundled defaults injs/assets, and load via the node's panel. Anything else is silently ignored. - The save can time out. The Python side waits up to about 10 seconds for the browser to upload all four renders. On a slow machine with a heavy model, the queue can stall and log a timeout in the console - keep the browser tab on the ComfyUI page while it runs.
- It's a browser widget, not a "real" node - collapses and graph reloads can reset camera state, so grab your camera position before you close the workflow.
Three View is niche - it's for people who actually have a 3D asset and want to drive diffusion from it. But for that job, nothing else in the graph does it this cleanly.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| imageThreejs | STRING | theejs_image.png,theejs_image_lines.png,theejs_image_depth.png,theejs_image_normal.png | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| lines | IMAGE | — |
| depth | IMAGE | — |
| normal | IMAGE | — |