Variations and Iterations
On this page
Updated November 2, 2026 — Image models.
gpt-image-1, the model this course was originally written for, was discontinued on October 23, 2026. Additionally,gpt-image-1.5,gpt-image-1-mini, andchatgpt-image-latestwill be retired on December 1, 2026. Examples now usegpt-image-2.5-sunburst, which offers the highest precision for editing;gpt-image-2.5-flareis faster for standard generation. Both models also supportxhighandmaxquality settings. GPT Image models always return images in base64: theresponse_formatparameter from older DALL·E models is no longer accepted and has been removed from the code. Source: Image generation (OpenAI).
Variations and Iterations
Getting the perfect image on the first try is rare. Real work involves generating variations, comparing them, and then progressively tightening the prompt until you reach the desired result. This lesson gives you the three tools for this method: variation by re-running, variation by modifying the prompt, and iteration through successive refinement.
Generating Variations of the Same Concept
The simplest method is to re-run the generation with the same prompt: GPT Image produces a different result every time. This is useful at the start of exploration, when you don’t yet know what you’re looking for and want to see the range of possible interpretations of an idea.
from openai import OpenAI
import base64
client = OpenAI()
def generate_variations(prompt: str, n_variations: int, prefix: str):
"""Generate several independent variations of the same prompt."""
paths = []
for i in range(n_variations):
response = client.images.generate(
model="gpt-image-2.5-sunburst",
prompt=prompt,
size="1024x1024",
quality="medium",
)
path = f"{prefix}_v{i+1}.png"
data = base64.b64decode(response.data[0].b64_json)
with open(path, "wb") as f:
f.write(data)
paths.append(path)
print(f"Variation {i+1} saved: {path}")
return paths
variations = generate_variations(
prompt="Illustration of a gardener robot watering flowers, childish and colorful style",
n_variations=5,
prefix="gardener_robot"
)
Variations by Modifying the Prompt
The previous approach leaves you with randomness; this one gives you control. You fix an invariant subject—here, a steaming coffee cup on a wooden table—and move only one dimension: the mood. The five results then become comparable, since you know exactly what distinguishes them. This is the difference between five attempts and an experiment.
from openai import OpenAI
import base64
client = OpenAI()
base_prompt = "Steaming coffee cup on a wooden table"
moods = [
"soft morning light, warm tones",
"Parisian café vibe, black and white",
"pop art style, saturated colors",
"Scandinavian minimalist, white background",
"cozy winter atmosphere, bokeh string lights",
]
for i, mood in enumerate(moods):
prompt = f"{base_prompt}, {mood}"
response = client.images.generate(
model="gpt-image-2.5-sunburst",
prompt=prompt,
size="1024x1024",
quality="medium",
)
path = f"cafe_mood_{i+1}.png"
data = base64.b64decode(response.data[0].b64_json)
with open(path, "wb") as f:
f.write(data)
print(f"Mood: {mood[:30]}... → {path}")
Iteration by Refining the Prompt
When the result isn’t satisfactory, the right reaction isn’t to re-run and hope for better luck, but to add the missing constraint. The series below shows this progression in a worked example: “A logo for a fintech startup” tells the model nothing; the second iteration bounds the number of colors and the background; the third names the visual metaphor and palette; the fourth goes down to hex codes and stroke width. With each pass, the space of possible results narrows and reproducibility increases.
from openai import OpenAI
import base64
client = OpenAI()
iterations = [
# Iteration 1: base prompt
"A logo for a fintech startup",
# Iteration 2: adding visual constraints
"A logo for a fintech startup, minimalist design, maximum two colors, transparent background",
# Iteration 3: style precision
(
"Minimalist logo for a fintech startup, abstract geometric icon "
"evoking growth, navy blue and emerald green palette, no text, vector style"
),
# Iteration 4: fine adjustments
(
"Minimalist logo for a fintech startup, abstract geometric icon: "
"two interlocking arrows forming a diamond, navy blue (#1B2A4A) and "
"emerald green (#2ECC71), 2px thin stroke, white background, sharp vector render, centered"
),
]
for i, prompt in enumerate(iterations):
response = client.images.generate(
model="gpt-image-2.5-sunburst",
prompt=prompt,
size="1024x1024",
quality="high",
)
path = f"logo_iteration_{i+1}.png"
data = base64.b64decode(response.data[0].b64_json)
with open(path, "wb") as f:
f.write(data)
print(f"Iteration {i+1}: {prompt[:60]}...")
print(f" → {path}\n")
Automated Iteration Pipeline
On a slightly longer project, human memory isn’t enough: after fifteen images, you no longer know which prompt produced which result. The following pipeline structures the work into rounds, generates several variants per round, names files traceably, and keeps the complete history of prompts in a JSON file. This history file is what makes the setup valuable: it lets you go back and capitalize from one project to the next.
from openai import OpenAI
import base64
import json
client = OpenAI()
def iteration_pipeline(brief: str, n_rounds: int = 3, n_per_round: int = 3):
"""
Iteration pipeline: generates several variants per round,
keeps a history of prompts and results.
"""
history = []
current_prompt = brief
for round_num in range(1, n_rounds + 1):
print(f"\n{'='*50}")
print(f"ROUND {round_num} — Prompt: {current_prompt[:80]}...")
print(f"{'='*50}")
round_images = []
for i in range(n_per_round):
response = client.images.generate(
model="gpt-image-2.5-sunburst",
prompt=current_prompt,
size="1024x1024",
quality="medium",
)
path = f"pipeline_r{round_num}_v{i+1}.png"
data = base64.b64decode(response.data[0].b64_json)
with open(path, "wb") as f:
f.write(data)
round_images.append(path)
print(f" Variant {i+1}: {path}")
history.append({
"round": round_num,
"prompt": current_prompt,
"images": round_images
})
# Refine the prompt for the next round
if round_num < n_rounds:
current_prompt += ", more details, better composition"
# Save the history
with open("pipeline_history.json", "w") as f:
json.dump(history, f, indent=2, ensure_ascii=False)
print(f"\nHistory saved: pipeline_history.json")
return history
iteration_pipeline(
brief="Illustration of an independent bookstore seen from outside, autumn evening, Ghibli style",
n_rounds=3,
n_per_round=3
)
Comparing Variations in a Catalog
Opening nine PNGs one by one in a viewer doesn’t allow for any serious comparison. Instead, generate an HTML page that displays them side by side, with images encoded in base64 so the file is self-contained and shareable as-is with a client or art director.
import base64
from pathlib import Path
from glob import glob
def generate_catalog(images: list[str], title: str, output: str = "catalog.html"):
"""Create an HTML page with all images side by side."""
html_parts = [
'<!DOCTYPE html><html><head><meta charset="utf-8">',
f'<title>{title}</title>',
'<style>',
'body { background: #111; color: #fff; font-family: sans-serif; padding: 2rem; }',
'.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1rem; }',
'.card { background: #222; border-radius: 8px; overflow: hidden; }',
'.card img { width: 100%; display: block; }',
'.card p { padding: 0.5rem 1rem; font-size: 0.85rem; color: #aaa; }',
'</style></head><body>',
f'<h1>{title}</h1><div class="grid">',
]
for img_path in images:
with open(img_path, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
html_parts.append(f'<div class="card">')
html_parts.append(f'<img src="data:image/png;base64,{b64}" />')
html_parts.append(f'<p>{Path(img_path).name}</p></div>')
html_parts.append('</div></body></html>')
with open(output, "w") as f:
f.write("\n".join(html_parts))
print(f"Catalog generated: {output}")
images = sorted(glob("pipeline_r*_v*.png"))
generate_catalog(images, "Variations — Ghibli Bookstore")
The Discipline of Iteration
A few principles separate productive iteration from costly trial and error. Change only one parameter at a time, otherwise you’ll never know which one improved the result. Keep a history of your prompts and renders, as the pipeline above does, to capitalize instead of starting from scratch on every project. Work in quality: low during the entire exploration phase and switch to high only for the final render, or you’ll pay top dollar for images you’ll throw away. When you’re simply looking for quick diversity, prefer a single call with the n parameter over a loop of requests. Finally, name your files descriptively: it’s a trivial detail on day one, decisive after two hundred images.
Now apply this full sequence to a subject of your choice—logo, illustration, or product photo. Generate three variants with an initial prompt, refine that prompt in light of the results, generate three new variants, then produce the comparative HTML catalog of the six images.