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

Advanced visual prompts: composition, lighting, perspective

Lesson 9 of 12 Updated November 2, 2026 Developer
On this page
  1. Advanced visual prompts: composition, lighting, perspective
  2. Anatomy of a professional visual prompt
  3. Mastering composition
  4. Mastering lighting
  5. Mastering perspective
  6. A reusable prompt builder

Update November 2, 2026 — image models. gpt-image-1, which this course was originally written for, was discontinued on October 23, 2026, and 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, the most precise model for editing; gpt-image-2.5-flare is faster for standard generation. Both 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 not accepted and has been removed from the code. Source: Image generation (OpenAI).

Advanced visual prompts: composition, lighting, perspective

A well-structured prompt is what separates a decent image from a professional-quality one. The lever isn’t length—it’s vocabulary. GPT Image understands the terminology of photography, cinema, and visual arts. Speaking that language is like giving directions to a director of photography rather than making a wish. This lesson teaches you that vocabulary and how to organize it.

Anatomy of a professional visual prompt

An advanced prompt breaks down into layers, each answering a distinct question: who or what, arranged how, lit how, viewed from where, in what register, with what optical characteristics. Naming these layers explicitly in the prompt text—“Subject: … Composition: … Lighting: …”—helps the model avoid mixing registers. The example below describes an architect in their studio: the subject occupies the left third according to the rule of thirds, light comes laterally from large windows, the camera is at a subtle low angle with a 35mm focal length, the register is warm desaturated editorial documentary photography, and the depth of field leaves the background in light bokeh. Each of these six sentences removes a freedom the model would otherwise exercise at random.

from openai import OpenAI
import base64

client = OpenAI()

def prompt_multicouche(
    sujet: str,
    composition: str,
    eclairage: str,
    perspective: str,
    style: str,
    details_techniques: str,
    output: str
):
    """Builds a multi-layer prompt for maximum control."""
    prompt = (
        f"Subject: {sujet}. "
        f"Composition: {composition}. "
        f"Lighting: {eclairage}. "
        f"Perspective: {perspective}. "
        f"Style: {style}. "
        f"Technical details: {details_techniques}."
    )

    response = client.images.generate(
        model="gpt-image-2.5-sunburst",
        prompt=prompt,
        size="1536x1024",
        quality="high",
    )

    data = base64.b64decode(response.data[0].b64_json)
    with open(output, "wb") as f:
        f.write(data)
    print(f"Advanced image: {output}")
    return prompt


prompt_utilise = prompt_multicouche(
    sujet="Architect working on blueprints in a bright studio",
    composition="Rule of thirds, subject at left third, depth with multiple planes",
    eclairage="Lateral natural light from large windows, soft shadows",
    perspective="Slightly low angle (subtle low-angle shot), 35mm focal length",
    style="Editorial documentary photography, warm desaturated tones",
    details_techniques="Medium depth of field, sharp foreground, light background bokeh",
    output="architecte_atelier.png"
)

Mastering composition

Composition determines how the eye travels through an image, and you control it by naming rules the model recognizes.

Classic composition rules

The dictionary below gathers five major compositional families and applies them to an intentionally identical subject—an isolated lighthouse on a Breton cliff—so the comparison focuses solely on staging. The rule of thirds places the lighthouse at an intersection of power lines and produces the most “natural” image; Wes Anderson symmetry centers the vertical axis and adds a reflection in the water; the diagonal creates tension from the bottom-left corner to the top-right corner; frame within a frame makes you view the lighthouse through an arch or window, creating immediate depth; finally, minimalist composition reduces the lighthouse to a tiny dot in a vast negative space, turning the subject into a pretext. Five images of the same lighthouse, five different statements.

from openai import OpenAI
import base64

client = OpenAI()

compositions = {
    "regle_des_tiers": (
        "Rule of thirds composition, subject positioned at the intersection "
        "of power lines (right third, bottom third)"
    ),
    "symetrie": (
        "Perfectly symmetrical composition, central vertical axis, "
        "reflections in water, Wes Anderson style"
    ),
    "diagonale": (
        "Dynamic diagonal composition, power line from "
        "bottom-left corner to top-right corner"
    ),
    "cadre_dans_cadre": (
        "Frame within a frame: subject viewed through an arch, "
        "a window, or a natural framing"
    ),
    "minimaliste": (
        "Minimalist composition, huge negative space, "
        "small isolated subject in a vast environment"
    ),
}

