Nodes/ComfyUI Schematic/Schematic Voronoi
ComfyUI Node

Schematic Voronoi

Your photo becomes the density field — the wireframe mesh IS the image

By jeremieLouvaert·Created 2 months ago·Updated 19 days ago· 0
Schematic Voronoi
  • image
  • texture
  • density_mask
  • image
  • overlay_only
  • overlay_alpha
density_sourcebrightness
cells12000
density_gamma2.5
seed42
mesh_weight1.0
cell_fill0.00
seed_dotsfalse
overlay_opacity1.00
image_opacity0.20
texture_opacity0.50
frame_text_size12
frame_text_tlDesign & Strategy
frame_text_trAKURATE
frame_text_bl
frame_text_br
palettewhiteOnDark
bg_color#0a0a0a
stroke_color#d8d8d8
size_presetmatch input
mask_min0.010

Of the three nodes in this pack, Schematic Voronoi is the one that actually re-renders the photo rather than decorating it. It reads the image as a density field, builds a Voronoi tessellation that follows that field, and then the mesh becomes the image - tiny bright cells where the photo is hot, large faint cells where it's cold. This is the "technical wireframe" look that people spend forever trying to coax out of a prompt; here it's a deterministic post-process, pure numpy, no model involved.

The key mental shift vs. the other two nodes: the photo is not the thing being annotated, it's the input to the math. The tooltip on image_opacity nails it - it defaults to a low 0.2, "because the mesh IS the image here (density follows the photo's own field), not an overlay drawn on top of it." Drop image_opacity to 0 and you get pure mesh; raise it toward 1 for a hybrid where the photo shows through the wireframe.

How it works

Three steps, all grounded in the pack's source. First, the density field: density_source picks what the mesh follows - brightness (pixel luma), darkness (inverted luma), or detail (local contrast, computed as the standard deviation of luma via a box filter, so it outlines structure on either ground). Then seed placement: a deterministic MINSTD rejection-sampling stream scatters cells sites, with density_gamma as the contrast curve that decides how sharply seeds concentrate into the hottest regions - higher gamma, more clustering. Finally the geometry: seeds are mirror-reflected across each canvas edge (5n points total) and fed to scipy.spatial.Voronoi, then the n real cells are extracted, which is the trick that keeps cells finite and clean at the canvas border instead of shooting off to infinity.

Ridge intensity is graded by cell size, so dense regions glow and sparse regions fade - that's the whole aesthetic, and it's the reason cells is a target site count, not a relative density. The README is explicit: the same photo at higher canvas resolution keeps the same mesh structure, just scaled up with the canvas.

The inputs that matter

  • image - the source. Everything else is math on this.
  • density_source - the single most important choice. The tooltips are practically a tutorial: pair brightness with the dark-ground palettes (whiteOnDark, goldOnDark, greenOnDark), and pair darkness with blackOnLight so tonal values render right-side-up. detail outlines structure on either ground.
  • cells - site count (default 12000, max 30000). This is the absolute density knob; more cells = finer mesh.
  • density_gamma - how hard the mesh follows the field. Higher = sharper concentration into hot regions.
  • seed - deterministic MINSTD placement; the same seed gives the same tessellation, every frame of a batch.
  • image_opacity - 0 for pure mesh, 0.2 default, higher for a hybrid look.
  • mesh_weight - multiplies both ridge stroke widths, so you can thicken the wireframe without re-rolling the seeds.
  • palette - same five options as Overlay, custom included.

Optional extras worth knowing: cell_fill puts a flat plate-tone under each cell's wireframe, brightness-scaled by that cell's own field value (0 disables), and seed_dots draws a dot at each accepted seed location.

Outputs and wiring

Identical three-output pattern as the rest of the pack: image (full composite), overlay_only (the mesh without the photo, so you can composite the wireframe onto anything), and overlay_alpha (a 0-to-1 mask of where the vector elements were drawn - handy for selective downstream effects). Same frame text, seeded grain texture, and size presets as Overlay, so the three nodes compose into one visual family.

Install and gotchas

Same install as the whole pack - ComfyUI Manager (search "ComfyUI Schematic") or:

cd ComfyUI/custom_nodes
git clone https://github.com/jeremieLouvaert/ComfyUI-Schematic

Restart, and it's under AKURATE/Schematic. The one real dependency note: Voronoi uses scipy, and scipy ships with ComfyUI itself - the README and the source both confirm there's nothing extra to install. (Worth knowing: scipy is imported lazily inside the cell builder, so the pack loads fine even if scipy ever went missing; every sibling node still runs.)

Two grounded gotchas. First, the tone-inversion trap: brightness on a blackOnLight palette renders tonal values upside-down - the tooltip exists because people hit this immediately. Switch to darkness if you're on the light ground. Second, cells is an absolute count, so don't crank it when you upscale the canvas - the mesh scales with the resolution, and at 12000+ sites plus ridge rendering, a big canvas is where the pure-numpy engine actually starts to take a few seconds. If it's slow, lower cells, not mesh_weight. For a brand-new pack (first commit August 2026), it's remarkably polished, and the determinism makes it a genuinely fun one to seed-scrub.

CategoryAKURATE/Schematic

Inputs (23)

NameTypeDefaultDescription
imageIMAGE
density_sourceCOMBObrightnessField the seed mesh follows. Brightness = pixel luma - pair with dark-ground palettes (whiteOnDark, goldOnDark, greenOnDark). Darkness = inverted luma - pair with blackOnLight instead, so tonal values render right-side-up. Detail = local contrast (std of luma) - outlines structure on either ground.
cellsINT12000200–30000Target seed count - the actual number of tessellation sites, not a relative density scale. The same photo at higher canvas resolution keeps the same mesh structure, scaled up with the canvas.
density_gammaFLOAT2.50.5–4Contrast curve applied to the normalized field before seed sampling. Higher = seeds concentrate more sharply into the hottest regions of the field.
seedINT420–4294967295Random seed for seed placement (MINSTD rejection sampling). Same seed used every frame in a batch.
mesh_weightFLOAT1.00.5–3Multiplies both ridge stroke widths (normal and the extra-dense tier) before the 2x supersample scale - thicken or thin the wireframe without changing seed density.
cell_fillFLOAT0.000–0.5Optional flat fill under each cell's wireframe, brightness-scaled by that cell's own field value (the mask's value when density_mask is wired - flat under a binary mask; plate-tone under the wirework). 0 disables.
seed_dotsBOOLEANfalseDraw a small dot at each accepted seed location.
overlay_opacityFLOAT1.000–1Master opacity for the mesh: cell fill, ridges, seed dots, and frame text.
image_opacityFLOAT0.200–1Fade the background photo. Low by default - the mesh IS the image here (density follows the photo's own field), not an overlay drawn on top of it.
texture_opacityFLOAT0.500–1Opacity of the noise overlay (screen blend)
frame_text_sizeINT126–72Font size of the 4 corner labels
frame_text_tlSTRINGDesign & StrategyTop-left corner frame text
frame_text_trSTRINGAKURATETop-right corner frame text
frame_text_blSTRINGBottom-left corner frame text
frame_text_brSTRINGBottom-right corner frame text
paletteCOMBOwhiteOnDarkColor palette for background and stroke color
bg_colorSTRING#0a0a0aCustom palette background color (hex). Used when palette = custom
stroke_colorSTRING#d8d8d8Custom palette stroke color (hex). Used when palette = custom
size_presetCOMBOmatch inputCanvas size. 'match input' keeps the source image dimensions; presets cover-fit (crop-to-fill, centered) the source before resizing
textureoptIMAGEOptional texture/noise image, screen-blended over the canvas. If empty and texture_opacity > 0, a procedural grain fallback is used
density_maskoptMASKOptional mask that DRIVES SEED DENSITY, replacing density_source entirely: white = fine/dense cells, black = coarse. density_gamma still applies. Only the mask's relative structure matters (it is re-normalised to its own maximum: a uniform grey mask equals a white one). At mask_min 0 black zones can render with NO ridges crossing them - raise mask_min to keep a sparse mesh everywhere. Cover-fitted like the photo when sizes differ; frame 0 used for batches (all frames then share one tessellation); an empty or non-mask-shaped input is ignored with a console note.
mask_minoptFLOAT0.0100–1Minimum seed density where density_mask is black, as a fraction of the maximum. 0 = the mask alone decides (black can go empty); default 0.01 keeps a sparse coarse mesh everywhere. Only active when density_mask is wired.

Outputs (3)

NameTypeDescription
imageIMAGE
overlay_onlyIMAGE
overlay_alphaMASK