Image Text Overlay
Put Text on an Image Without an AI Model (or an API)
- image
- IMAGE
Sometimes you don't want the model to generate text into your image - you want to put text on your image. A watermark, a label, a "PREVIEW" stamp across a test render, a caption under a character sheet. This node is the boring, dependable way to do that: feed it an image and a string, it draws the text with Pillow, and hands you back the composite. No model download, no API key, no prompt. If it feels like it shouldn't need to exist, that's because in a saner world ComfyUI would ship this in core. It doesn't, so this is one of those one-file custom nodes you grab, use twice a week, and forget.
One thing to clear up before you go looking: the pack title says "Text Overlay Plugin" but the node class is Image Text Overlay, and it's the only node in the pack. There's no companion "remove text" or "text to mask" node. It does exactly one thing.
How it works
The source is a single ~70-line file with no dependencies beyond what ComfyUI already bundles. It takes your IMAGE tensor, converts it to a NumPy array, flips it into a PIL image, loads a font with ImageFont.truetype(), draws your string with ImageDraw.text(), and converts the whole thing back to a tensor. That round-trip is why it slots into the graph like any other image node: the output wires straight into Save Image, an upscaler, or - the README's own suggestion - a ControlNet preprocessor. Stamp words on your reference, feed it to a preprocessor, and the next generation pass tries to reproduce those letters. That's a cheap trick for forcing legible text without fighting the prompt, the same trick the KB's ControlNet essay describes for barcodes, logos, and QR codes.
The one genuinely quirky input is color. It's an integer between 0 and 16777215 - which is just 0xFFFFFF in disguise. The UI renders it as a color picker (that's the display: "color" flag in the node definition), and the code unpacks the integer into RGB. If you hand-edit workflow JSON, think of it as hex 0xRRGGBB.
The inputs that matter
- image - anything with an IMAGE output: the VAE decode, another overlay, a Load Image.
- text - your string. It looks multiline, but see the gotchas below before you trust that.
- font - a path to a
.ttfor.otffile. This is the one that bites people. - font_size - 1–256, default 16.
- x / y - pixel coordinates with a top-left origin; 0,0 drops text in the corner.
- alignment - left, right, or center, relative to your
x. Center is handy for title bars. - color - the integer/hex color above, default black.
Output is a single IMAGE. That's the whole deal.
Installing it
No heavy lifting, which is rare and pleasant: the pack has no requirements.txt and downloads no model files. Easiest path is ComfyUI Manager - search for "Text Overlay" and install "ComfyUI - Text Overlay Plugin". Or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/mikkel/ComfyUI-text-overlay
Restart ComfyUI and it's there. It only needs Pillow, which ComfyUI already ships with.
Where people get burned
- The default font is a trap. The node defaults to
arial.ttf, which is a Windows font. On Linux or macOS - where most ComfyUI installs actually run - Pillow throwsOSError: cannot open resourcethe moment you hit Queue. Point the font field at a real path instead:/usr/share/fonts/truetype/dejavu/DejaVuSans.ttfon most Linux boxes, or download a font you like and use its absolute path. I verified this failure locally; it's the first thing anyone hits. - It's unmaintained, and Pillow caught up with it. The last commit is from late 2023, and the code calls
draw.textsize(), which Pillow deprecated in 10.0 and removed in 12.0. On a recent ComfyUI install you can hit an AttributeError at runtime. It's effectively abandonware that still works on most setups. - The multiline field doesn't really do multiline.
draw.text()won't render\nthe way you'd hope - Pillow needsmultiline_textfor that, and this node doesn't use it. Keep strings single-line, or stack two overlay nodes. - One image at a time. The code squeezes off the batch dimension, so feed it single frames; in a video pipeline, apply it per frame rather than to the whole batch.
When people want text baked into the image with inpainting, they reach for AnyText and its relatives - and those are fragile, heavy, and frequently broken by their own repo churn. When you just want words on top, this is the dependable one.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| text | STRING | Hello | — |
| font_size | INT | 161–256 | — |
| x | INT | 0 | — |
| y | INT | 0 | — |
| font | STRING | arial.ttf | — |
| alignment | COMBO | left | 3 options: left, right, center |
| color | INT | 00–16777215 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |