Ideogram 4 is a 9.3B open weights DiT. Its typography is the best of anything you can run locally: exact strings, several text blocks at once, small print that stays legible, and bbox control over where all of it lands. Getting it running on a Mac took some figuring out, so here is the working setup, what it costs, and the prompt format that makes the difference.
Everything below is a straight render at 1088x1920. No upscaler, no retouching, no compositing.
Repo: github.com/Bambushu/ideogram4-mac
Screen printed travel poster. Three text sizes, all exact, V4_TURBO_12 at 12 steps.
Neon signage. Script, block caps and a small amber line, each correctly lit and reflected in the wet street.
Packaging. Five strings including the small print.
Dense hand lettered chalk. Six separate text blocks, all legible, with eraser ghosting behind them.
Swiss type poster. Enormous letterforms cropped by the frame, plus three supporting lines.
ComfyUI already ships an official template
You should know this before deciding you need my workflows. It lives under Templates, Image, "Text to Image (Ideogram v4)". It is the authority on the recipe, and the recipe in my files is taken from it rather than invented.
What it does not cover is Apple Silicon. Which checkpoint works, what it costs per step, what it peaks at in memory: none of that is in there. It is also built as a subgraph wrapping math and JSON extraction nodes to drive preset selection, which is fine to use and awkward to read. So I made two flat versions where every node is visible, added a seed scan variant, and measured the whole thing on a Mac.
Getting it running on a Mac
The models are on HuggingFace at Comfy-Org/Ideogram-4. You need four files, and note there are two UNets. Ideogram 4 does CFG with a separate unconditional model instead of running one model twice, so DualModelGuider evaluates both and both stay resident while sampling.
ideogram4_fp8_scaled, 9.28 GBideogram4_unconditional_fp8_scaled, 9.28 GBqwen3vl_8b_fp8_scaled, 10.59 GB (CLIP type: ideogram4)flux2-vae, 0.34 GB
On Apple Silicon you also need ComfyUI-AppleSilicon-FP8 by pawel-mazurkiewicz. Comfy-Org ships this model as fp8_scaled, int8_convrot and nvfp4, with no dense bf16 build, and MPS could not touch Float8_e4m3fn until that node landed. It installs MPS scoped patches at ComfyUI startup and decodes contiguous fp8 through a device lookup table. Its default path decodes to bf16 before the matmul rather than doing native fp8 arithmetic, so it is a compatibility win rather than a speed one, which is the author's own framing. That node is why any of this works.
Being precise, because it is easy to overclaim here: recent ComfyUI can fall back to dequantise then matmul for the other two formats, so saying they cannot run would be too strong. There is just no optimised quantised path for them on Metal. And outside ComfyUI entirely, MFLUX runs Ideogram 4 through MLX. This article is about the ComfyUI route.
The recipe
Straight from the official template:
Ideogram4Schedulerfor sigmas. It takes width and height, so the schedule is resolution aware. That is why there is no separate shift node. Keep its width and height in sync with your latent, and keep both multiples of 16.DualModelGuidercfg 7.0CFGOverridecfg 3.0, start 0.7, end 1.0, which is the guidance drop for the polish phaseeuler, with the negative as a
ConditioningZeroOutof the positive
Official presets: Quality 48 (48 steps, mu 0.0, std 1.5), which Ideogram calls its default, Default 20 (20, 0.0, 1.75), and Turbo 12 (12, 0.5, 1.75).
Useful property: presets seem to preserve composition at a fixed seed. Turbo and Default use different mu, but in the A/B I ran (one caption, seed 42, 1088x1920) the layout came out identical and only refinement changed. So you can scan at Turbo 12, then re render that seed at Default 20 or Quality 48. One caveat: that is a single caption at a single seed, not a sweep, so treat it as a working assumption and eyeball the final against the scan. It only holds at fixed resolution anyway, since latent shape sets the noise.
What it costs
48 GB M series, --lowvram, using the sampler's own reported s/it:
720x1280 (0.92 MP): 11.0 s/it
896x1600 (1.43 MP): 15.9 s/it
1088x1920 (2.09 MP): 23.3 s/it
Cost is linear with pixel count, roughly 11 to 12 s/it per megapixel. There is no cliff to avoid and no sweet spot to hunt for. You are trading minutes for resolution. Turbo 12 at 1088x1920 is about 5 minutes, Default 20 about 8, plus a one time model load of 1 to 2 minutes.
Memory is the tight part. Across a 14 render batch at 1088x1920, peak ranged from 36.8 to 44.4 GB out of 48, including roughly 14 GB of baseline from macOS and other apps. That left under 4 GB free at the top end. Nothing failed, but it is not much margin, so treat 2 MP as a ceiling rather than a starting point and close things before a long batch. Do not raise the MPS watermark to work around an OOM. Unified memory is shared with the OS, and you will trade a failed render for a hung machine. Lower the resolution instead.
The part that actually matters: it wants JSON
Ideogram 4 was trained on structured JSON captions. A plain text prompt is out of distribution, which gets you quietly worse images, weaker adherence, and more false positive refusals, with nothing telling you that is what happened.
{"high_level_description": "One paragraph: subject, action, setting, mood, format.",
"style_description": {"aesthetics": "...", "lighting": "...", "photo": "...",
"medium": "...", "color_palette": ["#E8DCC4", "#2B4C6F"]},
"compositional_deconstruction": {
"background": "What fills the frame behind everything.",
"elements": [{"type": "text", "bbox": [650, 90, 790, 910], "text": "DOLOMITI",
"desc": "heavy condensed geometric sans, all capitals, deep blue ink"}]}}Paste the whole thing into CLIPTextEncode. There is no special node. The JSON string is the prompt.
Things worth knowing. Top level key order matters. style_description carries either photo or art_style, never both. bbox is [y_min, x_min, y_max, x_max], integers 0 to 1000 regardless of render size, so note that y comes first. Give every text block its own element instead of using line breaks, because that is how you get real hierarchy. Describe typefaces by character, like "heavy condensed geometric sans", rather than by name.
Spatial relationships need to be concrete too. bbox places things. It does not describe how two elements relate. I asked for a swimmer silhouette "overlapping the enclosed counter of the letter O" and got an orange bird beside the word, on every seed, while the type rendered perfectly every time. Rewriting that element as a self contained description with its own clear space bbox fixed it first try. The corollary is useful: if every seed fails the same way, rewrite the element instead of laddering seeds.
Concrete beats generic, and it doubles as a refusal fix. Filler prose like "a polished professional setting" is out of distribution and attracts refusal cards even for harmless subjects. Refusals are trained into the model, so it renders a flat grey card rather than erroring. If it refuses on some seeds, a seed ladder rescues it. If it refuses on every seed, reword it, because making the vague parts concrete fixes most cases.
Full grammar writeup, both workflows and all the examples: github.com/Bambushu/ideogram4-mac