sujet_base = "An isolated lighthouse on a Breton cliff facing the sea"

for nom, composition in compositions.items():
    prompt = f"{sujet_base}, {composition}, landscape photography"

    response = client.images.generate(
        model="gpt-image-2.5-sunburst",
        prompt=prompt,
        size="1536x1024",
        quality="high",
    )

    path = f"composition_{nom}.png"
    data = base64.b64decode(response.data[0].b64_json)
    with open(path, "wb") as f:
        f.write(data)
    print(f"Composition {nom} → {path}")

Planes and depth

Depth isn’t requested in bulk; it’s built plane by plane. The following prompt describes a Provençal market by explicitly assigning content and sharpness to each of the three planes: soaps and lavender in the foreground, perfectly sharp; the vendor and their customer in the middle ground, slightly blurred; golden stone facades and plane trees in the background, with pronounced bokeh. The final mention of a 50mm lens opened to f/1.8 only optically confirms what the description has already established—and it’s this intentional redundancy that makes the result reliable.

from openai import OpenAI
import base64

client = OpenAI()

prompt_profondeur = (
    "Provençal market scene with three distinct planes: "
    "sharp foreground—display of lavender and artisanal soaps, "
    "middle ground—vendor and customer in conversation, slight blur, "
    "background—golden stone facades and plane trees, pronounced bokeh. "
    "Photography with 50mm f/1.8 lens, shallow depth of field."
)

response = client.images.generate(
    model="gpt-image-2.5-sunburst",
    prompt=prompt_profondeur,
    size="1536x1024",
    quality="high",
)

data = base64.b64decode(response.data[0].b64_json)
with open("marche_profondeur.png", "wb") as f:
    f.write(data)

Mastering lighting

Lighting is the most powerful tool for creating atmosphere, and the most cost-effective in terms of characters invested. Six setups cover most needs, and the loop below applies them all to the same portrait of a focused violinist. Rembrandt lighting, with its single source at 45 degrees and its triangular highlight on the opposite cheek, gives the classic studio portrait. Rim light places the source behind the subject and keeps only the contour halo, producing a silhouette image. Split lighting cuts the face in two halves, one lit, the other in shadow, immediately shifting to a cinematic register. Golden hour brings raking golden light and its nostalgic lens flare, urban neon brings pink and cyan sources reflected on wet surfaces, and high-key studio brings shadowless uniformity and a commercial white background. The same subject becomes intimate, mysterious, or advertising-ready in turn.

from openai import OpenAI
import base64

client = OpenAI()

eclairages_avances = {
    "rembrandt": (
        "Rembrandt lighting: single source at 45 degrees, triangular highlight "
        "on opposite cheek, deep dramatic shadows"
    ),
    "rim_light": (
        "Backlighting (rim light): silhouette outlined by a luminous halo, "
        "source directly behind subject, details in shadows"
    ),
    "split": (
        "Split lighting: half face lit, half in shadow, "
        "clear separation, mysterious cinematic atmosphere"
    ),
    "golden_hour": (
        "Golden hour: raking golden light, long warm shadows, "
        "subtle lens flare, soft nostalgic atmosphere"
    ),
    "neon": (
        "Urban neon lighting: multiple pink and cyan sources, "
        "reflections on wet surfaces, nocturnal cyberpunk atmosphere"
    ),
    "studio_high_key": (
        "High-key studio: uniformly bright lighting, minimal shadows, "
        "white background, clean commercial atmosphere"
    ),
}

sujet = "Portrait of a focused violinist"

for nom, eclairage in eclairages_avances.items():
    prompt = f"{sujet}, {eclairage}"

    response = client.images.generate(
        model="gpt-image-2.5-sunburst",
        prompt=prompt,
        size="1024x1536",
        quality="high",
    )

    path = f"eclairage_{nom}.png"
    data = base64.b64decode(response.data[0].b64_json)
    with open(path, "wb") as f:
        f.write(data)
    print(f"Lighting {nom} → {path}")

Mastering perspective

