Skip to main content
Lesson 5 of 12 · Image Generation via API: GPT Image

Variations and Iterations

Lesson 5 of 12 Updated November 2, 2026 Developer
On this page
  1. Variations and Iterations
  2. Generating Variations of the Same Concept
  3. Variations by Modifying the Prompt
  4. Iteration by Refining the Prompt
  5. Automated Iteration Pipeline
  6. Comparing Variations in a Catalog
  7. The Discipline of Iteration

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, and chatgpt-image-latest will be retired on December 1, 2026. Examples now use gpt-image-2.5-sunburst, which offers the highest precision for editing; gpt-image-2.5-flare is faster for standard generation. Both models also support xhigh and max quality settings. GPT Image models always return images in base64: the response_format parameter 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.