# AI Image Generation

> **Scope:** nextjs
> **Layer:** 1 (load when relevant)
> **Keywords:** image, gemini, asset, ai-generation, placeholder, mcp, next/image
> **Load When:** feature has UI components requiring image assets

**Verified against:** Gemini Image MCP + Next.js 15 `next/image`. Last-verified: 2026-05-20.

---

## When to Use AI-Generated Images

| Use Case | Approach | Examples |
|----------|----------|---------|
| **AI generation** | Gemini Image MCP | Hero images, brand illustrations, feature-specific visuals, empty states, background panels |
| **Stock photos** | External sources | Generic content (people in offices, nature scenes, stock portraits) |
| **SVG/CSS** | Code-based | Icons, patterns, gradients, geometric shapes, loading animations |
| **Skip** | Other tools | Screenshots (Playwright), diagrams (Mermaid), logos (vector tools) |

**Decision rule:** If the image is unique to the project's brand/aesthetic and doesn't exist as stock, use AI generation. If it's generic and widely available, use stock. If it's geometric or icon-based, use SVG/CSS.

## Brand Fidelity: Reference, Don't Invent

The advantage of an image MCP over generating from scratch is staying consistent with the client's **real** assets. Pass them as reference (`edit_image` with `referenceImages`) rather than generating in a vacuum. A generated look-alike is a different product, and the client sees it immediately.

Two cases where this is a hard rule:

- **Property developers and architecture:** the building is the client's **real architectural render**. Never AI-recreate it. AI only animates it (see `frontend/design-system/ai-video-generation.md`) or composes a scene around the real render.
- **Physical products:** pass the real product photo as reference and generate angle or setting variations of the **same** product.

### Chain from an approved hero

Generate the **hero first**. Once approved, use it as the reference for every derived image (detail shots, cards, scene variants), so the whole set shares light, palette and identity instead of looking like separate photo shoots.

```
hero -> [approved] -> derivatives with the approved hero as referenceImages -> [approved per image]
```

Skipping this chain is the most common cause of a page whose images do not feel like one set.

## Gemini Image MCP API

```javascript
// Generate a new image
mcp__gemini_image_mcp__generate_image({
  prompt: string,           // Text description (max 5000 chars, narrative style)
  aspectRatio?: "1:1" | "2:3" | "3:2" | "3:4" | "4:3" | "9:16" | "16:9",
  outputPath?: string,      // File path to save (e.g., "public/images/feature/hero.png")
  model?: string,           // Default: gemini-2.5-flash-image
})

// Edit an existing image
mcp__gemini_image_mcp__edit_image({
  imageSource: string,      // Path to image or image ID
  prompt: string,           // Short, targeted edit instructions (1-2 sentences)
  outputPath?: string,
})

// Continue editing last image
mcp__gemini_image_mcp__continue_editing({
  prompt: string,           // One adjustment at a time
})

// List generated images
mcp__gemini_image_mcp__list_images({
  type?: "generated" | "edited" | "all",
  limit?: number,
})

// Get image details
mcp__gemini_image_mcp__get_image_info({
  imageId?: string,         // Defaults to last image
})
```

## Prompt Engineering Patterns

### Design-System-Aware Prompts

Read `design-system.md` or `globals.css` to extract project tokens. Inject into prompts:

- **Background color** → use as image background
- **Primary/accent colors** → use as highlight colors
- **Font pair** → mention for text elements
- **Aesthetic direction** → "Modern Luxury", "Kinetic Atelier", etc.

```
"{subject description}. Dark charcoal background (#10131a) with copper (#D2802D)
and gold (#FFB779) accent colors. {style modifiers}. Premium luxury aesthetic.
{composition rules}."
```

### Component-Contextual Prompts

Describe what the image will BE in the UI, not just the subject:

- **Hero image** → "floating tablet showing app dashboard on dark background" (not just "fitness dashboard")
- **Auth background** → "gym equipment with moody lighting" (matches brand feel)
- **Empty state** → "artistic wireframe body silhouette" (matches feature context)

