gemini-generate-img

Use when generating images from text prompts via Google's Gemini image models ("nano-banana") with the `google-genai` SDK or OpenRouter. Covers model choice, aspect ratio, prompt patterns, multi-candidate sampling, and retry logic. Triggers: "generate image", "gemini image", "nano-banana", "text to image", "gemini-3-pro-image", "gemini-2.5-flash-image", "imagen", "paper figure generation"

10 stars

Best use case

gemini-generate-img is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Use when generating images from text prompts via Google's Gemini image models ("nano-banana") with the `google-genai` SDK or OpenRouter. Covers model choice, aspect ratio, prompt patterns, multi-candidate sampling, and retry logic. Triggers: "generate image", "gemini image", "nano-banana", "text to image", "gemini-3-pro-image", "gemini-2.5-flash-image", "imagen", "paper figure generation"

Teams using gemini-generate-img should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/gemini-generate-img/SKILL.md --create-dirs "https://raw.githubusercontent.com/dongzhuoyao/tao-research-skills/main/skills/genai/gemini-generate-img/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/gemini-generate-img/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How gemini-generate-img Compares

Feature / Agentgemini-generate-imgStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Use when generating images from text prompts via Google's Gemini image models ("nano-banana") with the `google-genai` SDK or OpenRouter. Covers model choice, aspect ratio, prompt patterns, multi-candidate sampling, and retry logic. Triggers: "generate image", "gemini image", "nano-banana", "text to image", "gemini-3-pro-image", "gemini-2.5-flash-image", "imagen", "paper figure generation"

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Gemini Image Generation

## When to Use

- Text-to-image generation via Gemini image models (a.k.a. nano-banana) from Python
- Generating paper figures, teasers, demo assets, or social creative in research pipelines
- Picking between `gemini-3-pro-image-preview` (quality) and `gemini-2.5-flash-image` (speed)
- Running batch / parallel candidate sampling with retry

For editing an existing image or composing multiple images, use `gemini-edit-img`.

## Quick Start

```python
import os
from google import genai
from google.genai import types

client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])

response = client.models.generate_content(
    model="gemini-3-pro-image-preview",
    contents=[types.Part.from_text(text="A quiet reading nook at dawn, watercolor")],
    config=types.GenerateContentConfig(
        response_modalities=["IMAGE"],
        candidate_count=1,
        image_config=types.ImageConfig(aspect_ratio="16:9", image_size="1k"),
    ),
)

for part in response.candidates[0].content.parts:
    if part.inline_data:
        with open("out.png", "wb") as f:
            f.write(part.inline_data.data)
        break
```

## Models

| Model | Use when | Notes |
|-------|----------|-------|
| `gemini-3-pro-image-preview` | Quality matters — hero figures, paper diagrams | Best text rendering; slower |
| `gemini-2.5-flash-image` | Iteration / many candidates | Fast, cheap, the "nano-banana" default |
| `gemini-3.1-flash-image-preview` | Current fast preview | Use when 2.5-flash-image deprecates |

Default to `gemini-3-pro-image-preview` for finals; switch to `gemini-2.5-flash-image` when `candidate_count` > 4 or for quick drafts.

## Aspect Ratio & Size

Pass through `types.ImageConfig(aspect_ratio=..., image_size=...)`:

| Ratio | Pixel dims | Use |
|-------|-----------|-----|
| `1:1`  | 1024×1024 | logos, square covers |
| `16:9` | 1344×768  | landing hero, slides, YouTube |
| `9:16` | 768×1344  | stories, mobile portrait |
| `4:5`  | 896×1152  | Instagram feed |
| `21:9` | 1536×672  | paper teasers, banners |
| `3:2`  | 1248×832  | photo classic |

`image_size`: `"1k"` (default) | `"2k"` | `"4k"`. Start at 1k; upscale only for finals — 4k is ~10× slower.

## Prompt Patterns

### In-image text

Gemini-3-pro handles text better than any open model but still miscounts long strings.

- Quote the literal text: `a neon sign reading "Quiet Chapter"`
- Keep per-sign text ≤ 6 words
- For multi-sign scenes, list each sign explicitly rather than bundling

