Image Generation
Generate and edit images from text prompts, reference images, and masks
After reading this guide, you will know:
- How to generate images from text prompts.
- How to generate several images from one prompt in a single request.
- How to edit existing images with source images and masks.
- How to select different image generation models.
- How to specify image sizes (for supported models).
- How to inspect token usage and calculate image costs.
- How to save images to disk or attach them with Rails Active Storage.
- How to handle errors during image generation.
Basic Image Generation
Describe the image you want, then save it:
image = RubyLLM.paint "A red panda coding Ruby on a laptop, watercolor"
image.save "red_panda.png"
save handles both hosted URLs and inline image data.
Generating Several Images at Once
Pass count: to request several images in one call. RubyLLM returns an array when the request comes back with several images, and a single image otherwise:
images = RubyLLM.paint("a siamese cat", model: "gpt-image-2", count: 4)
images.each_with_index do |image, index|
image.save("cat-#{index}.png")
end
Some models generate one image per request regardless of count:.
Usage for the request lives on the first image. Read images.first.cost.total; it is nil when pricing or usage is unavailable.
Token Usage and Costs
When providers return image token usage, images expose the same cost shape as chats and messages:
image = RubyLLM.paint("A small watercolor robot", model: "gpt-image-2")
image.tokens.input
image.tokens.output
image.cost.input
image.cost.output
image.cost.total
See Tokens and Costs for usage accounting.
Editing Existing Images
Some models, such as OpenAI’s GPT Image models, can edit an existing image instead of generating from scratch. Use with: to pass one or more source images, and mask: when you want to constrain which parts of the image may change.
image = RubyLLM.paint(
"Turn the logo green and keep the background transparent",
model: "gpt-image-2",
with: "logo.png"
)
with: accepts the same kinds of sources RubyLLM already supports elsewhere for attachments: local files, URLs, IO-like objects, and Active Storage attachments.
Editing With Multiple Images
image = RubyLLM.paint(
"Combine these references into a postcard illustration",
model: "gpt-image-2",
with: ["person.png", "style-reference.png"]
)
Editing With a Mask
image = RubyLLM.paint(
"Replace only the background with a sunset sky",
model: "gpt-image-2",
with: "portrait.png",
mask: "portrait-mask.png",
size: "1024x1024"
)
Choosing Models
Pass model: to choose an image model:
RubyLLM.paint("A mountain village at sunrise", model: "gemini-3.1-flash-lite-image")
Set default_image_model in Configuration to change the default. Find image models on the Models page. For hosted deployments, pass provider: explicitly; see Model Resolution.
Mistral accepts a chat model for image generation:
RubyLLM.paint("A red panda drawing a Ruby logo",
model: "mistral-small-latest")
ElevenLabs does not list image models through its model-listing endpoint. Pass a documented image model with assume_model_exists: true:
RubyLLM.paint("A small red ruby on a white background",
model: "gemini-3.1-flash-lite-image",
provider: :elevenlabs, assume_model_exists: true)
Configure the required Image & Video plan and permissions. You can reuse uploaded media assets as inputs.
Image Sizes
Pass size: to choose dimensions supported by your model:
image = RubyLLM.paint(
"A panoramic mountain landscape at dawn",
model: "gpt-image-2",
size: "1536x1024"
)
For Gemini, you can specify an aspect ratio or resolution tier:
image = RubyLLM.paint(
"A red ruby gemstone on white",
model: "gemini-3.1-flash-lite-image",
size: "16:9"
)
Gemini also accepts "1K", "2K", and "4K". Gemini, ElevenLabs, and Stable Diffusion on Bedrock interpret pixel dimensions as an aspect ratio; the model may return different dimensions. Bedrock image editing controls its own output size, so leave size: unset when editing. Omit size: to let a model choose.
Working with Generated Images
Saving Images Locally
image.save "illustration.png"
save returns the path you passed. Keep the extension consistent with image.mime_type, and save hosted images before their URLs expire.
Getting Raw Image Blob
Use to_blob when another library or storage service needs the image bytes:
image_bytes = image.to_blob
Rails Active Storage Integration
Attach a generated image to your own model:
class Product < ApplicationRecord
has_one_attached :illustration
end
image = RubyLLM.paint "A hand-drawn illustration of #{product.name}"
product.illustration.attach(
io: StringIO.new(image.to_blob),
filename: "illustration.png",
content_type: image.mime_type
)
Here product is an existing Product record. Run generation in a background job when a web request should return immediately.
Image Metadata
| Reader | Value |
|---|---|
image.model |
The model that generated the image. |
image.mime_type |
The image’s MIME type, such as "image/png". |
image.revised_prompt |
The provider’s rewritten prompt, when reported. |
image.url |
A hosted image URL, when returned. |
image.data |
Base64-encoded image data, when returned inline. |
image.base64? |
Whether inline data is available. |
Use save or to_blob to read the image without branching on its delivery format.
Errors and Background Work
Generation and downloads can fail, so let your job or request handle the error where it can retry or report the failure. RubyLLM raises RubyLLM::BadRequestError for rejected requests and other RubyLLM::Error subclasses for provider failures. See Error Handling for retries and specific exceptions.
Store generated images for reuse. For jobs that need a longer request timeout, see Connection Settings.
Next Steps
- Video Generation - animate an image you have generated.
- Attachments - ask a model about an image.
- Rails Integration - use media generation in your application jobs.