본문으로 건너뛰기
Back to blog
Image Production

When AI Can't Render Korean Text: Background from AI, Text from Code

AI image generators break Korean text, logos, and exact pixel dimensions. The fix: generate only the background scene with AI (Claude delegates to Codex imagegen), then layer Korean text and logos via HTML with web fonts, captured to PNG with a headless browser. A build log for automating blog illustrations and OG cards.

9 min readImage Production
Korean text AI imageAI image text corruptionAI image generationCodex imagegenOG image creationOpen Graph imageKorean web fontheadless browser screenshotflat vector illustrationthumbnail automationKakaoTalk link previewbatch image generation
When AI Can't Render Korean Text: Background from AI, Text from Code

Audience: marketers · solo operators · content creators  ·  Format: build log + production method  ·  2026-06-26

Key Summary · TL;DR

Q. Korean text and logos break in AI-generated images, what's the fix?

AI image generators can't produce Korean characters, accurate logos, or exact pixel dimensions. So the image and text layers are separated: generate only the background scene with AI (Claude has no image generation capability itself, so it delegates to Codex and uses its imagegen tool), then layer Korean text and the logo onto an HTML template using web fonts and capture the whole thing to PNG with a headless browser. Blog illustrations that carry no text let numbers speak through captions. OG cards that must carry text have the text added outside the pixel layer by code. For many pages, fill two spreadsheet columns (scene key, copy) and batch-generate everything at once.

3 lines you can use right now

  • Never ask AI to write text. Keep AI to the background only; add text and logos in code.
  • Claude has no image generation. Use codex exec to borrow the imagegen tool from a Codex instance logged into ChatGPT.
  • For headless rendering, embed Korean fonts as files in the HTML, and after swapping any image use ?v= to bust the cache.
Flat vector contrast: Korean-like characters inside a navy frame are a grey smear, while the same characters rendered outside the frame as a banner strip are perfectly crisp

Image: Text baked inside the image breaks. Text kept outside the image is crisp. That's the whole point of this article.

Delegate text to AI and it turns text into broken pixels

AI image generators draw pictures as pixels. The problem is that they draw characters the same way, as "pixels that look like characters." Ask for a Korean headline and you get a mangled blob that barely mimics the strokes.

Logos have the same problem. The generator has no idea what the real logo looks like and invents a plausible fake, slightly off in color, spacing, and weight. No matter how much you tweak the prompt, it never comes out right consistently. If you get a passable image on one attempt, the next attempt breaks it again.

Dimensions don't cooperate either. Tell it "output at 800 by 400" and the result won't match exactly. A card with wrong dimensions gets cropped by KakaoTalk (a Korean messaging app) or Facebook on its own terms. The three things a card most needs, text, a logo, and exact dimensions, just happen to be the three things AI image generators are weakest at.

So text was pulled out of the image

After a long fight with prompts, the direction changed. Asking AI to handle text was the mistake from the start. AI would be used only for mood and scene; text would be added separately.

The principle then became one line: AI makes the picture; text lives outside the pixels. Background art and text (title, logo) are treated as completely separate layers. The image is "atmosphere" that can afford a little softness. Text is "information" that can never break.

Flat vector diagram: an image layer and a text layer merge in the center to produce one finished card

AI handles the image layer; code handles the text layer. Combine them and you get one finished card. Two tools, one principle.

This single principle applies in two places. Blog illustrations that carry no text at all let numbers and conclusions be stated in captions below the image. Sharing cards that must carry text have their headline added outside the pixels by code. The tools differ; the principle is identical.

Claude borrows Codex to generate the image

So who actually generates the background image? Claude, the model writing this article, has no image generation capability. It can write text but it cannot draw. So it calls on a different tool nearby.

That tool is Codex. Codex is a coding agent that runs in the terminal, and when you log in once with a ChatGPT account it comes with an image generation capability (imagegen). Claude sends a single command to wake Codex and says "draw this scene and save it here."

The actual command looks like this. Don't be intimidated by the code. It's just "Codex, draw this with imagegen and save to the assets folder as a PNG" translated into machine-readable form.

codex exec --dangerously-bypass-approvals-and-sandbox - <<'PROMPT'
use the imagegen skill (built-in image_gen tool).
Flat vector editorial illustration, navy #1c3d5a base + beige,
single orange #ff8649 accent, no text, no faces.
Save to .../05-outputs/blog/assets/2026-06-26_hero.png
PROMPT
Three-step flow diagram: writing tool → key-unlocked relay tool → image file output

Claude calls Codex; Codex produces the image. The image generation is essentially borrowed via a ChatGPT login.

Multiple images can be queued at once, it takes a while, so run it in the background and keep writing in the meantime. In summary: Claude doesn't draw directly; it borrows Codex (which has drawing capability via its ChatGPT login) as the actual rendering engine.

Images without text are easy, offload numbers to captions

Start with the text-free case: the illustrations used throughout this blog. The rule is that they carry only concept, metaphor, or mood. Numbers and conclusions, anything that "must be said in words", never go inside the image.

Style is locked to a single set: flat vector, navy base, sand/beige tones, and a single orange #ff8649 accent. No text, no numbers, faces vague and without detail. This makes all illustrations within one article look like siblings.

Text-free illustration alongside a clearly readable caption strip beneath it, showing the two layers separated

No text in the image. Numbers and conclusions are handled by the caption below.

So where does a figure like "survival rate 30 vs. 10" get stated? The caption directly below the image. The image sets the mood; the number lives in the caption. This is the lowest-effort way to keep text outside the pixels.

Cards that must carry text: code adds the headline outside the pixels

Text-free images are the easy case. The harder problem is cards that must carry text, those sharing cards (OG images) that appear when a link is pasted into a chat. They need a title, a logo, and supporting copy, all crisp.

The same principle applies again. Generate only the background scene with AI, but this time leave one side empty. About 45% of the left is kept clear for text, with only the model and props filling the right half. Code then places the text in that empty space.

Text is written in HTML. A Korean web font renders the headline. A real logo SVG is placed accurately. Key words are colored with the accent. This HTML is then opened in a headless browser (Chrome running without a display window) and photographed to produce a PNG. Since text is drawn by fonts, it's always crisp and dimensions are always exact.

Flow diagram: HTML template passes through a headless browser to produce the finished PNG card

Background from AI, text from HTML. Captured by headless Chrome, the PNG has crisp text.

chrome --headless=new --hide-scrollbars \
  --force-device-scale-factor=1 --window-size=800,400 \
  --screenshot=card.png "file:///.../card.html"

Title length varies per page, so font size is adjusted automatically. With 440px reserved for the left text region, Korean characters are estimated at 1.0 width and Latin characters and spaces at 0.45, and the font is scaled between 40px and 62px. Short titles get large text; long titles shrink just enough to stay on one line without forced wrapping.

The reason to size so generously: KakaoTalk renders sharing cards at roughly 300px wide. For more on designing for that small size (the 9px math, contrast, gaze cueing), see the companion post (Fixing Invisible OG Images). This post focuses only on how to produce the cards.

Korean text broke even in headless, fonts were embedded into the file

When it seemed like everything was done, one more trap was hiding. Headless Chrome draws text using the fonts installed on that computer. So on a machine without the design font, or in a shared file, characters would come out wrong.

The cause is simple: if a font is referenced only by name, any environment that doesn't have that font installed substitutes a fallback. The fix was to embed the font file itself into the HTML as base64. With the font embedded, it travels with the file. The same design font renders on any machine, even offline.

Trap: Headless rendering ultimately relies on fonts installed on the host machine. If the Korean design font isn't embedded, any machine or shared file without the font falls back to a substitute. "It worked fine on my machine" is exactly the symptom you get at this point.

Before/after: a card with missing fonts shows garbled text; a card with embedded font shows crisp output

Referencing a font by name breaks on machines without it. Embedding the file makes it crisp everywhere.

The same logic applies to logos and model photos. Use the official SVG or PNG for the logo, not an AI-generated fake. Use the official cut-out (background-removed) photo for the model. Anything that must not break gets placed by code from the accurate original.

Dozens of cards can't be made by hand, two spreadsheet columns do it

Once you have the method for one card, scale is the next problem. Dozens of pages means dozens of cards. Editing HTML one by one quickly becomes unsustainable. So a single spreadsheet becomes the control tower.

Only two columns need filling per card. The scene key column specifies which background to use (by device, feature, or content type). The copy column holds the headline and sub-copy in one cell: // for line breaks, *asterisks* around a word for accent-color emphasis, || before sub-copy. The script reads each row and bakes all the cards at once.

ColumnWhat to writeExample
Scene keyWhich background scene to useinternet · mobile · apple
CopyHeadline, emphasis, line breaks, sub-copy in one cellInternet Sign-up // Cash Rebate || Apply instantly

Inside one cell: // breaks a line, *word* renders in accent color, text after || becomes sub-copy. The script parses these markers and places everything automatically.

Flat vector diagram: a spreadsheet with two highlighted columns feeding into a row of generated card thumbnails

Humans fill two columns, scene key and copy. The script bakes dozens of cards.

The last piece is hosting. Cards only work as OG images when served from a public URL. Push them to a public repository like GitHub and use that URL. One trap remains: swap an image and still see the old card for days. The cache is keyed on the URL, not the file content. Keeping the same URL while changing the file means the platform never knows. Append ?v=2 (or any new version number) to the URL and it's treated as a new address, breaking the cache.

Flat vector diagram: a version key unlocks the cache lock bound to the old URL, revealing the updated image

The cache is keyed on the URL. Changing the file without changing the URL changes nothing. Bump ?v= to open the lock.

Summary: when images break next time

It's one pipeline. Don't ask AI for text. Let Codex generate the image, let code place the text outside the pixels, embed fonts into the file, and when volume is high let the spreadsheet do the baking.

6 things to check when images break
  1. Division of labor: Are you asking AI for text, logos, or exact dimensions? Keep AI to the background only; handle text in code.
  2. Image generation: Claude has no image generation. Use codex exec with a ChatGPT-logged-in Codex instance and save the PNG to assets.
  3. Text-free images: Did you put numbers or conclusions inside the image? Pull them out to a caption below (flat vector, navy + orange, no text).
  4. Text-carrying cards: Leave ~45% of the background empty for text, then write headline and logo in HTML with a Korean font and bake via headless.
  5. Font: Is headless relying on the system font? Embed the Korean font as a file to eliminate "worked on my machine" bugs.
  6. Operations and cache: For many pages, use two spreadsheet columns (scene, copy) for batch generation; bump ?v= after any swap.

If you can only remember one thing, make it this:

AI makes the picture. Text lives outside the pixels. Anything that must not break, hand to code.

Sources

The production method described here (background from AI, text from code, headless baking, spreadsheet batch generation) is a record of how the sharing cards on one real site and the illustrations on this blog were actually made. That AI generators can't handle Korean characters, logos, or exact pixel dimensions is from direct experience (the degree of failure varies by generator and generation). Card dimensions, cache, and re-scraping were confirmed against the primary sources above. Company and brand details have been generalized.

Found this useful? Share it

Latest posts

Related projects

Get new posts by email

Insights on marketing, analytics, and dev, delivered to your inbox.