Image to Prompt Stability: Same Bytes, Different Exports
Two answers, and the difference is what you mean by "the same picture". Hand the tool the identical bytes and it does not wobble: forty files × twelve lines × nineteen repeat runs is 9,120 line comparisons, and 0 of them came back different; the eight measurements this page tracks were bit-identical on all 760 repeat comparisons. Re-export that picture and the guarantee is gone. At six bits per channel, 214 of 480 lines change. At 80 pixels wide, 338 do. Adding or subtracting one from every colour channel of every pixel — a change no eye can see — moves 80. The tool is deterministic; the file you hand it is not, and that is where every difference on this page comes from.
What these numbers are. Forty real files — 39 photographs at 640 × 360 and one code screenshot at 800 × 450 — decoded on this machine, box-downsampled to the 192 × 192 grid the live tool measures on, and composed into four shapes at three lengths: 480 lines. A “difference” is one character or more between two composed strings. Every figure below comes from re-running that pipeline on rebuilt files, not from a description of it. No image generator was run and no image was generated — there is no image model behind this site and none was called — so all of this is about the words the tool writes about a file, never about what any model would draw.
Twenty runs on the same bytes
The first question is the narrow one: if nothing about the file changes, does anything about the output change? Each of the forty files was put through the complete pipeline — decode, downsample, measure, classify, compose — twenty times over, and each run’s twelve lines were compared character-for-character against the first run’s.
| What was held fixed, and what was repeated | Comparisons | Different |
|---|---|---|
| Identical file bytes, full pipeline, 20 runs — all 12 lines compared | 9,120 | 0 |
| The eight measurements behind the words, 20 runs, compared bit-for-bit | 760 | 0 |
| The palette array, 20 runs, compared as a whole array | 760 | 0 |
| The batch of 40 files processed in three orders (original, reversed, shuffled) | 960 | 0 |
Two things are worth pulling out of that. The first is that “0” is not a rounding artefact: the eight measurements were compared as exact JSON strings, so a difference in the tenth decimal place would have counted, and none appeared. The second is that the one thing that did move is time — a single file through the whole pipeline took a median of 19.10 ms across 800 runs, ranging from 14.01 to 40.13 ms, which is scheduling noise on this machine and nothing to do with the output.
The only place a deterministic pipeline could still surprise you is a tie: the palette is sorted by share, and two colours with exactly equal shares would be ordered by whatever the sort does with them. In these forty files that never happens — 0 of 40 have two palette entries with the same share — so the ordering is decided by the numbers every time. Processing order does not matter either: the same forty files were run in their original order, reversed, and in a fixed shuffled order, and 0 of 960 lines moved. There is no state carried from one file to the next.
Eleven ways to write the same PNG
Now hold the pixels and change the container. Each file was decoded, re-encoded from its own pixels into eleven different PNGs — every one of the five scanline filter types, two deflate levels, a timestamped text chunk, colour-management chunks, the image data split across eight chunks, and an RGBA colour type with a full alpha channel — then decoded again and run through the whole pipeline.
| PNG variant | Median file size | Lines changed | Pixels match |
|---|---|---|---|
| filter 0 (none) | 374,836 | 0 / 480 | 40 / 40 |
| filter 1 (sub) | 274,227 | 0 / 480 | 40 / 40 |
| filter 2 (up) | 273,351 | 0 / 480 | 40 / 40 |
| filter 3 (average) | 275,876 | 0 / 480 | 40 / 40 |
| filter 4 (paeth) | 263,444 | 0 / 480 | 40 / 40 |
| deflate level 0 (stored) | 701,452 | 0 / 480 | 40 / 40 |
| deflate level 9 | 374,438 | 0 / 480 | 40 / 40 |
plus a tEXt export timestamp | 374,885 | 0 / 480 | 40 / 40 |
plus gAMA and sRGB chunks | 374,865 | 0 / 480 | 40 / 40 |
| image data split across 8 chunks | 374,920 | 0 / 480 | 40 / 40 |
| colour type 6 (RGBA) | 381,664 | 0 / 480 | 40 / 40 |
5,280 line comparisons, no difference in any of them, and all 440 re-encodes decoded back to the exact pixels they started from. The size column is there to show the container really did change: the same pixels stored uncompressed take a median of 701,452 bytes against 374,438 at the highest deflate level, a median gap of 314,347 bytes, and the timestamp chunk adds exactly 49 bytes. None of it reaches the words. Whatever your exporter writes into the file — when you exported it, what profile it tagged, how it packed the rows — is discarded before a single measurement is taken.
How far the colour can be cut before the words move
Container changes are invisible because the pixels survive them. The next question is where the words actually give: keep everything else and reduce the colour depth, one bit per channel at a time, eight bits down to one.
| Bits | Levels | Lines changed | Files affected | Max brightness drift |
|---|---|---|---|---|
| 8 | 256 | 0 / 480 | 0 / 40 | 0.000 |
| 7 | 128 | 57 / 480 | 6 / 40 | 0.648 |
| 6 | 64 | 214 / 480 | 20 / 40 | 1.886 |
| 5 | 32 | 276 / 480 | 26 / 40 | 4.820 |
| 4 | 16 | 407 / 480 | 37 / 40 | 11.876 |
| 3 | 8 | 458 / 480 | 39 / 40 | 25.095 |
| 2 | 4 | 480 / 480 | 40 / 40 | 40.968 |
| 1 | 2 | 480 / 480 | 40 / 40 | 89.402 |
Per file, the first depth at which any of its twelve lines moves is a median of 6 bits: sixteen files first move there, eight at five bits, seven at four, six at seven, and three at three. Nothing in this set survives below three bits. The mechanism is not exotic — quantising brightness by up to 89.402 grey levels pushes it across the cut points that decide the exposure and mood phrases, and quantising colour pushes saturation across the ones that decide vivid against moderately saturated. The words are thresholds, so a change only matters when it lands on one.
What that looks like on a single file, at the long Midjourney line. Six bits and four bits leave it untouched; two bits rewrites three separate clauses at once — a fourth colour appears, saturation flips from moderate to vivid, and the mood drops from uplifting to neutral:
baseline widescreen 16:9 composition, well-lit, balanced exposure, dominant palette of tan, brown, olive, moderately saturated colour, warm golden colour temperature, medium contrast, moderate detail and texture, evenly lit, brighter upper half, bright and uplifting atmosphere --ar 16:9 6 bit unchanged 4 bit unchanged 2 bit widescreen 16:9 composition, well-lit, balanced exposure, dominant palette of tan, charcoal, olive, brown, vivid, saturated colour, warm golden colour temperature, medium contrast, moderate detail and texture, evenly lit, brighter upper half, balanced, natural atmosphere --ar 16:9
How small the re-export can be
The other thing an exporter does is change the size. Each file was resampled down to a given width — height scaled to keep the ratio — and then measured on the same 192 × 192 grid, so the only variable is how many of the original pixels were left to average into each cell.
| Width | Lines changed | Files affected | Max edge drift | Ratio the tool writes |
|---|---|---|---|---|
| 640 px | 0 / 480 | 0 / 40 | 0.017 | 16:9 |
| 512 px | 176 / 480 | 16 / 40 | 0.775 | 16:9 |
| 320 px | 129 / 480 | 12 / 40 | 0.651 | 16:9 |
| 160 px | 188 / 480 | 18 / 40 | 9.750 | 16:9 |
| 80 px | 338 / 480 | 31 / 40 | 32.392 | 16:9 |
| 40 px | 459 / 480 | 40 / 40 | 54.160 | 40:23 |
| 20 px | 462 / 480 | 40 / 40 | 63.424 | 20:11 |
| 10 px | 480 / 480 | 40 / 40 | 64.688 | 5:3 |
Every one of the forty files first moves somewhere between 512 and 40 pixels wide, with a median of 240 px: sixteen first move at 512, eleven at 80, five at 40, four at 320 and four at 160. Note that the count is not monotone — 320 px changes fewer lines than 512 px does — because moving a value away from a threshold and then back across it is not the same as never crossing it. The measurement that drives most of this is edge density: at 80 px it has moved by up to 32.392, on a scale where the whole corpus runs from 3.71 to 65.84. Brightness barely budges at any width (at most 0.619), because averaging pixels preserves their average.
The last column is the one to watch. Down to 80 px the ratio the tool writes is still 16:9, but at 40 px every file in the set — 40 of 40 — starts reporting 40:23, then 20:11, then 5:3. Nothing about the picture changed shape; the height was rounded to a whole number of pixels, the ratio stopped reducing to 16:9, and the tool reported the numbers it was given. A thumbnail of a 16:9 photograph is not always 16:9, and the tool will tell you what the file says rather than what you meant.
The nudge that does nothing, and the one that does
So the words hold across a re-encode and give way on a re-export. The finest version of the question is: what is the smallest change to the pixels that still moves a line? Four perturbations, applied to the full-resolution pixels of all forty files before any measurement.
| Perturbation | Lines changed | Files affected | Max brightness drift |
|---|---|---|---|
| One pixel in the centre, red +1 | 0 / 480 | 0 / 40 | 0.0000058 |
| Every pixel, every colour channel ±1 (fixed pseudo-random) | 80 / 480 | 8 / 40 | 0.0354 |
| Every pixel, all three channels +1 | 144 / 480 | 12 / 40 | 1.0041 |
| Chroma subsampled 4:2:0 (2 × 2 blocks averaged) | 36 / 480 | 3 / 40 | 0.0242 |
The first row is the floor. One pixel out of 230,400 was brightened by one step and 0 of 480 lines moved; brightness shifted by 0.0000058 grey levels, and the largest move in any of the eight measurements tracked here was 0.0000271. That is what a single pixel is worth after the resize averages 6.25 of them into each cell and 36,864 cells are averaged again.
The second and third rows are the ones that matter in practice, and they are not the same size of change. Shuffling every channel of every pixel by one step either way — visually identical, and it moves brightness by at most 0.0354 — still changed 80 lines on 8 files. Pushing every channel up by one, which is exactly +1 grey level under the Rec.709 weights the tool uses for brightness and measured 1.0041, changed 144 lines on 12 files. Both are changes no one can see, and both are large enough to cross a threshold on a fifth to a third of the corpus — which is the honest statement about stability: the tool will always give you the same answer for the same bytes, and the set of pictures that produce your line is far narrower than you would guess from how coarse the vocabulary is.
Chroma subsampling is the gentlest of the four and the most instructive about which measurements are fragile. It moved 36 lines on 3 files, and it did so evenly: three files, twelve lines each, every shape and length affected identically. Saturation moved by at most 0.0062 and warmth by at most 0.128. Averaging colour in 2 × 2 blocks is what most photo formats do by default, and on this corpus it is the one re-export that usually leaves the words alone.
What the 9,120 repeat runs and 1,240 rebuilt files were run on
Forty files, twelve lines each: 480 baselines. The repeat test re-ran the full pipeline twenty times per file (800 runs, 9,120 line comparisons, 760 measurement comparisons, 760 palette comparisons) and once more in two alternative batch orders (960 comparisons). The container test rebuilt each file eleven ways from its own pixels: 440 re-encodes, 5,280 line comparisons, and a pixel-by-pixel check that all 440 decoded to exactly what was encoded. The re-export tests rebuilt each file at eight colour depths, eight widths, one chroma subsampling and three noise patterns: 800 rebuilt files with altered pixels and 9,600 further line comparisons. “Changed” means a difference of one character or more against that file’s own baseline; no comparison is against another file.
Three limits belong next to these numbers. The re-exports are simulations of what an exporter does — quantisation, resampling, chroma averaging — not real JPEG or WebP files, so they establish how much a change of that kind has to be worth before the words move, not what any specific program produces. The corpus is forty stock photographs plus one screenshot, all 16:9, so the ratio column is a statement about files that start at 16:9 and the depth thresholds are the ones this set lands near; a set of near-duplicate images would move sooner. And no generator was run: nothing here says what any image model would draw from these lines, only which lines this tool writes about which files.
What survives all three caveats is the split the numbers show. Determinism is a property of the file, and it is absolute: same bytes, same twelve lines, every time, whatever the container says, whatever order the batch ran in. Robustness is a property of the picture, and it is thin: six bits of colour, a fifth of the original width, or one grey level added everywhere is enough to rewrite part of the line. If you are checking a tool, run the same file twice — that is the check on how to test an image to prompt site. If you are keeping a line, keep the file it came from, because that is the only thing the words are actually a statement about.
For the twelve measurements the words are read off, see what an image to prompt tool measures; for the resize that turns your pixels into the 36,864 cells every measurement is taken on, where your image goes; for the cut points each phrase flips at, the twenty-nine cut points; and for how far a picture can be pushed while every line stays identical, how far the round trip gets you.