kubakub frame in frame
Real rooms and parasites behind every window
- plan
- viewer
- matrix
- media
- world
- image
- plan
- area
- generate
- back
- walls
- shadow
- frames_json
- report
- regions
What it's for
The single most convincing trick in facade projection mapping isn't a bright colour or a big animation. It's making the audience believe there's a room behind the wall. The window stops being a lit rectangle and becomes an opening - parallax, side walls, a back wall in shadow - and the building suddenly has depth it doesn't physically have.
kubakub frame in frame is the node that builds that in the viewer's perspective. Every region whose plan gives it a depth in metres becomes a frame seen from where the audience actually stands. Positive depth means a room behind the frame; negative depth means a parasite - something sticking out of the facade toward the viewer, with a shadow on the wall behind it, which is the other half of the illusion and the more surprising one.
It replaced the older lab node (frame guide) and it's the better tool: the guide node gives you masks, this one also gives you media placement, the plan entries for a diffusion pass, and a generate mask that tells the sampler exactly which pixels to repaint.
How it works
Three sources per frame, and they're chosen per region in the region plan via fif_source:
generate- the node draws the perspective guide and you feed the image to the region sampler with a high denoise (~0.95) and the reference set to self. The model invents the room inside the geometry you drew, so you get light and detail that obey the perspective rather than fighting it.media- corner-pin one of your images onto the back wall. Wire a batch intomediaand name them withmedia_names; regions pick one withfif_media = <name>or by index.world- one shared world image behind the whole facade. It's stretched to the matrix so that, through the viewer projection, it maps 1:1 onto the building - which means a single panorama can become the city behind every window at once.
fif_depth_m is the switch in the plan. 3 means a room 3 metres deep; -0.5 means a balcony sticking half a metre out. Windows made of several panes are treated as one opening by window_hull (on by default), so the mullions stay in front of the glass instead of being carved up.
The parasites get their own physics: shadow_strength (0.45), shadow_angle_deg (60, where 0 is right / 90 is down / 180 is left) and shadow_length per metre of protrusion. That shadow is what sells it. Side walls of media and world frames get shaded in the content's own colour, with brightness set by wall_level (0.7) - a wall that's too bright reads as a lit flat surface and kills the depth.
Inputs and outputs
Required: plan (from kubakub region plan, with fif_depth_m / fif_source / fif_media set per region), viewer (from kubakub audience viewpoint - this is where the perspective comes from and without it there is no frame), matrix (the full projection image the frames are drawn into), plus the shadow and wall tuning above.
Optional: media for fif_source = media, plus media_names, and world for the shared world behind the facade.
Outputs, and the ones that matter:
image- the matrix with every frame drawn in;generateframes carry the perspective guide.generate- the mask the region sampler should paint. That's your hand-off into diffusion.plan- passed on with every parasite added as a new region<name>_out(frame + walls + front face) carrying the frame's settings.area,back,walls,shadow- the masks, if you want to grade or diffuse them separately.regions- the plan's regions with the parasite regions included, so they work in anything that takes regions.frames_json- per frame: depth, source, scale, outline, back_quad. Theback_quadis the corner pin, i.e. exactly where your media image has to be warped to sit on that back wall.report- the frames with their depth and source.
Install
Manager → search kubakub nodes → install → restart. Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/kubakubkub/ComfyUI_KubaNodes
python -m pip install -r ComfyUI_KubaNodes/requirements.txt
ComfyUI 0.37.0+. The only added package is opencv-python-headless (keep your own OpenCV build if you have one). For the generate path you need an image model - Flux 2 Klein 4B is the smallest the pack's examples use; for media and world you need nothing but your own pictures. No Blender required here, even though it's a 3D-looking result.
Common issues
- Nothing appeared.
fif_depth_mis 0 for those regions. In the plan's syntax that's a rule like[W_F1_*]/fif_depth_m = 3- no depth, no frame, by design. - The room looks flat and pasted. Check the
viewer: a viewpoint that's centred and close makes every room's walls converge almost identically. Move the audience spot off-axis and the parallax appears. - Parasites don't pop. Push
shadow_strengthandshadow_length, and checkwall_level- bright side walls are the classic reason an object reads as a sticker rather than a box. - Diffusion ignored your geometry. Feed
generate(the mask), notarea, and set denoise high with the reference on self, which is what the pack's example workflow does.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| plan | KUBA_PLAN | region plan with fif_depth_m / fif_source / fif_media per region. | |
| viewer | KUBA_VIEWER | From kubakub audience viewpoint: where the audience stands. | |
| matrix | IMAGE | The matrix (the full projection image at delivery size) the frames are drawn into. | |
| shadow_strength | FLOAT | 0.450–1 | Parasites (fif_depth_m < 0) cast a shadow on the facade; 0 = none. |
| shadow_angle_deg | FLOAT | 60-180–180 | Direction the shadow falls in the facade plane: 0 right, 90 down, 180 left. |
| shadow_length | FLOAT | 0.700–10 | Shadow length per metre the parasite sticks out (1 = light at 45 deg). |
| wall_level | FLOAT | 0.700–2 | Brightness of the side walls of media / world frames. |
| window_hull | BOOLEAN | true | On: a frame made of several parts (glass panes) is one opening, the gaps (mullions) stay in front. Off: the largest part only. |
| mediaopt | IMAGE | Images for fif_source = media (a batch; cover-fitted to each back wall). | |
| media_namesopt | STRING | One name per media image, for fif_media = <name>. Or use indices. | |
| worldopt | IMAGE | The shared world behind the facade (stretched to the matrix: through the viewer projection it maps 1:1 onto the facade). |
Outputs (10)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | The matrix with all frames; generate frames carry the guide. |
| plan | KUBA_PLAN | The plan for the region sampler: every parasite is a new region '<name>_out' (frame + walls + front face) with the frame's settings. |
| area | MASK | All frame pixels. |
| generate | MASK | Frames the region sampler should paint (fif_source = generate). |
| back | MASK | Back walls (rooms) and front faces (parasites). |
| walls | MASK | Side walls. |
| shadow | MASK | Where parasites darken the facade. |
| frames_json | STRING | Per frame: depth, source, scale, outline, back_quad (corner pin). |
| report | STRING | The frames with their depth, source and scale. |
| regions | KUBA_REGIONS | The regions of the plan output, with the '<name>_out' regions of the parasites: for masks, previews and other nodes that take regions. Without parasites these are the plan's own regions. |