The Problem
FLUX.2-class image generation normally requires a Python ML stack, a CUDA or MPS runtime, and roughly 16GB of RAM. That rules out headless servers, low-memory VMs, and Apple Silicon machines without a working PyTorch install. This repo removes those constraints: a fork of antirez/iris.c, it runs FLUX.2-klein-4B entirely in C with zero external dependencies beyond the C standard library.
What This Does
It generates images from text prompts, and optionally transforms existing images, using the FLUX.2-klein-4B model. All inference — the Qwen3-4B text encoder, the transformer, the VAE — is implemented in C across flux.c, flux_transformer.c, flux_image.c, flux_kernels.c, flux_qwen3.c, flux_tokenizer.c, and flux_vae.c. The integrated Qwen3 encoder means no external embedding step is needed.
Two properties matter operationally. Memory-mapped weights (default) cut peak memory from ~16GB to ~4-5GB, and the encoder is released after encoding to free ~8GB. MPS and BLAS acceleration are optional; the pure-C path runs anywhere.
How It Is Wired
Execution starts at main.c. The static import graph is empty — 4 internal modules, 0 import edges, no cycles — so the C modules are not linked by analyzable imports; wiring between them is by direct function calls that the analysis did not map. What is documented: the CLI parses flags in main.c, routes to text-to-image or image-to-image paths, and drives the pipeline in flux.c. A run touches the outside world in few hops: download_model.sh or download_model.py fetches weights over the network from HuggingFace (~16GB); inference opens the safetensors via flux_safetensors.c and memory-maps them; output is written as PNG to the path given by -o. flux_sample.c handles the denoising loop, flux_qwen3_tokenizer.c tokenizes the prompt, and flux_metal.m / flux_shaders.metal provide optional GPU kernels on Apple Silicon.
How To Use It
Build and run per the README:
make mps # Apple Silicon (fastest)
# or: make blas # Intel Mac / Linux with OpenBLAS
# or: make generic # Pure C, no dependencies
./download_model.sh # or: python download_model.py
./flux -d flux-klein-model -p "A fluffy orange cat sitting on a windowsill" -o cat.png
Image-to-image uses -i input.png with a descriptive prompt. --show displays results in Kitty/Ghostty terminals. No environment variables or keys are required. A Makefile defines the build targets; main.c is the CLI entry point.
Real-World Use
A practical fit: an 8GB-RAM headless Linux box with no CUDA toolkit, where the Python ML stack cannot run FLUX.2 at all. The binary is self-contained, weights are memory-mapped, and generation runs via a single command — suitable for a batch job or a thin wrapper service that shells out to ./flux and serves the resulting PNG.
Code Health & Issues
Static analysis (measured, not opinion) found 29 findings: 11 high, 18 medium, across 5 kinds.
- High — deep nesting (x16) —
flux.c,flux.h,flux_kernels.c: max indentation depth 13; control flow is hard to follow. Fix with early returns and extracted blocks. - High — duplicated code (x22) —
flux.c,flux_sample.c,flux_kernels.c,flux_qwen3.cand others: 22 repeated 6-line blocks across 8 files; extract shared helpers. - High — oversized files (x7) —
flux_transformer.c,flux.c,flux_image.c: 3356 code lines in one file; changes ripple widely. Split by responsibility. - Medium — unclosed file handles —
debug/debug_img2img_compare.py,run_test.py:open(...)not wrapped inwith; use context managers. - Medium — high branching density —
flux_image.c,flux_qwen3_tokenizer.c,flux_safetensors.c: 234 branch points over 794 lines; consider table-driven dispatch. - High — no CI — 17 source files, no workflow; every change merges unbuilt. Add a GitHub Actions workflow running
makeand the test suite on push/PR.
SDLC observations: tests and test vectors exist (test_vectors/, run_test.py), a license is present, and no committed secrets were found. There is no Dockerfile or lockfile.
The Bottom Line
This is a serious piece of systems engineering — a dependency-free, memory-efficient FLUX.2 implementation that genuinely runs where the Python stack cannot. The trade-off is maintainability: deeply nested, oversized files and duplicated logic will make changes slow. Use it if you need image generation on constrained hardware; budget refactoring time before extending it.