### Narrative Style

Gemini responds best to coherent paragraphs. Build prompts by layering:

1. **Subject** — hyper-specific description
2. **Environment** — foreground, mid-ground, background (layered for depth)
3. **Lighting** — multiple sources with different colors/directions
4. **Style** — photography terms ("85mm portrait lens") or art style ("cel-shaded")
5. **Color palette** — dominant colors and contrast relationships
6. **Composition** — camera angle, framing, negative space

**Never:** keyword spam ("beautiful, stunning, 4k, masterpiece"), JSON prompts, negative prompts ("no cars").

## Iterative Editing Workflows

**Cardinal rule: generation demands depth; editing demands restraint.**

### Adding / Removing Elements
One sentence. Don't describe how to integrate — the model handles that.
```
"Add a knitted wizard hat to the cat."
```

### Background Replacement
State the new background simply. Don't over-describe.
```
"Replace the background with a tropical beach at sunset."
```

### Inpainting (Targeted Changes)
Name what changes and what stays in one line.
```
"Change the blue sofa to a brown leather chesterfield. Keep everything else."
```

### Style Transfer
Name the target style.
```
"Transform this into Van Gogh's Starry Night style."
```

### Iterative Refinement with `continue_editing`
One change at a time:
- "Make the lighting warmer"
- "More contrast in the shadows"
- "Zoom out a bit"
- "More serious expression"

### Text Rendering
The model frequently misspells words. After generating any image with text:
1. View the result
2. If text is wrong: `continue_editing` with "Fix the text — it should read '{correct text}' exactly. Keep everything else."
3. Expect 2-3 iterations for correct text

## Aspect Ratio Guide

| Use Case | Ratio | Examples |
|----------|-------|---------|
| Hero / banner | `16:9` | Landing page hero, YouTube thumbnail, desktop wallpaper |
| Card / blog header | `3:2` | Feature cards, blog post headers, landscape photo |
| Avatar / icon | `1:1` | Profile pictures, social media posts, square icons |
| Auth panel / sidebar | `3:4` | Login brand image, Pinterest-style, portrait poster |
| Portrait | `2:3` | Book cover, tall illustration, portrait photo |
| Story / reel | `9:16` | Mobile wallpaper, social story, vertical banner |
| Standard display | `4:3` | Documentation, slides, standard display |

## File Naming & Directory

**Convention:** `public/images/{feature}/{purpose}.png`

- Lowercase, kebab-case, descriptive names
- Group by feature, not by type
- Examples:
  - `public/images/ui-redesign/hero-mockup.png`
  - `public/images/ui-redesign/gym-equipment.png`
  - `public/images/ui-redesign/body-analysis.png`

## Next.js Image Integration

```tsx
import Image from 'next/image';

// Above-the-fold hero image (priority for LCP)
<Image
  src="/images/{feature}/hero-mockup.png"
  alt="AI fitness analysis dashboard"
  width={1440}
  height={810}
  priority
  sizes="100vw"
/>

// Card image with responsive sizing
<Image
  src="/images/{feature}/card-image.png"
  alt="Feature card illustration"
  width={600}
  height={400}
  sizes="(max-width: 768px) 100vw, 50vw"
  className="rounded-lg"
/>

// Background image with fill
<div className="relative h-full">
  <Image
    src="/images/{feature}/auth-background.png"
    alt=""
    fill
    className="object-cover"
    priority={false}
  />
</div>
```

## `assets.md` Format

The `assets.md` file is created during the UI/UX phase (Step 4.5) and consumed by the implement phase.

```markdown
# Image Assets — {feature}

## Design Context
- Aesthetic: {from design-system.md}
- Primary: {--color-primary value}
- Background: {--color-background value}
- Accent: {--color-accent value}
- Font pair: {display + body fonts}

## Assets

### IMG-001: {Descriptive Name}
- **Purpose:** {What this image represents in the UI}
- **Route:** {App route where it appears}
- **Component:** {React component name}
- **Aspect Ratio:** {1:1 | 3:2 | 16:9 | etc.}
- **Output Path:** `public/images/{feature}/{kebab-name}.png`
- **Prompt:** "{Narrative prompt with design system colors and aesthetic direction injected}"
- **Priority:** {high | medium | low}
```

