
Nano Banana Use
- 70 installs
- 124 repo stars
- Updated February 6, 2026
- cnemri/google-genai-skills
Generates, edits, and composes images with Gemini Nano Banana models via portable uv-run Python scripts, with API-key or Vertex AI auth.
About
Runs image generation, editing, and multi-image composition through Gemini Nano Banana models via ready-made scripts. A developer uses it to create or modify images from the CLI without writing SDK code.
- generate, edit, and compose scripts run with uv
- Configurable model, aspect ratio, and safety filter
Nano Banana Use by the numbers
- 70 all-time installs (skills.sh)
- Ranked #848 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cnemri/google-genai-skills --skill nano-banana-useAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 70 |
|---|---|
| repo stars | ★ 124 |
| Last updated | February 6, 2026 |
| Repository | cnemri/google-genai-skills ↗ |
What it does
Generates, edits, and composes images with Gemini Nano Banana models via portable uv-run Python scripts, with API-key or Vertex AI auth.
Files
Nano Banana Use
Use this skill to generate, edit, and compose images using Gemini's Nano Banana models (gemini-2.5-flash-image and gemini-3-pro-image-preview).
This skill uses portable Python scripts managed by uv.
Prerequisites
Ensure you have one of the following authentication methods configured in your environment:
1. API Key:
-
GOOGLE_API_KEYorGEMINI_API_KEY
2. Vertex AI:
-
GOOGLE_CLOUD_PROJECT -
GOOGLE_CLOUD_LOCATION -
GOOGLE_GENAI_USE_VERTEXAI=1
Usage
Generate an Image
Step 1: Confirm Parameters Before running the script, confirm the following parameters with the user or state the defaults you will use:
- Prompt: The image description.
- Model: Default is
gemini-3-pro-image-preview. - Aspect Ratio: Default is
1:1. - Safety Filter: Default is
BLOCK_MEDIUM_AND_ABOVE.
Step 2: Run the Script Run the python script using uv:
uv run skills/nano-banana-use/scripts/generate_image.py "A futuristic banana city" --output city.pngEdit an Image
Modify an existing image based on a text prompt.
uv run skills/nano-banana-use/scripts/edit_image.py original.png "Make the sky purple" --output edited.pngCompose Images
Generate a new image based on multiple input images and a prompt.
uv run skills/nano-banana-use/scripts/compose_image.py --image style.png --image subject.jpg "A painting of the subject in the style of the first image" --output composition.pngOptions
-
prompt: The text description of the image. -
--model: The model to use. Defaults togemini-3-pro-image-preview. -
--output: The filename for the saved image. Defaults togenerated_image.png. -
--aspect-ratio: The aspect ratio of the generated image. Defaults to1:1. Supported:1:1,16:9,4:3,3:4,9:16. -
--safety-filter-level: Safety filter threshold. Defaults toBLOCK_MEDIUM_AND_ABOVE.
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "google-genai",
# "python-dotenv",
# "pillow",
# ]
# ///
import os
from dotenv import load_dotenv
import argparse
import sys
from google import genai
from google.genai import types
from PIL import Image
import io
load_dotenv()
def get_client():
api_key = os.environ.get("GOOGLE_API_KEY") or os.environ.get("GEMINI_API_KEY")
if api_key:
return genai.Client(api_key=api_key)
project = os.environ.get("GOOGLE_CLOUD_PROJECT")
location = os.environ.get("GOOGLE_CLOUD_LOCATION")
use_vertex = os.environ.get("GOOGLE_GENAI_USE_VERTEXAI", "").lower()
if project and location and use_vertex in ("1", "true"):
return genai.Client(vertexai=True, project=project, location=location)
print("=" * 60)
print("ERROR: Missing required environment variables!")
print("=" * 60)
print()
print("Please create a .env file in your project root or add the")
print("following environment variables to your existing .env file:")
print()
print("Option 1 - Using Gemini API Key:")
print(" GOOGLE_API_KEY=your-api-key-here")
print()
print("Option 2 - Using Vertex AI:")
print(" GOOGLE_CLOUD_PROJECT=your-project-id")
print(" GOOGLE_CLOUD_LOCATION=global")
print(" GOOGLE_GENAI_USE_VERTEXAI=1")
print()
print("Note: For Vertex AI, location must be 'global'.")
print("=" * 60)
sys.exit(1)
def main():
parser = argparse.ArgumentParser(description="Compose images using Nano Banana models.")
parser.add_argument("--image", action='append', required=True, help="Path to input image(s). Can be used multiple times.")
parser.add_argument("prompt", help="Instruction for composing the image")
parser.add_argument("--model", default="gemini-3-pro-image-preview", help="Model to use (default: gemini-3-pro-image-preview)")
parser.add_argument("--output", default="composed_image.png", help="Output filename")
parser.add_argument("--aspect-ratio", default="1:1", help="Aspect ratio (e.g., 1:1, 16:9)")
parser.add_argument("--safety-filter-level", default="BLOCK_NONE", help="Safety filter level")
args = parser.parse_args()
client = get_client()
try:
contents = []
for img_path in args.image:
if not os.path.exists(img_path):
print(f"Error: Input image '{img_path}' not found.")
sys.exit(1)
with open(img_path, "rb") as f:
image_bytes = f.read()
# Determine mime type roughly
mime_type = "image/jpeg"
if img_path.lower().endswith(".png"):
mime_type = "image/png"
elif img_path.lower().endswith(".webp"):
mime_type = "image/webp"
contents.append(types.Part.from_bytes(data=image_bytes, mime_type=mime_type))
contents.append(args.prompt)
response = client.models.generate_content(
model=args.model,
contents=contents,
config=types.GenerateContentConfig(
response_modalities=['IMAGE'],
image_config=types.ImageConfig(
aspect_ratio=args.aspect_ratio,
),
safety_settings=[
types.SafetySetting(
category="HARM_CATEGORY_SEXUALLY_EXPLICIT",
threshold=args.safety_filter_level
),
types.SafetySetting(
category="HARM_CATEGORY_DANGEROUS_CONTENT",
threshold=args.safety_filter_level
),
types.SafetySetting(
category="HARM_CATEGORY_HARASSMENT",
threshold=args.safety_filter_level
),
types.SafetySetting(
category="HARM_CATEGORY_HATE_SPEECH",
threshold=args.safety_filter_level
),
]
)
)
if response.candidates and response.candidates[0].content.parts:
for part in response.candidates[0].content.parts:
if part.inline_data:
img = Image.open(io.BytesIO(part.inline_data.data))
img.save(args.output)
print(f"Image saved to {args.output}")
return
print("No image generated.")
except Exception as e:
print(f"Error composing image: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "google-genai",
# "python-dotenv",
# "pillow",
# ]
# ///
import os
from dotenv import load_dotenv
import argparse
import sys
from google import genai
from google.genai import types
from PIL import Image
import io
load_dotenv()
def get_client():
api_key = os.environ.get("GOOGLE_API_KEY") or os.environ.get("GEMINI_API_KEY")
if api_key:
return genai.Client(api_key=api_key)
project = os.environ.get("GOOGLE_CLOUD_PROJECT")
location = os.environ.get("GOOGLE_CLOUD_LOCATION")
use_vertex = os.environ.get("GOOGLE_GENAI_USE_VERTEXAI", "").lower()
if project and location and use_vertex in ("1", "true"):
return genai.Client(vertexai=True, project=project, location=location)
print("=" * 60)
print("ERROR: Missing required environment variables!")
print("=" * 60)
print()
print("Please create a .env file in your project root or add the")
print("following environment variables to your existing .env file:")
print()
print("Option 1 - Using Gemini API Key:")
print(" GOOGLE_API_KEY=your-api-key-here")
print()
print("Option 2 - Using Vertex AI:")
print(" GOOGLE_CLOUD_PROJECT=your-project-id")
print(" GOOGLE_CLOUD_LOCATION=global")
print(" GOOGLE_GENAI_USE_VERTEXAI=1")
print()
print("Note: For Vertex AI, location must be 'global'.")
print("=" * 60)
sys.exit(1)
def main():
parser = argparse.ArgumentParser(description="Edit images using Nano Banana models.")
parser.add_argument("image", help="Path to the input image to edit")
parser.add_argument("prompt", help="Instruction for editing the image")
parser.add_argument("--model", default="gemini-3-pro-image-preview", help="Model to use (default: gemini-3-pro-image-preview)")
parser.add_argument("--output", default="edited_image.png", help="Output filename")
parser.add_argument("--aspect-ratio", default="1:1", help="Aspect ratio (e.g., 1:1, 16:9)")
parser.add_argument("--safety-filter-level", default="BLOCK_NONE", help="Safety filter level")
args = parser.parse_args()
if not os.path.exists(args.image):
print(f"Error: Input image '{args.image}' not found.")
sys.exit(1)
client = get_client()
try:
# Load the input image
with open(args.image, "rb") as f:
image_bytes = f.read()
# Determine mime type roughly
mime_type = "image/jpeg"
if args.image.lower().endswith(".png"):
mime_type = "image/png"
elif args.image.lower().endswith(".webp"):
mime_type = "image/webp"
response = client.models.generate_content(
model=args.model,
contents=[
types.Part.from_bytes(data=image_bytes, mime_type=mime_type),
args.prompt
],
config=types.GenerateContentConfig(
response_modalities=['IMAGE'],
image_config=types.ImageConfig(
aspect_ratio=args.aspect_ratio,
),
safety_settings=[
types.SafetySetting(
category="HARM_CATEGORY_SEXUALLY_EXPLICIT",
threshold=args.safety_filter_level
),
types.SafetySetting(
category="HARM_CATEGORY_DANGEROUS_CONTENT",
threshold=args.safety_filter_level
),
types.SafetySetting(
category="HARM_CATEGORY_HARASSMENT",
threshold=args.safety_filter_level
),
types.SafetySetting(
category="HARM_CATEGORY_HATE_SPEECH",
threshold=args.safety_filter_level
),
]
)
)
if response.candidates and response.candidates[0].content.parts:
for part in response.candidates[0].content.parts:
if part.inline_data:
img = Image.open(io.BytesIO(part.inline_data.data))
img.save(args.output)
print(f"Image saved to {args.output}")
return
print("No image generated.")
except Exception as e:
print(f"Error editing image: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "google-genai",
# "python-dotenv",
# "pillow",
# ]
# ///
import os
from dotenv import load_dotenv
import argparse
import sys
from google import genai
from google.genai import types
from PIL import Image
import io
load_dotenv()
def get_client():
api_key = os.environ.get("GOOGLE_API_KEY") or os.environ.get("GEMINI_API_KEY")
if api_key:
return genai.Client(api_key=api_key)
project = os.environ.get("GOOGLE_CLOUD_PROJECT")
location = os.environ.get("GOOGLE_CLOUD_LOCATION")
use_vertex = os.environ.get("GOOGLE_GENAI_USE_VERTEXAI", "").lower()
if project and location and use_vertex in ("1", "true"):
return genai.Client(vertexai=True, project=project, location=location)
print("=" * 60)
print("ERROR: Missing required environment variables!")
print("=" * 60)
print()
print("Please create a .env file in your project root or add the")
print("following environment variables to your existing .env file:")
print()
print("Option 1 - Using Gemini API Key:")
print(" GOOGLE_API_KEY=your-api-key-here")
print()
print("Option 2 - Using Vertex AI:")
print(" GOOGLE_CLOUD_PROJECT=your-project-id")
print(" GOOGLE_CLOUD_LOCATION=global")
print(" GOOGLE_GENAI_USE_VERTEXAI=1")
print()
print("Note: For Vertex AI, location must be 'global'.")
print("=" * 60)
sys.exit(1)
def main():
parser = argparse.ArgumentParser(description="Generate images using Nano Banana models.")
parser.add_argument("prompt", help="The text prompt for image generation")
parser.add_argument("--model", default="gemini-3-pro-image-preview", help="Model to use (default: gemini-3-pro-image-preview)")
parser.add_argument("--output", default="generated_image.png", help="Output filename")
parser.add_argument("--aspect-ratio", default="1:1", help="Aspect ratio (e.g., 1:1, 16:9, 4:3, 3:4, 9:16)")
parser.add_argument("--safety-filter-level", default="BLOCK_NONE", help="Safety filter level")
args = parser.parse_args()
client = get_client()
try:
response = client.models.generate_content(
model=args.model,
contents=args.prompt,
config=types.GenerateContentConfig(
response_modalities=['IMAGE'],
image_config=types.ImageConfig(
aspect_ratio=args.aspect_ratio,
),
safety_settings=[
types.SafetySetting(
category="HARM_CATEGORY_SEXUALLY_EXPLICIT",
threshold=args.safety_filter_level
),
types.SafetySetting(
category="HARM_CATEGORY_DANGEROUS_CONTENT",
threshold=args.safety_filter_level
),
types.SafetySetting(
category="HARM_CATEGORY_HARASSMENT",
threshold=args.safety_filter_level
),
types.SafetySetting(
category="HARM_CATEGORY_HATE_SPEECH",
threshold=args.safety_filter_level
),
]
)
)
if response.candidates and response.candidates[0].content.parts:
for part in response.candidates[0].content.parts:
if part.inline_data:
img = Image.open(io.BytesIO(part.inline_data.data))
img.save(args.output)
print(f"Image saved to {args.output}")
return
print("No image generated.")
except Exception as e:
print(f"Error generating image: {e}")
sys.exit(1)
if __name__ == "__main__":
main()