Schematic Construction
A full projective-geometry annotation plate, computed from your photo, not drawn on top of it
- image
- pixelate_mask
- image
- overlay_only
- overlay_alpha
- element_table
If Schematic Overlay is the one-stop "annotated product shot" look, Schematic Construction is the heavy sibling: it lays a full projective-geometry plate over the photo - two convergent ray pencils, concentric spheres, computed intersections, a graduated scale, a running data column - the whole Kepler-era diagram apparatus. This is the node you reach for when you want a render to read as a measured drawing, something that looks like it was produced by an astronomer with a pencil and a very long ruler, rather than a sticker sheet.
The through-line is that everything on the plate is computed, not drawn. Every label sits on a point the geometry actually produced. If Overlay is decoration, Construction is argument - and the seed doesn't just shuffle content, it chooses among valid layouts.
How it works
The pipeline starts with the same detection engine as Overlay: the image is scored on a block_size grid, points above threshold become the anchors, and two foci shoot rays at them. The primary focus (focus_a_x / focus_a_y) carries a convergent ray pencil (rays_a), and the secondary focus (focus_b_enabled, rays_b) is what produces the interesting intersections - the tooltip's point: the second pencil creates the non-trivial quadratic intersections where B's rays cut A's spheres. focus_clear keeps the focus itself untangled by starting rays a fixed fraction out from it.
Concentric spheres (sphere_count, sphere_inner, sphere_outer, sphere_span) sit about the primary focus, and the annotation engine computes every ray-meets-sphere intersection. marker_every thins the markers, label_every thins the labels, and a label collision resolver silently drops any label it can't place cleanly - so annotation thins out exactly where the geometry is densest. That's a feature, not a bug; without it the plate would be unreadable mush.
Then the micrographic dressing: a hatched lune (lune_density), a graduated scale on the outer sphere (scale_ticks), frame-exit labels giving each ray its index and true bearing (exit_labels_every), leader callouts (callout_count), and the running IDX / ANG / REF data column (data_column_rows). Three weight classes - datum, construct, fine - keep the hierarchy readable, and line_weight scales them together.
The inputs that matter
image- the photograph. Its own tooltip is the whole philosophy: "It is never altered outside a pixelate zone."seed- the big one. It picks focus positions within their legal bands, which detected points the rays aim through, sphere radii, which gap carries the lune, and which intersections become pixelate zones. Same seed, same plate - deterministic across a batch.focus_a_x/focus_a_y,focus_b_enabled- where the geometry converges. Everything resolves to focus A, so nudging it re-aims the whole plate.rays_a/rays_b/sphere_count- density of the two pencils and the spheres. This is where you control how busy the plate is.palette- only three options here:whiteOnDark,blackOnLight, orcustom(withbg_color/stroke_color). The tooltip's advice is spot on: useblackOnLighton a light photograph, because polarity is how this node stays legible rather than shadowing every stroke.elements(optional) - the addressable-elements override string. One per line, e.g.rays_b: offorspheres: +0.02,-0.01, with offsets as normalised canvas fractions so they survive a resolution change. Every group also has its ownoff_*_x/off_*_yoffset widgets.pixel_size- mosaic tile size;1disables pixelation. The sample zones land on computed intersections, so every patch sits on an event the construction produced.
Outputs and where they wire
Same three image outputs as Overlay - image (full composite), overlay_only (the plate without the photo, for compositing elsewhere), overlay_alpha (a 0-to-1 mask of everywhere a vector element was drawn) - plus a fourth:
element_table- a STRING listing the resolved element table. The README's trick: paste any line back intoelementsand edit it - that's how you nudge individual elements without hunting for widgets.
Install and getting comfortable
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 appears under AKURATE/Schematic alongside Overlay and Voronoi. Only dependency is Pillow (already in ComfyUI), no models. If the annotation looks too small, that's type_scale (it scales all annotation, and the tooltip notes the type stays a fixed fraction of canvas - it's texture, not content, so it won't hold up to heavy upscaling). If the plate is too dense, raise marker_every and label_every rather than dropping the whole thing. And if you're overwhelmed by the ~45 inputs, ignore almost all of them at first: set image, pick a palette, nudge focus_a_x / focus_a_y, and let seed do the layout roulette. It's a fiddly node - that's the point - and it's the only one I've seen that ships a derivation document for why each line is where it is.
Inputs (70)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | The photograph the plate is drawn over. It is never altered outside a pixelate zone. | |
| seed | INT | 420–2147483647 | Chooses among valid LAYOUTS as well as content: focus positions within their legal bands, which detected points the rays aim through, sphere radii, which gap carries the lune, which intersections become zones. |
| detection_mode | COMBO | combined | What the detector scores. Drives where every ray aims and where zones land. |
| block_size | INT | 164–96 | Detection block size in pixels, as Overlay's. |
| threshold | INT | 280–255 | Minimum block score to accept a point. |
| max_circles | INT | 261–200 | Maximum detected points the pencils can aim through. |
| min_distance | INT | 701–1000 | Minimum spacing between detected points, in pixels. |
| min_radius | INT | 81–500 | Smallest detected-point radius. Only visible when detection_circles is enabled in `elements`, since the circles are the one element that draws a point's radius. |
| max_radius | INT | 461–500 | Largest detected-point radius. Only visible when detection_circles is enabled in `elements`. |
| focus_a_x | FLOAT | 0.150–1 | Primary focus X, as a fraction of width. Everything converges here; the seed varies it within a band. |
| focus_a_y | FLOAT | 0.880–1 | Primary focus Y, as a fraction of height. |
| focus_b_enabled | BOOLEAN | true | The second focus. The astronomical reference has two, and the second pencil is what produces the non-trivial intersections. |
| focus_b_x | FLOAT | 0.820–1 | Secondary focus X, as a fraction of width. |
| focus_b_y | FLOAT | 0.140–1 | Secondary focus Y, as a fraction of height. |
| focus_clear | FLOAT | 0.0550–0.3 | Rays start this far out from the focus, as a fraction of the short side, so the focus stays clear instead of tangling. Applies identically to BOTH foci. |
| rays_a | INT | 300–200 | Rays in the primary pencil. |
| rays_b | INT | 200–200 | Rays in the secondary pencil. |
| ray_jitter | FLOAT | 0.0500–0.5 | Angular scatter applied to each ray, in radians. |
| sphere_count | INT | 40–24 | Concentric spheres about the primary focus. |
| sphere_inner | FLOAT | 0.340.02–2 | Innermost sphere radius, as a fraction of the short side. |
| sphere_outer | FLOAT | 0.840.02–3 | Outermost sphere radius, as a fraction of the short side. |
| sphere_span | FLOAT | 985–360 | Angular sweep of the sphere arcs, in degrees. |
| marker_size | FLOAT | 0.00200.0002–0.02 | Intersection marker radius, as a fraction of the short side. |
| marker_shape | COMBO | circle | Shape drawn at every computed intersection. The two crosses are stroke-only by nature and ignore marker_fill. |
| marker_fill | BOOLEAN | true | Fill the marker, or draw its outline only. Ignored for the cross shapes. |
| marker_every | INT | 11–20 | Mark every Nth computed intersection. Raise it to thin the plate. |
| label_every | INT | 31–40 | Label every Nth marked intersection. |
| lune_density | INT | 344–200 | Hatch lines in the lune, the shaded band between two rays across one sphere gap. |
| scale_ticks | INT | 502–400 | Graduations along the outer sphere. |
| scale_label_every | INT | 51–40 | Number every Nth graduation. |
| type_scale | FLOAT | 1.000.3–3 | Scales all annotation. Type stays a fixed fraction of canvas, so it is sub-legible at any resolution: it is texture, not content. |
| exit_labels_every | INT | 21–40 | Label every Nth ray where it leaves the frame, with its index and true bearing. |
| callout_count | INT | 20–20 | Leader callouts into micro-blocks. |
| data_column_enabled | BOOLEAN | true | The running IDX / ANG / REF column keyed to the ray table. |
| data_column_rows | INT | 151–60 | Rows in the data column. |
| zone_count | INT | 30–40 | Pixelate sample zones, placed on computed intersections so each patch sits on an event the construction produced. |
| pixel_size | INT | 141–200 | Mosaic tile size, identical to Overlay's. 1 disables pixelation. |
| zone_size_min | FLOAT | 442–1000 | Smallest zone half-extent in pixels. Each zone is drawn a size at random between min and max, so the patches vary instead of being a uniform grid. Set min equal to max for Overlay's fixed-size behaviour. |
| zone_size_max | FLOAT | 922–1000 | Largest zone half-extent in pixels. Zone spacing is computed from THIS value, so patches never overlap even when a small one lands beside a large one. |
| pixel_stroke | BOOLEAN | true | Outline each zone, as Overlay does. |
| palette | COMBO | whiteOnDark | Ink polarity. Use blackOnLight on a light photograph: polarity is how this node stays legible, rather than shadowing every stroke. |
| bg_color | STRING | #101014 | Background behind the photograph and the ground of the overlay_only output. Only applies when palette is set to custom, and it is hidden in `image` at image_opacity 1.0 since the photograph covers it. |
| stroke_color | STRING | #FFFFFF | Ink colour. Only applies when palette is set to custom. |
| line_weight | FLOAT | 1.000.1–6 | Scales all three weight classes together. The classes themselves are fixed by the spec. |
| overlay_opacity | FLOAT | 1.000–1 | Opacity of every drawn mark. |
| image_opacity | FLOAT | 1.000–1 | Opacity of the photograph under the plate. |
| frame_text_size | FLOAT | 9.01–60 | Corner text size, per mille of the short side. |
| frame_text_tl | STRING | DESIGN & STRATEGY | Top-left plate text. |
| frame_text_tr | STRING | AKURATE | Top-right plate text. |
| frame_text_bl | STRING | Bottom-left plate text. | |
| frame_text_br | STRING | Bottom-right plate text. | |
| elementsopt | STRING | Per-ELEMENT overrides, one per line: 'rays_b: off', 'spheres: +0.02,-0.01', 'g_annot: +0.00,+0.03'. Offsets are normalised canvas fractions applied after anchoring. Read element_table for every valid name. | |
| pixelate_zonesopt | STRING | Extra zone centres as 'x,y;x,y', exactly as Overlay parses them. | |
| pixelate_maskopt | MASK | Extra zone centres taken from a mask, exactly as Overlay does. | |
| off_zones_xopt | FLOAT | 0.000-1–1 | Offset the g_zones group as a unit: the pixelate sample zones. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_spheres_xopt | FLOAT | 0.000-1–1 | Offset the g_spheres group as a unit: the concentric spheres and their captions. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_rays_xopt | FLOAT | 0.000-1–1 | Offset the g_rays group as a unit: both ray pencils. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_marks_xopt | FLOAT | 0.000-1–1 | Offset the g_marks group as a unit: the computed intersections, their labels and the lune. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_focus_xopt | FLOAT | 0.000-1–1 | Offset the g_focus group as a unit: both focus marks and their labels. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_scale_xopt | FLOAT | 0.000-1–1 | Offset the g_scale group as a unit: the graduated scale and its numerals. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_annot_xopt | FLOAT | 0.000-1–1 | Offset the g_annot group as a unit: frame-exit labels, callouts, the data column, detection circles. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_frame_xopt | FLOAT | 0.000-1–1 | Offset the g_frame group as a unit: the four corner plate texts. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_zones_yopt | FLOAT | 0.000-1–1 | Offset the g_zones group as a unit: the pixelate sample zones. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_spheres_yopt | FLOAT | 0.000-1–1 | Offset the g_spheres group as a unit: the concentric spheres and their captions. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_rays_yopt | FLOAT | 0.000-1–1 | Offset the g_rays group as a unit: both ray pencils. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_marks_yopt | FLOAT | 0.000-1–1 | Offset the g_marks group as a unit: the computed intersections, their labels and the lune. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_focus_yopt | FLOAT | 0.000-1–1 | Offset the g_focus group as a unit: both focus marks and their labels. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_scale_yopt | FLOAT | 0.000-1–1 | Offset the g_scale group as a unit: the graduated scale and its numerals. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_annot_yopt | FLOAT | 0.000-1–1 | Offset the g_annot group as a unit: frame-exit labels, callouts, the data column, detection circles. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
| off_frame_yopt | FLOAT | 0.000-1–1 | Offset the g_frame group as a unit: the four corner plate texts. Normalised canvas fraction, applied after anchoring, so members keep their shared edges. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| overlay_only | IMAGE | — |
| overlay_alpha | MASK | — |
| element_table | STRING | — |