Perspective influences how the viewer perceives the subject, and its effect is almost always psychological before it’s aesthetic. A high angle looks down on the scene and diminishes what it views; a low angle, camera at ground level, enlarges the subject and gives it power; a perfectly vertical aerial view abolishes volume and turns reality into a graphic pattern; a worm’s-eye view pushes the low angle to the extreme, making all vanishing lines converge toward the sky; finally, an eye-level view with a 50mm focal length reproduces human vision and disappears in favor of the subject. On glass-and-steel skyscrapers, the difference between these five images lies not in the building but in the relationship they establish with it. Note that the loop switches to portrait format for the two low angles, because a building low-angle shot needs height to breathe.

from openai import OpenAI
import base64

client = OpenAI()

perspectives = {
    "plongee": (
        "High-angle view (top-down), camera overlooking the scene, "
        "domination effect, subject viewed from above at 45 degrees"
    ),
    "contre_plongee": (
        "Low-angle view (bottom-up), camera at ground level looking up, "
        "effect of grandeur and power"
    ),
    "aerienne": (
        "Drone aerial view, perfectly vertical (90 degrees), "
        "graphic composition, textures and patterns visible from above"
    ),
    "worm_eye": (
        "Worm's-eye view, camera placed on ground, extreme perspective "
        "with vanishing lines converging toward the sky"
    ),
    "eye_level": (
        "Eye-level view, natural and immersive, "
        "50mm focal length, like human vision"
    ),
}

sujet = "Modern glass and steel skyscrapers"

for nom, perspective in perspectives.items():
    prompt = f"{sujet}, {perspective}, architectural photography"

    response = client.images.generate(
        model="gpt-image-2.5-sunburst",
        prompt=prompt,
        size="1024x1536" if nom in ["contre_plongee", "worm_eye"] else "1536x1024",
        quality="high",
    )

    path = f"perspective_{nom}.png"
    data = base64.b64decode(response.data[0].b64_json)
    with open(path, "wb") as f:
        f.write(data)
    print(f"Perspective {nom} → {path}")

A reusable prompt builder

Concatenating seven layers by hand in every script will sooner or later make you forget one or swap two. The PromptBuilder class solves this by exposing one method per layer and returning self at each call, allowing you to chain settings in a readable order and specify only some of them. The build() method then assembles the subject and populated layers into text ready to pass to client.images.generate(). The seaside café example implements all seven available layers—composition, lighting, perspective, style, colors, atmosphere, technique—and produces a prompt no improvisation would make this complete.

class PromptBuilder:
    """Builder for structured visual prompts."""

    def __init__(self, sujet: str):
        self.sujet = sujet
        self.layers = {}

    def composition(self, desc: str):
        self.layers["composition"] = desc
        return self

    def eclairage(self, desc: str):
        self.layers["lighting"] = desc
        return self

    def perspective(self, desc: str):
        self.layers["perspective"] = desc
        return self

    def style(self, desc: str):
        self.layers["style"] = desc
        return self

    def couleurs(self, desc: str):
        self.layers["color palette"] = desc
        return self

    def atmosphere(self, desc: str):
        self.layers["atmosphere"] = desc
        return self

    def technique(self, desc: str):
        self.layers["technique"] = desc
        return self

    def build(self) -> str:
        parts = [self.sujet]
        for key, value in self.layers.items():
            parts.append(f"{key}: {value}")
        return ". ".join(parts)


# Usage
prompt = (
    PromptBuilder("Artisanal seaside café, terrace with umbrellas")
    .composition("Rule of thirds, terrace in foreground, sea at horizon")
    .eclairage("Late afternoon, raking golden light, long shadows")
    .perspective("Slight high angle, 28mm wide-angle focal length")
    .style("Lifestyle photography, warm tones, analog film")
    .couleurs("Terracotta, Mediterranean blue, off-white, olive green")
    .atmosphere("Relaxed and Mediterranean, sea breeze")
    .technique("Deep depth of field, sharp from foreground to infinity")
    .build()
)

print(prompt)
# Use this prompt with client.images.generate()

Now take ownership of this toolkit on a single demanding subject: an old library. Produce five images with the PromptBuilder, systematically varying composition, lighting, and perspective, then gather them in an HTML catalog. Seeing the five side by side is how you’ll learn which of these layers matter most in your practice.