arkennemasis fal · Flux Control LoRA Canny · $0.04/MP
Structure control without installing a single ControlNet
- control_lora_image
- image
- images
- image_paths
- timings
- seed
- has_nsfw_concepts
- prompt
- info
ControlNet was the single most impactful thing to happen after text-to-image itself, and it has always been fiddly to set up: the right preprocessor for your architecture, the right union checkpoint, a strength value that depends on which model family you're on. Canny on Flux means a different implementation than Canny on SDXL - Flux ControlNets hook transformer attention layers rather than wrapping a UNet encoder - and the files don't transfer between bases.
This node is the lazy version. It's fal's FLUX Control LoRA Canny endpoint behind a ComfyUI node, and the whole configuration problem collapses into two sockets and one number.
What it does
Canny detects hard edges and produces a clean, thin edge map. Anything downstream that respects those edges - architecture, mechanical objects, scenes with clear contours - will hold its composition while the prompt decides what the surfaces are made of. That's the classic use: keep the building, change the materials and the light.
control_lora_image is required. That's the structure source: the reference photo, or an edge map you made yourself. control_lora_strength defaults to 1.0 and is the only knob you'll touch after the prompt.
On strength, the community baseline for Flux ControlNets landed around 0.6, with published comparisons noting that details started dropping off quickly above 0.7. That number came from union ControlNets in the local workflow. Here you're calling a purpose-built Canny LoRA endpoint, so 1.0 as a default isn't absurd - but if your output looks like the edge map is being traced rather than interpreted, that's the field to lower.
The inputs that matter
prompt- what the edges should become. Plain sentences.control_lora_image- the edge source.control_lora_strength- 1.0 default, lower it to loosen the grip.image(optional) - this one deserves a flag, because the tooltip calls it image-to-image and it is: it's the colour/content reference. Leave it empty and you get something new wearing your reference's skeleton. Connect a photo and the endpoint leans on it too, which is a different node in this pack (...canny_image_to_image) with astrengthcontrol.preprocess_depth- defaults to true, and its tooltip talks about extracting depth from images. On a Canny node. That's fal's schema showing its copy-paste from the depth endpoint; it isn't the field that does the work here. Leave it where fal put it.loras- a JSON field for extra LoRA URLs to merge in. Empty leaves it out, and for most people it stays empty.
The rest is the standard Flux set: image_size (landscape_4_3 default, custom for exact dimensions), num_inference_steps at 28, seed at -1, guidance_scale at 3.5 (Flux guidance - not the SDXL meaning of CFG), num_images, enable_safety_checker, output_format (jpeg here, not png).
Outputs
images plus image_paths (files land in output/fal/fal-ai_flux-control-lora-canny/), then timings, seed, has_nsfw_concepts, prompt, and info with the request id, estimate, files and fal's full answer.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Hishamahmer/comfyui-arkennemasis
Restart ComfyUI, or ComfyUI-Manager → Install via Git URL. requirements.txt only declares httpx, and that's for the pack's Codex nodes - the fal nodes need nothing past the standard library. Nothing is downloaded to your machine; the model lives on fal, your GPU stays idle.
FAL_KEY=your-key-from-fal.ai/dashboard/keys
goes in the .env next to run_nvidia_gpu.bat in the portable build (or the ComfyUI folder). No node in this pack has a key field - by design, so a key can't ride along inside a shared workflow file.
Cost, and the caching question
$0.04 per megapixel, so 1024×1024 is about four cents and a 2048-wide landscape is roughly sixteen - the price badge on the node follows your image_size and num_images live. max_cost_usd (default $20) refuses the run before upload if the estimate is over.
reuse_identical_run defaults on, which is a bigger deal here than on a text-to-image node. Canny is deterministic: same map, same prompt, same settings, same picture. Paying twice for it is pointless, and the node will return the file it already made instead. Where it becomes a trap is when you're iterating - tweak control_lora_strength and it's a different run, but if you forget which setting you last paid for you can't just re-queue and compare. Use the seed output and keep the settings in the graph.
If it doesn't work
Nothing like my reference came out. Check what you actually fed into control_lora_image. If it's a photo of a busy scene, the edge map is busy too and the model has to honour all of it. A clean, high-contrast subject gives you a usable edge map; a low-contrast one gives you noise.
The output looks traced. Lower control_lora_strength and let the prompt do more of the work.
Unexpected aspect ratio. image_size is a combo with its own default; if the reference is a wide photo and you left it on landscape_4_3, you asked for 4:3.
Inputs (19)
| Name | Type | Default | Description |
|---|---|---|---|
| prompt | STRING | The prompt to generate an image from. | |
| image_size | COMBO | landscape_4_3 | The size of the generated image. 'custom' uses the width and height below. |
| image_size_width | INT | 102416–14142 | Width in pixels when image_size is 'custom'. |
| image_size_height | INT | 102416–14142 | Height in pixels when image_size is 'custom'. |
| num_inference_steps | INT | 281–50 | The number of inference steps to perform. |
| seed | INT | -1-1–2147483647 | The same seed and the same prompt given to the same version of the model will output the same image every time. -1 leaves it to the model. |
| loras | STRING | The LoRAs to use for the image generation. You can use any number of LoRAs and they will be merged together to generate the final image. JSON. Empty leaves it out. | |
| guidance_scale | FLOAT | 3.500–35 | The CFG (Classifier Free Guidance) scale is a measure of how close you want the model to stick to your prompt when looking for a related image to show you. |
| num_images | INT | 11–4 | The number of images to generate. |
| enable_safety_checker | BOOLEAN | true | If set to true, the safety checker will be enabled. Disabling it requires account authorization; unauthorized requests are always checked, and images flagged as unsafe are returned as black images. |
| output_format | COMBO | jpeg | The format of the generated image. |
| control_lora_image | IMAGE | The image to use for control lora. This is used to control the style of the generated image. | |
| control_lora_strength | FLOAT | 1.000–2 | The strength of the control lora. |
| preprocess_depth | BOOLEAN | true | If set to true, the input image will be preprocessed to extract depth information. This is useful for generating depth maps from images. |
| imageopt | IMAGE | URL of image to use for image-to-image generation. | |
| max_cost_usdopt | FLOAT | 20.00–100000 | Safety cap. If the estimated cost of this run is above this many US dollars the node stops BEFORE sending anything to fal. 0 = no cap. |
| reuse_identical_runopt | BOOLEAN | true | If these exact inputs (the settings AND the same pictures/clips/audio) were already paid for and the files are still in output/fal, return them instead of paying again - also after a ComfyUI restart. Turn off for a fresh take with the same settings. |
| extra_jsonopt | STRING | Advanced: a JSON object merged into the request last, for any fal field this node has no box for. Example: {"seed": 7}. Leave empty normally. | |
| max_concurrentopt | INT | 11–32 | How many paid fal calls may run at the same time in one run, across every fal node. 1 (default) = one after another. This node starts its paid part only while fewer than this many fal calls are running. Once any fal node fails, the ones still waiting are not started, so nothing more is billed. |
Outputs (7)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | — |
| image_paths | STRING | — |
| timings | STRING | — |
| seed | INT | — |
| has_nsfw_concepts | STRING | — |
| prompt | STRING | — |
| info | STRING | JSON: request id, cost estimate, saved files and fal's full answer. |