[{"data":1,"prerenderedAt":581},["ShallowReactive",2],{"blog-\u002Fblog\u002Fcodex-img":3,"mp-\u002Fblog\u002Fcodex-img":558,"more-posts-\u002Fblog\u002Fcodex-img":563},{"id":4,"title":5,"body":6,"category":541,"date":542,"description":543,"draft":544,"extension":545,"meta":546,"navigation":547,"path":548,"project":18,"seo":549,"stem":551,"tags":552,"__hash__":557},"blog\u002Fblog\u002Fcodex-img.md","codex-img generates images from the terminal with your ChatGPT subscription",{"type":7,"value":8,"toc":533},"minimark",[9,25,72,75,82,89,95,100,103,106,110,124,184,191,195,201,208,212,215,304,311,354,360,363,370,373,408,412,415,483,490,494,526,529],[10,11,12,19,20,24],"p",{},[13,14,18],"a",{"href":15,"rel":16},"https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fcodex-img",[17],"nofollow","codex-img"," generates and edits images from the command line with a ChatGPT subscription. It sends your prompt to the same image endpoint the Codex CLI uses, with the login ",[21,22,23],"code",{},"codex login"," already saved, and writes the result to disk.",[26,27,32],"pre",{"className":28,"code":29,"language":30,"meta":31,"style":31},"language-sh shiki shiki-themes github-light github-dark","codex-img \"a red fox in snow, flat vector\" -o fox.png\ncodex-img \"make it night with aurora\" -i fox.png -o fox-night.png\n","sh","",[21,33,34,53],{"__ignoreMap":31},[35,36,39,42,46,50],"span",{"class":37,"line":38},"line",1,[35,40,18],{"class":41},"sScJk",[35,43,45],{"class":44},"sZZnC"," \"a red fox in snow, flat vector\"",[35,47,49],{"class":48},"sj4cs"," -o",[35,51,52],{"class":44}," fox.png\n",[35,54,56,58,61,64,67,69],{"class":37,"line":55},2,[35,57,18],{"class":41},[35,59,60],{"class":44}," \"make it night with aurora\"",[35,62,63],{"class":48}," -i",[35,65,66],{"class":44}," fox.png",[35,68,49],{"class":48},[35,70,71],{"class":44}," fox-night.png\n",[10,73,74],{},"These are the images those two commands produced, with the prompts exactly as written, converted to lossy WebP for this page (1.2 MB and 1.5 MB became 41 KB and 78 KB). The first took 33 seconds:",[10,76,77],{},[78,79],"img",{"alt":80,"src":81},"A flat vector illustration of a red fox standing in snow, with snowy pines and mountains behind it","\u002Fimg\u002Fblog\u002Fcodex-img\u002Ffox.webp",[10,83,84,85,88],{},"The second passes the first as a reference with ",[21,86,87],{},"-i",". The edit kept the fox, its pose and the whole composition, and changed only the lighting and the sky:",[10,90,91],{},[78,92],{"alt":93,"src":94},"The same fox and landscape at night, under green and purple northern lights","\u002Fimg\u002Fblog\u002Fcodex-img\u002Ffox-night.webp",[96,97,99],"h2",{"id":98},"why","Why",[10,101,102],{},"My subscription includes image generation, but in the terminal the usual way to use it is to ask the Codex agent to draw something. That puts a chat model between me and the image model, and it may rewrite the prompt on the way. I wanted one command, one request, and my prompt reaching the image model exactly as written.",[10,104,105],{},"The other reason is coding agents. When Claude Code needs an icon or a cover for something it's building, it should be able to call a CLI and get a file back, like any other tool.",[96,107,109],{"id":108},"what-i-learned-about-the-endpoint","What I learned about the endpoint",[10,111,112,113,116,117,116,120,123],{},"The endpoint accepts a prompt, ",[21,114,115],{},"size",", ",[21,118,119],{},"quality",[21,121,122],{},"background"," and up to five reference images. It follows some of them more loosely than you'd expect:",[125,126,127,144,150,156,178],"ul",{},[128,129,130,135,136,139,140,143],"li",{},[131,132,133],"strong",{},[21,134,115],{}," ",[131,137,138],{},"is a hint."," A ",[21,141,142],{},"1536x1024"," request has come back at 1536x1024 and also at 1370x1148. The shape you describe in the prompt seems to count for at least as much: the cover of this post asked for \"a very wide 2.3:1 banner\" and came back at 1902x827.",[128,145,146,149],{},[131,147,148],{},"The model field is ignored."," The backend picks the image model itself, and the Codex CLI hardcodes the name anyway.",[128,151,152,155],{},[131,153,154],{},"Quality is capped at medium"," on the subscription.",[128,157,158,135,162,165,166,169,170,173,174,177],{},[131,159,160],{},[21,161,122],{},[131,163,164],{},"defaults to the backend's choice, and that can be transparent."," I didn't ask for transparency in the fox above, but ",[21,167,168],{},"--json"," reported ",[21,171,172],{},"\"background\":\"transparent\"",": the sky came back partly see-through, which looks like black smudges in most viewers. Flattened onto white (which codex-img does when it writes JPEG), it's the clean image you see here. Pass ",[21,175,176],{},"-b opaque"," when you need a solid background.",[128,179,180,183],{},[131,181,182],{},"It doesn't validate anything."," Unknown values are silently ignored, so codex-img checks every option before sending, instead of spending quota on a request that quietly did something else.",[10,185,186,187,190],{},"codex-img only reads ",[21,188,189],{},"~\u002F.codex\u002Fauth.json",", and it never refreshes the token. Refresh tokens rotate, so refreshing from a second program could log Codex itself out. If the login has expired, codex-img exits with code 2 and asks you to open Codex, which renews it.",[96,192,194],{"id":193},"built-for-agents","Built for agents",[10,196,197,198,200],{},"Paths go to stdout, progress to stderr, and ",[21,199,168],{}," prints one line per image with its path, size and timing. Every kind of failure has its own exit code: 2 for login, 3 for quota, 4 for moderation. The repository includes an Agent Skill that tells Claude Code and Codex how to use all this. It covers when to spend quota (only when you asked for an image), not to retry a quota or login error, to look at the result before reporting back, and how to write prompts for the image model.",[10,202,203,204,207],{},"The skill also works without the binary. It comes with a small Python script that uses only the standard library and speaks the same flags, exit codes and JSON, but writes PNG only. Copying the skill folder into ",[21,205,206],{},"~\u002F.claude\u002Fskills"," is enough to get started.",[96,209,211],{"id":210},"smaller-files","Smaller files",[10,213,214],{},"The endpoint always returns PNG, and a 1536x1024 PNG is usually close to 1 MB. So I added local conversion to the binary. Here's one generated image, a flat red paper plane, in each output:",[216,217,218,231],"table",{},[219,220,221],"thead",{},[222,223,224,228],"tr",{},[225,226,227],"th",{},"Output",[225,229,230],{},"Size",[232,233,234,243,251,259,271,282,293],"tbody",{},[222,235,236,240],{},[237,238,239],"td",{},"PNG from the backend",[237,241,242],{},"946 KB",[222,244,245,248],{},[237,246,247],{},"PNG, recompressed losslessly (every PNG gets this now)",[237,249,250],{},"462 KB",[222,252,253,256],{},[237,254,255],{},"JPEG, quality 90",[237,257,258],{},"82 KB",[222,260,261,268],{},[237,262,263,264,267],{},"PNG, 256 colours (",[21,265,266],{},"-c 256",")",[237,269,270],{},"137 KB",[222,272,273,279],{},[237,274,275,276,267],{},"PNG, 64 colours (",[21,277,278],{},"-c 64",[237,280,281],{},"8 KB",[222,283,284,290],{},[237,285,286,287,267],{},"WebP, lossy at quality 80 (the default for ",[21,288,289],{},"-f webp",[237,291,292],{},"7.5 KB",[222,294,295,301],{},[237,296,297,298,267],{},"WebP, lossless (",[21,299,300],{},"--lossless",[237,302,303],{},"469 KB",[10,305,306,307,310],{},"The palette option works like pngquant and keeps transparency, so it's a good fit for icons and stickers. This sticker was generated with ",[21,308,309],{},"-b transparent"," and reduced to 64 colours, from 898 KB to 128 KB. It sits directly on the page, with no background of its own:",[26,312,314],{"className":28,"code":313,"language":30,"meta":31,"style":31},"codex-img \"sticker of a sleepy orange cat curled up, thick white border, flat vector\" -b transparent -o cat.png\ncodex-img convert cat.png -c 64          # -> cat.min.png\n",[21,315,316,334],{"__ignoreMap":31},[35,317,318,320,323,326,329,331],{"class":37,"line":38},[35,319,18],{"class":41},[35,321,322],{"class":44}," \"sticker of a sleepy orange cat curled up, thick white border, flat vector\"",[35,324,325],{"class":48}," -b",[35,327,328],{"class":44}," transparent",[35,330,49],{"class":48},[35,332,333],{"class":44}," cat.png\n",[35,335,336,338,341,344,347,350],{"class":37,"line":55},[35,337,18],{"class":41},[35,339,340],{"class":44}," convert",[35,342,343],{"class":44}," cat.png",[35,345,346],{"class":48}," -c",[35,348,349],{"class":48}," 64",[35,351,353],{"class":352},"sJ8bj","          # -> cat.min.png\n",[10,355,356],{},[78,357],{"alt":358,"src":359},"A sticker of a sleepy orange tabby cat curled up, with a thick white border and a transparent background","\u002Fimg\u002Fblog\u002Fcodex-img\u002Fcat-sticker.png",[10,361,362],{},"The transparency wasn't perfectly clean: faint, almost invisible specks floated outside the white border, and they showed up on this site's dark background. I cleared pixels under 35% opacity with ImageMagick before reducing the colours.",[10,364,365,366,369],{},"One thing surprised me: dithering, which is normally on in tools like this, made the paper plane's 64-colour version 109 KB instead of 13 KB (both before recompression). The dither noise spreads across the flat background and compresses badly, so in codex-img dithering is off unless you ask for it with ",[21,367,368],{},"--dither",".",[10,371,372],{},"All of this also works on images you already have, with no login and no quota:",[26,374,376],{"className":28,"code":375,"language":30,"meta":31,"style":31},"codex-img convert hero.png -o hero.webp\ncodex-img convert icon.png -c 64        # -> icon.min.png\n",[21,377,378,392],{"__ignoreMap":31},[35,379,380,382,384,387,389],{"class":37,"line":38},[35,381,18],{"class":41},[35,383,340],{"class":44},[35,385,386],{"class":44}," hero.png",[35,388,49],{"class":48},[35,390,391],{"class":44}," hero.webp\n",[35,393,394,396,398,401,403,405],{"class":37,"line":55},[35,395,18],{"class":41},[35,397,340],{"class":44},[35,399,400],{"class":44}," icon.png",[35,402,346],{"class":48},[35,404,349],{"class":48},[35,406,407],{"class":352},"        # -> icon.min.png\n",[96,409,411],{"id":410},"the-cover","The cover",[10,413,414],{},"The cover of this post came from codex-img on the first try:",[26,416,418],{"className":28,"code":417,"language":30,"meta":31,"style":31},"codex-img - -s 1536x1024 -o cover.png --json \u003C\u003C'EOF'\nWide banner cover image for a developer tool's project page. Deep\ncharcoal-to-plum background with a soft radial glow in the center...\nIn the exact center, a single glossy macOS-style app icon: a rounded\nsquircle with a warm coral-to-orange gradient, with a terminal prompt\nchevron \">\" and a small picture-frame symbol...\nEOF\n",[21,419,420,448,453,459,465,471,477],{"__ignoreMap":31},[35,421,422,424,427,430,433,435,438,441,445],{"class":37,"line":38},[35,423,18],{"class":41},[35,425,426],{"class":44}," -",[35,428,429],{"class":48}," -s",[35,431,432],{"class":44}," 1536x1024",[35,434,49],{"class":48},[35,436,437],{"class":44}," cover.png",[35,439,440],{"class":48}," --json",[35,442,444],{"class":443},"szBVR"," \u003C\u003C",[35,446,447],{"class":44},"'EOF'\n",[35,449,450],{"class":37,"line":55},[35,451,452],{"class":44},"Wide banner cover image for a developer tool's project page. Deep\n",[35,454,456],{"class":37,"line":455},3,[35,457,458],{"class":44},"charcoal-to-plum background with a soft radial glow in the center...\n",[35,460,462],{"class":37,"line":461},4,[35,463,464],{"class":44},"In the exact center, a single glossy macOS-style app icon: a rounded\n",[35,466,468],{"class":37,"line":467},5,[35,469,470],{"class":44},"squircle with a warm coral-to-orange gradient, with a terminal prompt\n",[35,472,474],{"class":37,"line":473},6,[35,475,476],{"class":44},"chevron \">\" and a small picture-frame symbol...\n",[35,478,480],{"class":37,"line":479},7,[35,481,482],{"class":44},"EOF\n",[10,484,485,486,489],{},"It also showed me a gap. At first codex-img could only write lossless WebP, and lossless formats struggle with smooth gradients: the cover came out at 623 KB, slightly bigger than the PNG. So I added lossy WebP, using libwebp. The cover on this page is now ",[21,487,488],{},"codex-img convert cover.png -f webp",", and it's 7.3 KB. For flat art the palette PNG is about as small and keeps exact colours; for gradients and photos, lossy WebP wins.",[96,491,493],{"id":492},"try-it","Try it",[10,495,496,497,502,503,506,507,509,510,513,514,517,518,521,522,525],{},"Download the binary for macOS or Linux from the ",[13,498,501],{"href":499,"rel":500},"https:\u002F\u002Fgithub.com\u002Ffoksa\u002Fcodex-img\u002Freleases\u002Flatest",[17],"latest release",", put it on your ",[21,504,505],{},"PATH",", and log in once with ",[21,508,23],{}," using your ChatGPT account. ",[21,511,512],{},"codex-img status"," checks the login without using any quota. Format conversion, lossy WebP, palette PNGs and ",[21,515,516],{},"convert"," are on ",[21,519,520],{},"main"," and will be in the next release. Until then, ",[21,523,524],{},".\u002Fscripts\u002Finstall.sh"," builds from source and links the skill into Claude Code and Codex.",[10,527,528],{},"This isn't an official API. It's the backend the Codex CLI uses, so it can break when OpenAI changes it. It's Apache-2.0 licensed.",[530,531,532],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":31,"searchDepth":55,"depth":55,"links":534},[535,536,537,538,539,540],{"id":98,"depth":55,"text":99},{"id":108,"depth":55,"text":109},{"id":193,"depth":55,"text":194},{"id":210,"depth":55,"text":211},{"id":410,"depth":55,"text":411},{"id":492,"depth":55,"text":493},"release","2026-09-28","A small Rust CLI that sends your prompt straight to the Codex image endpoint, using the login Codex already saved. No API key, no agent in between, and an Agent Skill so Claude Code and Codex can use it too.",false,"md",{},true,"\u002Fblog\u002Fcodex-img",{"title":5,"description":550},"A small Rust CLI that sends your prompt straight to the Codex image endpoint, using the login Codex already saved. No API key, no agent in between, and an Agent Skill for coding agents.","blog\u002Fcodex-img",[553,554,555,556],"ai","codex","claude","open source","aWtjESdWpl4jWMhwL64PfWpJFo75vQViQs22jseFiAA",{"path":559,"title":18,"description":560,"image":561,"detailImage":562,"imageFit":562,"coverLabel":562},"\u002Fprojects\u002Fcodex-img","Generate and edit images from the command line with your ChatGPT\u002FCodex subscription. No API key, and no agent in between.","\u002Fimg\u002Fproducts\u002Fcodex-img.webp",null,[564,565,569,573,577],{"path":548,"title":5,"date":542},{"path":566,"title":567,"date":568},"\u002Fblog\u002Fusage-window-starter","Usage Window Starter activates your next Claude and Codex session the minute the old one resets","2026-09-24",{"path":570,"title":571,"date":572},"\u002Fblog\u002Fdocument-iteration-skill-6-0","document-iteration-skill 6.0 rewrites the skill in positive terms","2026-09-23",{"path":574,"title":575,"date":576},"\u002Fblog\u002Fopen-claude-agent-sdk-0-50-0","open-claude-agent-sdk 0.50.0 fixes broken clean installs","2026-09-22",{"path":578,"title":579,"date":580},"\u002Fblog\u002Fcamera-video-converter-is-on-play-store","Camera Video Converter is on the Play Store","2026-09-02",1790599779439]