What a Midjourney prompt looks like when it comes from an image
A comma list of up to ten slots, each one filled by comparing a measured number against a fixed threshold — and one --ar parameter at the end that was never measured at all.
A Midjourney prompt generated from an image is a comma list of measured phrases plus a trailing --ar parameter. Nine of the ten phrases are measured on a 192 × 192 grid inside your browser. The --ar is the exception: it is arithmetic on your file’s original pixel width and height, computed before any measurement happens. It is also the only part of the line that can flatly contradict the words in front of it.
Here is a real output — a 640 × 360 photograph of grass and wildflowers shot straight into the sun, at the longest setting:
widescreen 16:9 composition, dark, low-key lighting, dominant palette of black, charcoal, silver, white, vivid, saturated colour, slightly warm tone, high contrast, moderate detail and texture, brighter edges with a darker centre, brighter upper half, moody and dramatic atmosphere --ar 16:9
That is 291 characters, 40 words, ten slots, and no full stop anywhere in it. Below: what the ten slots are, where the --ar actually comes from, why the two ends of the line sometimes disagree, and what this shape gives you that the Stable Diffusion, Flux and DALL·E versions of the same prompt do not.
Ten slots, always in the same order
The Midjourney line is assembled in src/compose.js in a fixed sequence. Nothing reorders it, and nothing is weighted — each phrase is dropped in exactly once:
| # | Slot | Chosen by |
|---|---|---|
| 1 | Framing + “composition” | Aspect ratio against the thresholds 2.2 / 1.7 / 1.4 / 1.15 / 0.9 / 0.7 / 0.55 |
| 2 | Exposure | Mean luminance against 175 / 130 / 80 |
| 3 | Dominant palette | Up to four nearest named colours, from 12-bit colour buckets merged at distance 52 |
| 4 | Saturation | Monochrome pixel share against 0.85, else mean saturation against 0.45 / 0.22 |
| 5 | Colour temperature | Mean red minus mean blue, against 14 / 5 / −5 / −14 |
| 6 | Contrast | Luminance standard deviation against 68 / 38 |
| 7 | Detail | Mean gradient magnitude against 15 / 7 |
| 8 | Lighting distribution | Centre mean vs border mean, at a 1.18× ratio |
| 9 | Vertical balance | Top mean vs bottom mean, at a 1.2× ratio (only present when the ratio is crossed) |
| 10 | Mood | Luminance and colour temperature together |
The three length settings simply truncate that list: short keeps 5 slots, medium keeps 8, long keeps 10. The palette line never moves — it is spliced in at index 2, so it is third at every length.
One counting trap: the long output above contains 14 commas but only 10 slots. Five of those commas live inside phrases — “dark, low-key lighting”, “vivid, saturated colour”, and the three separators between four colour names. If you are counting terms for a token budget, count slots, not commas.
The --ar is the one value that never touched the measurement grid
Everything else in the line is computed from the 192 × 192 buffer — 36,864 cells, whatever size your file is. The aspect parameter is not. In src/analyze.js, the width and height used for the ratio are taken from the original file dimensions, and only fall back to the buffer if those are missing:
const nw = (natural && natural.w) || W; const nh = (natural && natural.h) || H; ... ar: nw / nh, arParam: aspectParam(nw, nh),
So the practical consequence is this: if the original dimensions are dropped, the --ar collapses to 1:1 while every phrase measured from pixels stays the same. We ran it both ways on the same picture at the long setting:
| File size | With dimensions | Dimensions dropped | Slots that changed |
|---|---|---|---|
| 1913 × 977 | --ar 60:31 | --ar 1:1 | 1 of 10 (framing) |
| 1920 × 1080 | --ar 16:9 | --ar 1:1 | 1 of 10 (framing) |
| 1000 × 999 | --ar 60:60 | --ar 1:1 | 0 of 10 |
The framing phrase changes too, but only because it reads the same ratio that produced the parameter. The other nine slots — exposure, palette, saturation, temperature, contrast, detail, lighting distribution, vertical balance, mood — are identical to the character. In the third row, where the framing phrase was already “square 1:1”, the removal of the dimensions changed literally nothing but the last five characters of the line.
How the number is produced: greatest common divisor, then a hard cap of 60
aspectParam() in src/palette.js reduces your width and height by their greatest common divisor, and only if either side still exceeds 60 does it scale both down so the larger one is exactly 60. The cap is there so an odd crop cannot emit something like 1913:977:
| File size | gcd | Reduced | Emitted | True | Error |
|---|---|---|---|---|---|
| 1920 × 1080 | 120 | 16:9 | 16:9 | 1.7778 | 0% |
| 640 × 360 | 40 | 16:9 | 16:9 | 1.7778 | 0% |
| 1024 × 1024 | 1024 | 1:1 | 1:1 | 1.0000 | 0% |
| 1080 × 1920 | 120 | 9:16 | 9:16 | 0.5625 | 0% |
| 3000 × 2000 | 1000 | 3:2 | 3:2 | 1.5000 | 0% |
| 1500 × 1001 | 1 | 1500:1001 | 60:40 | 1.4985 | +0.1% |
| 1258 × 566 | 2 | 629:283 | 60:27 | 2.2226 | −0.018% |
| 1258 × 565 | 1 | 1258:565 | 60:27 | 2.2265 | −0.194% |
| 1913 × 977 | 1 | 1913:977 | 60:31 | 1.9580 | −1.152% |
| 1000 × 999 | 1 | 1000:999 | 60:60 | 1.0010 | −0.1% |
Two things fall out of that table. First, any file whose width and height share no common divisor gets pinned to 60 on its long side — that is the “rounded rather than exact” trade-off, and it is worst at −1.15% for the 1913 × 977 case. Second, and more surprising: a near-square file never emits 1:1. Every one of 1000 × 999, 1001 × 1000, 999 × 998, 1500 × 1499, 1200 × 1199 and 1002 × 1000 emits 60:60; 1010 × 1000 emits 60:59. Only a file whose sides divide evenly — 1024 × 1024, say — produces the clean 1:1 you would expect.
The 60:31 in that table is the part that does not travel. Midjourney reads the parameter, so the number is the number; a model that takes its ratio from a fixed list of fourteen strings cannot read it at all, and the closest it can come is 9.21% away — see which twelve things do not line up between a generated prompt and a nano banana request.
We flagged this on the four-format comparison page as “check the value before you generate” without showing what it produces. These are those numbers.
The place where the line disagrees with itself
Because the framing phrase and the parameter come out of two different code paths, they do not always agree. The phrase is picked by threshold comparisons on a floating-point ratio in src/describe.js; the parameter is integer arithmetic in src/palette.js. Feed a file with awkward dimensions and one line of prompt can carry two different aspect claims:
| File size | Slot 1 | Tail | Agree? |
|---|---|---|---|
| 1920 × 1080 | widescreen 16:9 composition | --ar 16:9 | Yes |
| 1024 × 1024 | square 1:1 composition | --ar 1:1 | Yes |
| 1258 × 566 | ultra-wide panoramic composition | --ar 60:27 | Yes (no number in the phrase) |
| 1000 × 999 | square 1:1 composition | 60:60 | Same ratio, different writing |
| 1913 × 977 | widescreen 16:9 composition | --ar 60:31 | No — 1.778 vs 1.935 |
| 1080 × 1920 | portrait 2:3 composition | --ar 9:16 | No — 0.667 vs 0.563 |
The 1080 × 1920 row is the one that bites: a tall phone photo gets described as “portrait 2:3” while the parameter asks for 9:16. Since --ar is a machine instruction and the words in front of it are only a suggestion, the parameter is what Midjourney will honour. If you want the frame the phrase describes, edit the tail by hand — it is four characters of work.
What Midjourney gets that the other three do not
The same measurement, formatted four ways at the longest setting, from the 640 × 360 night photograph:
| Target | Chars | Commas | Full stops | Quality prefix | Trailing parameter |
|---|---|---|---|---|---|
| Midjourney | 319 | 14 | 0 | No | --ar 16:9 |
| Stable Diffusion | 336 | 16 | 0 | masterpiece, best quality, | None |
| Flux | 273 | 6 | 7 | No | None |
| DALL·E | 219 | 9 | 3 | No | None |
Midjourney is the only one of the four that carries a machine parameter at all. It is also the only shape with no full stop — Flux writes seven sentences, DALL·E writes three, Midjourney writes none. And it is the only shape that does not get the masterpiece, best quality, prefix, because those tags are a habit of Stable Diffusion checkpoints, not of Midjourney.
The two comma-list shapes turn out to be related by a fixed amount. The Stable Diffusion line is the Midjourney line with a 27-character quality prefix bolted on the front and the --ar tail taken off the end, so the difference is exactly 27 minus the length of the tail:
| File size | Short | Medium | Long | Tail length |
|---|---|---|---|---|
| 1920 × 1080 | +17 | +17 | +17 | 10 |
| 1080 × 1920 | +17 | +17 | +17 | 10 |
| 1913 × 977 | +16 | +16 | +16 | 11 |
| 1000 × 999 | +16 | +16 | +16 | 11 |
Nothing about the picture changes that number. It holds across all twelve combinations we ran.
If you run the line through a weight checker, the parameter becomes a weight
Any tool that splits a prompt on commas and reads term:number as an explicit weight will read the tail of a Midjourney line as a weight, because the colon is right there. We pushed the parameters through our own parser (tools/weight-syntax.js, usable band 0.4 to 1.6):
| Tail | Parsed as | Weight | Inside 0.4–1.6? |
|---|---|---|---|
--ar 16:9 | “… --ar 16” | 9 | No |
--ar 9:16 | “… --ar 9” | 16 | No |
--ar 3:2 | “… --ar 3” | 2 | No |
--ar 2:3 | “… --ar 2” | 3 | No |
--ar 60:27 | “… --ar 60” | 27 | No |
--ar 60:31 | “… --ar 60” | 31 | No |
--ar 60:60 | “… --ar 60” | 60 | No |
--ar 1:1 | “… --ar 1” | 1 | Yes |
Every one of the nine real Midjourney outputs we generated (three images × three length settings) reports exactly one out-of-range term, and it is always the tail. Strip everything from --ar onwards and all nine report zero out-of-range terms — every remaining phrase carries weight 1. That is the whole trick: check the weights on the comma list, not on the finished line.
What the Midjourney line never contains
- No subject noun. The one place the describer assigns a subject is a monochrome branch; all three test images measured a monochrome share far below that line, so the slot was empty in all nine outputs. The noun is always yours to write, and it belongs at the front.
- No weights. Nothing in the line is emphasised or de-emphasised. Once the tail is removed, every term parses to exactly 1.
- No negative. There is no negative-prompt field anywhere in the four output shapes. If you need one, you write it in Midjourney’s own
--noparameter yourself. - No full stop. Zero periods in all nine outputs, against seven for Flux and three for DALL·E on the same picture.
- Nothing from three of the measured fields. Mean green, the green-dominant share and the blue-dominant share are all computed every run, and none of them can reach the prompt: mean green is read nowhere outside the analyser, and the two hue shares only set a hint that the formatter never reads.
How to work with the line
- Write the noun first, every time. The generated line describes lighting, colour, contrast and shape. It does not know what is in the frame, so the first thing you type is the thing you want in it.
- Fix the tail when it disagrees with slot 1. If the framing phrase and the
--ardo not match, the parameter is the one that takes effect. Change it to the ratio you actually want. - Leave the measured phrases alone unless you can name the number that produced them. They are arithmetic on your file; replacing them replaces evidence with a guess.
- Add Midjourney’s own parameters after the tail. The tool emits exactly one parameter,
--ar. Version, style, chaos, stylize and--noare yours to append. - Crop and re-run rather than editing by hand when a phrase is wrong for your picture — the measurement follows the crop, and the whole line moves with it.
For how each of those ten slots is chosen, see what an image to prompt tool measures; for the same four shapes side by side with a downloadable table, see getting a usable AI image prompt from a picture; for the order to work in when a phrase is wrong, see fixing a wrong image to prompt output. For the pictures where this approach breaks down entirely, see which images it handles badly.