### Composition

- Subject → setting → mood → style: `a cartographer's desk at dusk, warm lamplight, tilted angle, watercolor, muted palette`
- Put style qualifiers at the end
- Avoid negative prompts (`no X`) — Gemini ignores most. Rephrase positively.

### Multi-candidate sampling

`candidate_count` is capped at 8 per call. For more, loop externally with parallel requests:

```python
# Reference: PaperBanana/utils/generation_utils.py::call_gemini_with_retry_async
target = 16
config.candidate_count = min(8, target)
results = []
while len(results) < target:
    resp = await client.aio.models.generate_content(model=model, contents=contents, config=config)
    results.extend(img_bytes_from(resp))
```

## Retry Pattern

Image generation returns an empty `parts` list on safety filters and transient errors. Always check before indexing:

```python
import asyncio

async def gen_with_retry(client, model, contents, config, max_attempts=5, base_delay=5):
    for attempt in range(max_attempts):
        try:
            resp = await client.aio.models.generate_content(
                model=model, contents=contents, config=config
            )
            if resp.candidates and resp.candidates[0].content.parts:
                for part in resp.candidates[0].content.parts:
                    if part.inline_data:
                        return part.inline_data.data
        except Exception as e:
            print(f"attempt {attempt + 1}: {e}")
        await asyncio.sleep(min(base_delay * (2 ** attempt), 30))
    raise RuntimeError("All attempts returned empty parts")
```

Full reference: `PaperBanana/utils/generation_utils.py::call_gemini_with_retry_async`.

## OpenRouter Alternative

Single key for both reasoning and image generation. Send `modalities` and `image_config` in the chat payload:

```python
import httpx

payload = {
    "model": "google/gemini-3-pro-image-preview",
    "messages": [{"role": "user", "content": [{"type": "text", "text": "..."}]}],
    "modalities": ["image", "text"],
    "image_config": {"aspect_ratio": "16:9", "image_size": "1k"},
}
resp = httpx.post(
    "https://openrouter.ai/api/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}"},
    json=payload, timeout=300,
)
```

Image bytes arrive in one of three places in the response — see `PaperBanana/utils/generation_utils.py::call_openrouter_image_generation_with_retry_async` for the full extraction chain (inline_data, `images` field, or base64 data URL in `content`).

## Environment

```bash
export GEMINI_API_KEY="..."          # Direct Gemini (preferred)
# or
export GOOGLE_API_KEY="..."          # Equivalent — google-genai reads either
# or
export OPENROUTER_API_KEY="sk-or-..." # Unified routing via OpenRouter
```

Keys: https://aistudio.google.com/apikey (Gemini) | https://openrouter.ai/keys (OpenRouter).

## Anti-Patterns

- **Using a non-image model** — `gemini-pro`, `gemini-2.5-flash` don't generate images. Model name must contain `-image`.
- **`response_modalities=["TEXT", "IMAGE"]` when you only want pixels** — adds a text caption, slows generation, bloats response. Use `["IMAGE"]`.
- **Indexing `response.candidates[0].content.parts` without checking** — safety filter returns empty parts. Always guard.
- **Long negative prompts** — `"no text, no humans, no watermark"` is mostly ignored. Rewrite positively.
- **`candidate_count > 8` per single call** — API hard-caps at 8. Loop externally.
- **Saving `inline_data.data` as `.jpg`** — it's PNG bytes. Convert with Pillow (`Image.open(BytesIO(b)).convert("RGB").save(..., "JPEG", quality=95)`) if you need JPEG.
- **Not fixing a seed** — Gemini image models are stochastic; for A/B you want `seed=` in `GenerateContentConfig` for reproducibility.

## See Also

- `gemini-edit-img` — Editing / composing existing images with the same Gemini image models
- `genai-evaluation-metrics` — Scoring generated image quality (FID, CLIP-score, LPIPS)
- `academic-deep-research` — Evaluating papers that report Gemini / nano-banana baselines

Related Skills

We are still matching the closest adjacent skills for this page. In the meantime, continue through the full directory.