visualtoprompt.com

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 row of ten squares, the first nine pale grey and the last one blue.
A row of ten squares, the first nine pale grey and the last one blue.

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 order for the Midjourney shape. The palette line is inserted with splice(2, 0, ...), which pins it to the third position at every length.
#SlotChosen by
1Framing + “composition”Aspect ratio against the thresholds 2.2 / 1.7 / 1.4 / 1.15 / 0.9 / 0.7 / 0.55
2ExposureMean luminance against 175 / 130 / 80
3Dominant paletteUp to four nearest named colours, from 12-bit colour buckets merged at distance 52
4SaturationMonochrome pixel share against 0.85, else mean saturation against 0.45 / 0.22
5Colour temperatureMean red minus mean blue, against 14 / 5 / −5 / −14
6ContrastLuminance standard deviation against 68 / 38
7DetailMean gradient magnitude against 15 / 7
8Lighting distributionCentre mean vs border mean, at a 1.18× ratio
9Vertical balanceTop mean vs bottom mean, at a 1.2× ratio (only present when the ratio is crossed)
10MoodLuminance 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:

Same picture, same 192 × 192 buffer. Only the natural dimensions were removed.
File sizeWith dimensionsDimensions droppedSlots that changed
1913 × 977--ar 60:31--ar 1:11 of 10 (framing)
1920 × 1080--ar 16:9--ar 1:11 of 10 (framing)
1000 × 999--ar 60:60--ar 1:10 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:

aspectParam(w, h, cap = 60) on real and awkward sizes. “Error” is how far the emitted ratio sits from the file’s true ratio.
File sizegcdReducedEmittedTrueError
1920 × 108012016:916:91.77780%
640 × 3604016:916:91.77780%
1024 × 102410241:11:11.00000%
1080 × 19201209:169:160.56250%
3000 × 200010003:23:21.50000%
1500 × 100111500:100160:401.4985+0.1%
1258 × 5662629:28360:272.2226−0.018%
1258 × 56511258:56560:272.2265−0.194%
1913 × 97711913:97760:311.9580−1.152%
1000 × 99911000:99960:601.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:

The first slot and the tail of the same generated Midjourney line, for six file sizes.
File sizeSlot 1TailAgree?
1920 × 1080widescreen 16:9 composition--ar 16:9Yes
1024 × 1024square 1:1 composition--ar 1:1Yes
1258 × 566ultra-wide panoramic composition--ar 60:27Yes (no number in the phrase)
1000 × 999square 1:1 composition60:60Same ratio, different writing
1913 × 977widescreen 16:9 composition--ar 60:31No — 1.778 vs 1.935
1080 × 1920portrait 2:3 composition--ar 9:16No — 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:

One picture, four target formats, long setting. All four were produced from the same 192 × 192 measurement.
TargetCharsCommasFull stopsQuality prefixTrailing parameter
Midjourney319140No--ar 16:9
Stable Diffusion336160masterpiece, best quality,None
Flux27367NoNone
DALL·E21993NoNone

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:

Stable Diffusion length minus Midjourney length, same picture, same length setting. The tail  --ar 16:9 is 10 characters;  --ar 60:31 is 11.
File sizeShortMediumLongTail length
1920 × 1080+17+17+1710
1080 × 1920+17+17+1710
1913 × 977+16+16+1611
1000 × 999+16+16+1611

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):

What a comma-and-colon parser reads out of … --ar X. Only 1:1 lands inside the band.
TailParsed asWeightInside 0.4–1.6?
--ar 16:9“… --ar 16”9No
--ar 9:16“… --ar 9”16No
--ar 3:2“… --ar 3”2No
--ar 2:3“… --ar 2”3No
--ar 60:27“… --ar 60”27No
--ar 60:31“… --ar 60”31No
--ar 60:60“… --ar 60”60No
--ar 1:1“… --ar 1”1Yes

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

How to work with the line

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.