**Example entry:**

```markdown
### IMG-001: Hero Mockup
- **Purpose:** App dashboard floating on dark background for landing page hero
- **Route:** /
- **Component:** HeroSection
- **Aspect Ratio:** 16:9
- **Output Path:** `public/images/ui-redesign/hero-mockup.png`
- **Prompt:** "A floating tablet showing a modern AI fitness analysis dashboard on a dark charcoal background (#10131a). Copper (#D2802D) and gold (#FFB779) accent lighting creates warm rim highlights around the device edges. The screen displays body metrics with glassmorphism cards. Premium luxury aesthetic, photorealistic, cinematic lighting with a key light from the left and cool blue fill from the right."
- **Priority:** high
```

## Downstream: Images That Become Video

Static images with faces usually pass image moderation. **Video generation is stricter**: face-bearing img2vid trips moderation (error E005).

So if an image is destined to become video footage, frame it without a face from the start (neck down, hands, the product). For tailoring, craft and manufacturing subjects this usually improves the shot anyway, because the product becomes the subject. See `frontend/design-system/ai-video-generation.md`.

For a frame that will drive a scroll scrub, also generate at the exact ratio the video will output, and place the subject on the side **opposite** the copy. See `frontend/design-system/premium-finish.md`.

## The Two Assets Everyone Forgets

- **Favicon / monogram.** Derive a simple mark (one or two letters, or the existing symbol) that reads at 32x32. Use `edit_image` from the real logo so it stays on-brand. Export PNG at 512x512 and let the browser downscale. **Never invent a new logo**, simplify the existing one.
- **OG / share image (1200x630).** The image that appears when the link is pasted into a chat or a feed. Use the strongest shot on the page, with breathing room for a possible brand overlay. Generate or crop at that exact ratio. See `frontend/seo/meta-schema-audit.md`.

## Variations and Cost

Generate **two or three variations per image**, varying angle, light or composition, **not just the seed**. Seed-only variation produces near-identical options and wastes the review.

If none are good, regenerate at the same step with the prompt adjusted by the feedback. **Do not advance with a weak image**: every derivative, any footage built from it, and the final page all inherit its quality.

## Quality Checklist

- [ ] Colors match design system tokens (no random colors in prompts)
- [ ] No text artifacts or misspellings (if text in image)
- [ ] Correct aspect ratio for intended use
- [ ] Appropriate file size (< 500KB for web, < 200KB for cards)
- [ ] Descriptive alt text written for accessibility
- [ ] `<Image>` component with proper width/height dimensions
- [ ] `priority` prop set for above-the-fold images
- [ ] Build compiles after integration (`npm run build`)
- [ ] Image visible in dev server at correct route
- [ ] Client assets passed as `referenceImages`; nothing generated in a vacuum
- [ ] Real renders and real product photos used as-is, never AI-recreated
- [ ] Hero approved first, then used as reference for derivatives
- [ ] Images headed for video framed without faces
- [ ] Favicon and 1200x630 OG image produced

## Boundary: Runtime Generation Is Another Standard

This standard is **authoring time**: a designer, an MCP tool, and assets that end up committed in the
repository. When the **product itself** generates images at runtime — a .NET backend calling a paid
API per request, with cost, review and provenance — the standard is
`ai-agents/modalities-image-gen.md`, and the surrounding job/review/labelling pipeline is
`ai-agents/media-pipeline.md`.

The two do not overlap: nothing here talks about paid calls per user, and nothing there repeats the
brand prompt craft above. The rule that never crosses the boundary is the hard one from
`ai-agents/media-pipeline.md`: **never generate media inside a conversation turn**.

*MORPH-SPEC by Polymorphism Tech*
