Skip to content

Instantly share code, notes, and snippets.

@bguiz
Created December 16, 2025 09:42
Show Gist options
  • Select an option

  • Save bguiz/d0a6c30966351d111af106f0a553d752 to your computer and use it in GitHub Desktop.

Select an option

Save bguiz/d0a6c30966351d111af106f0a553d752 to your computer and use it in GitHub Desktop.
Generate images using Nano Banana using an OpenRouter API key.
#!/usr/bin/env node
import fs from 'node:fs/promises';
// extract inputs
const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY;
const IMAGE_NAME = process.env.IMAGE_NAME;
const AI_MODEL = process.env.AI_MODEL || 'google/gemini-3-pro-image-preview';
const ASPECT_RATIO = process.env.ASPECT_RATIO || '1:1';
const IMAGE_SIZE = process.env.IMAGE_SIZE || '1K';
// read prompt from text file
const inputFileName = `./${IMAGE_NAME}.txt`;
const IMAGE_PROMPT = (await fs.readFile(inputFileName)).toString();
// don't allow script to run in output file exists
const outputFileName = `./${IMAGE_NAME}.json`;
const outputFileAlreadyExists = await fileExists(outputFileName);
if (outputFileAlreadyExists) {
console.error(`output file exists, overwrite not allowed: ${outputFileName}`);
process.exit(-1);
}
// make the API request to nano banana
const requestBody = {
model: AI_MODEL,
messages: [{ role: 'user', content: IMAGE_PROMPT }],
modalities: ['image', 'text'],
image_config: {
aspect_ratio: ASPECT_RATIO,
image_size: IMAGE_SIZE,
}
};
console.log(requestBody);
const response = await fetch(
'https://openrouter.ai/api/v1/chat/completions', {
method: 'POST',
headers: {
Authorization: `Bearer ${OPENROUTER_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
});
// write the raw LLM response into a JSON file
const result = await response.json();
await fs.writeFile(outputFileName, JSON.stringify(result, undefined, 2));
console.log(`wrote raw response: ${outputFileName}`);
// parse the raw LLM response and create individual image files from each one
const responseImages = result?.choices[0].message?.images;
const writeImagePromises = responseImages.map((image, index) => {
const imageBase64Url = image.image_url.url;
const { fileType, fileBuffer } = parseImageBase64Url(imageBase64Url);
const outputImageFileName = `${IMAGE_NAME}--${index + 1}.${fileType}`;
console.log(`writing image file: ${outputImageFileName}`);
return fs.writeFile(outputImageFileName, fileBuffer);
});
await Promise.all(writeImagePromises); // parallel
console.log('wrote all image files');
// utility function to convert base64 URIs to image data ready to be written to disk
function parseImageBase64Url(base64Url) {
const matches = base64Url.match(/^data:image\/(\w+);base64,(.+)$/);
if (!matches || matches.length !== 3) {
throw new Error('invalid base64 image URL');
}
const [, imageType, base64Data] = matches;
return {
fileType: imageType,
fileBuffer: Buffer.from(base64Data, 'base64')
};
}
// utility function to check if file exists
async function fileExists(filePath) {
try {
await fs.access(filePath, fs.constants.F_OK);
return true;
} catch {
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment