Skip to content

Repository files navigation

πŸ–ΌοΈ Nano Studio

AI-Powered Image Editing for Small and Medium-sized Enterprises

Angular Firebase Google AI TypeScript


Nano Studio is a web app that enables users to transform simple smartphone product photos into professional, culturally rich, and visually compelling marketing assets instantly. It uses Google's Gemini 3 Pro's Image model, dubbed as Nano Banana Pro to combine an uploaded product image with a detailed, text-based background prompt, to produce a high-quality, visually compelling image for marketing needs.

🌟 About the App

Transform simple smartphone product photos into studio-quality, culturally rich marketing assets β€” instantly. πŸ“Έβœ¨

Nano Studio aims to democratize high-quality product photography for Small and Medium-sized Enterprises (SMEs), particularly those selling authentic traditional goods (e.g., kanga, kikoy, Swahili crafts, local packaged foods). We provide a tool that transforms simple smartphone product snapshots into professional, culturally rich, and visually compelling marketing assets instantly.


How it works πŸ§ πŸ–ΌοΈ

Nano Studio utilizes Google's "Nano Banana Pro" β€” the official Gemini 3 Pro's Image generation and editing model β€” to combine an uploaded product image with a detailed, text-based background prompt.

  • You upload one or more product snapshots πŸ“·
  • You describe the desired background or vibe using natural language πŸ“
  • The model composes a studio-grade image that blends your product with an authentic, evocative backdrop in seconds ⚑

"Nano Banana Pro" (Gemini 3 Pro's Image) uses conversational prompts to make consistent, high-quality edits to photos β€” changing colors, adding objects, altering textures, and more β€” while preserving realism and product integrity.


Why it matters πŸ’‘

The core value proposition is the ability to generate studio-quality images with authentic, evocative backdrops in seconds, at minimal cost. This is especially impactful for SMEs and local artisans seeking:

  • Professional-looking product photos without hiring a studio πŸ’Ό
  • Fast turnarounds for campaigns and catalogs ⏱️
  • Context-rich visuals that reflect cultural authenticity 🌍

Features ✨

  • AI-Powered Image Generation: Utilizes Google's Gemini 3 Pro model to generate high-quality product images.
  • Multi-Image Upload: Upload multiple product images at once β€” each image is sent to the AI model for richer, more accurate results.
  • Prompt-Based Editing: Users can describe the desired background and style using natural language.
  • Quick Prompts: Choose from preset styles (Studio, Soft, Noir, Vibrant) to instantly populate the prompt field.
  • Authentication: Secure user authentication with Google, powered by Firebase Authentication.
  • Prompt History: Saves and displays a history of user prompts, stored in Firestore.
  • Generation Quota: Daily generation limits per user type (guests: 5, signed-in: 10) to manage API usage.
  • Responsive UI: Built with Angular, ensuring a seamless experience across devices.

Code Highlights πŸ‘¨πŸΎβ€πŸ’»

The application's core logic is split between the frontend (Angular) and the backend (Firebase Functions with Genkit).

1. Backend: Genkit & Firebase Functions (functions/src/index.ts)

The backend uses Genkit to define an AI flow that generates images and exposes it as a callable Firebase Function.

Genkit Flow Definition: The _generateImageFlowLogic defines the AI workflow. It takes a prompt and an array of base64-encoded images, maps them into media parts, and passes all of them to the Gemini model alongside the text prompt.

// functions/src/index.ts
const ImageGenerationInputSchema = z.object({
  prompt: z.string().describe('The prompt for the image generation'),
  base64Images: z.array(z.string()).min(1).describe('The base64 encoded image data for one or more images'),
})

export const _generateImageFlowLogic = ai.defineFlow(
  {
    name: 'generateImageFlow',
    inputSchema: ImageGenerationInputSchema,
    outputSchema: ImageGenerationOutputSchema,
  },
  async({ prompt, base64Images }) => {
    const payloadText = SYSTEM_PROMPT(prompt);

    try {
      // Generate image using the AI model β€” include all uploaded images
      const mediaParts = base64Images.map(img => ({ media: { url: `data:image/jpeg;base64,${img}` } }));
      const response = await ai.generate([
        ...mediaParts,
        {text: payloadText}
      ]);

      const base64ImageResult = response.media?.url?.split(',')[1];

      if (!base64ImageResult) {
        throw new HttpsError('internal', 'Could not extract base64 image data from the response.');
      }

      return { base64ImageResult };

    } catch (e: any) {
      logger.error("Error generating image:", e);
      throw new HttpsError('internal', 'An error occurred while generating the image.', e.message);
    }
  }
);

Firebase Function Wrapper: The Genkit flow is wrapped in a callable Firebase Function, making it accessible to the frontend.

// functions/src/index.ts
export const generateImageFlow = onCallGenkit(
  {
    secrets: [GEMINI_API_KEY],
    region: 'africa-south1',
    cors: isEmulated ? true : [/^https:\/\/nano-studios(--[a-z0-9-]+)?\.web\.app$/],
  },
  _generateImageFlowLogic
);

2. Frontend: Angular Services & Components

AI Service (src/app/services/core/ai/ai.service.ts): This service calls the generateImageFlow Firebase Function.

// src/app/services/core/ai/ai.service.ts
import { httpsCallable } from '@angular/fire/functions';

// ...

export class AiService {
  // ...
  async generateContent(prompt: string, base64Images: string[]): Promise<string> {
    try {
      const generateImage = httpsCallable<{ prompt: string, base64Images: string[] }, ImageGenerationOutput>(this.functions, 'generateImageFlow');
      const response = await generateImage({ prompt, base64Images });
      const base64ImageResult = response.data.base64ImageResult;
      // ...
      return `data:image/png;base64,${base64ImageResult}`;
    }
    // ...
  }
}

Authentication Service (src/app/services/core/auth/auth.service.ts): Handles user authentication using Firebase, supporting sign-in with Google.

// src/app/services/core/auth/auth.service.ts
export class AuthService {
  currentUser = signal<User | null>(null);
  isAuthenticated = signal<boolean>(false);

  constructor() {
    onAuthStateChanged(this.auth, (user) => {
      this.currentUser.set(user);
      this.isAuthenticated.set(!!user);
    });
  }

  signInWithGoogle(): Observable<User> {
    const provider = new GoogleAuthProvider();
    return from(signInWithPopup(this.auth, provider)).pipe(
      switchMap(result => of(result.user))
    );
  }
}

Home Component (src/app/pages/home/home.ts): The Home component manages an array of uploaded files and passes all base64-encoded images to the AiService. Users can add or remove individual images before generating.

// src/app/pages/home/home.ts
export class Home {
  aiService = inject(AiService);

  readonly files = signal<File[]>([]);
  readonly filePreviewUrls = signal<string[]>([]);
  readonly base64Images = signal<string[]>([]);

  readonly canGenerate = computed(() =>
    this.base64Images().length > 0 &&
    this.prompt().trim().length > 0 &&
    !this.loading() &&
    this.userDataService.canGenerateImage(this.authService.currentUser())
  );

  removeFile(index: number): void {
    this.files.update(f => f.filter((_, i) => i !== index));
    this.filePreviewUrls.update(u => u.filter((_, i) => i !== index));
    this.base64Images.update(b => b.filter((_, i) => i !== index));
  }

  async generate(): Promise<void> {
    if (!this.canGenerate()) return;
    this.loading.set(true);

    this.aiService.generateContent(this.prompt(), this.base64Images())
      .then(async res => {
        this.resultUrl.set(res);
        // ...
      })
      .catch(error => {
        // ...
      })
  }
}

Getting started πŸ› οΈ

πŸ§ͺ Quick Start with Firebase Studio

The fastest way to get Nano Studio running:

Try in Firebase Studio

Click above to launch Nano Studio in Firebase Studio

After launching:

  1. πŸ“‚ Open Terminal - Navigate to the Terminal tab in Firebase Studio
  2. πŸ“¦ Install Dependencies - Run these commands one by one:
    npm install
    cd functions
    npm install
    cd ..

βš™οΈ Configuration Guide

πŸ’‘ Complete Setup Guide - Follow these steps to configure Firebase and required APIs for Nano Studio.

πŸ—οΈ Step 1: Create Firebase Project

Firebase Console Setup

  1. 🌐 Open Firebase Console
  1. βž• Create New Project
  • Click "Create a new Firebase project"
  • Enter project name: nano-studio-app (or your preferred name)
  • Google Analytics is optional, so no need to enable it
  1. πŸ’³ Upgrade to Blaze Plan ⚠️ Required for AI Features
  • Locate billing section in bottom-left sidebar
  • Click "Upgrade" next to Spark plan
  • Select "Pay as you Go - Blaze Plan"
  • Choose "Google Cloud Platform Trial Billing Account"
  • Set budget alert (e.g., $2 USD)
  • Click "Link Cloud Billing Account"

Step 2: Enable Required APIs ☁️

Your Firebase project needs certain Google Cloud APIs enabled:

  1. Go to Google Cloud Console:
  • Visit the Google Cloud Console
  • Make sure your Firebase project is selected in the project dropdown
  • Click on "Dashboard" to see the project's overview page
  1. Enable the Secret Manager API:
  • In the left sidebar, go to "APIs & Services" > "Library"
  • Search for "Secret Manager API"
  • Click on it and press "Enable"

Note: Other APIs (Cloud Functions, Vertex AI, etc.) are automatically enabled when you deploy Firebase Functions.

Step 3: Install and Setup Firebase CLI πŸ› οΈ

πŸ“ Note for Firebase Studio users: Skip the CLI installation step and go directly to logging in.

  1. Install Firebase CLI (skip if using Firebase Studio):

    npm install -g firebase-tools
  2. Log in to Firebase:

    firebase login
  3. In the terminal, you will be prompted to enter to visit a URL to authenticate using an authorization code.

  4. Open the URL, select the same Google account you used to create the Firebase project.

  5. Click the "Yes, I just ran this command" button.

  6. The second step shows you a session code that should tally with the one seen back in your project's terminal. Click "Yes".

  7. In Step 3, copy the code and paste it into the terminal.

Step 4: Link Your Firebase Project πŸ”„

You need to make sure your project is linked correctly:

Method 1: Using Firebase CLI (Recommended)

Set your Firebase project as the default:

firebase use YOUR_PROJECT_ID

Replace YOUR_PROJECT_ID with your actual Firebase project ID (you can find this in the Firebase Console URL or project settings).

Method 2: Manual Configuration

If the CLI method doesn't work, you can edit the .firebaserc file manually:

  1. Open .firebaserc in your project root directory
  2. Update it to match your project ID:
    {
      "projects": {
        "default": "YOUR_PROJECT_ID"
      }
    }

πŸ’‘ Tip: You can verify your project is linked correctly by running firebase projects:list to see your available projects.

Step 5: Configure Firebase Services

firebase init

Configure Firebase services when prompted:

  • Select services: Choose and "Firestore" (use space to select, enter to confirm)
  • Select a location for your Firestore database: Choose a region closer to your users
  • Firestore Rules: Accept the default firestore.rules file
  • Firestore Indexes: Accept the default firestore.indexes.json file
  • Initialize or overwrite: When asked to either initialize or overwrite the codebase, select "Overwrite"
  • ⚠️ Important: When asked to overwrite existing files, select "No" to preserve the project code
  • Install dependencies: Choose "Yes"

Step 6: Configure Firebase Web App πŸ”₯

Now you need to register a web app in Firebase and get the configuration:

  1. Register your web app:
  • Go to Firebase Console and select your project
  • Click the gear icon (βš™οΈ) next to "Project Overview" β†’ "Project settings"
  • Scroll to "Your apps" section
  • If you don't have a web app yet, click "Add app" β†’ Web icon (</>)
  • Give your app a name (e.g., "Nova Reel Web App")
  • Check the Firebase Hosting box
  • Click "Register app"
  1. Get your Firebase configuration:
  • In the "Add Firebase SDK" step, copy the configuration object (it looks like this):
{
  apiKey: "your-api-key-here",
  authDomain: "your-project.firebaseapp.com",
  projectId: "your-project-id",
  storageBucket: "your-project.appspot.com",
  messagingSenderId: "123456789",
  appId: "your-app-id",
  measurementId: "your-measurement-id"
}
  1. Update your environment files:

    Open both environment files and replace the firebaseConfig object with your own:

    For src/environments/environment.ts (production):

    export const environment = {
      production: true,
      firebaseConfig: {
        // Paste your Firebase config here
        apiKey: "your-api-key-here",
        authDomain: "your-project.firebaseapp.com",
        projectId: "your-project-id",
        storageBucket: "your-project.appspot.com",
        messagingSenderId: "123456789",
        appId: "your-app-id",
        measurementId: "your-measurement-id"
      }
    };

    For src/environments/environment.development.ts (development):

    export const environment = {
      production: false,
      firebaseConfig: {
        // Same Firebase config as above
      }
    };

    Click "Continue to console" to continue.

Firebase Authentication Setup

Step 7: Enable Authentication

  • Click the "Build" dropdown in the sidebar
  • Select "Authentication"
  • Click "Get started"
  • Select "Google"
  • Toggle the "Enable" switch
  • Provide a support email address
  • Click "Save"

Firebase Firestore Database

Step 8: Enable Firestore Database

  • Click the "Build" dropdown in the sidebar
  • Select "Firestore Database"
  • Click "Create database"
  • Select "Standard Edition"
  • You can leave the database ID as "default"
  • Select the closest location to your users
  • Click "Next"
  • Select "Start in test mode"
  • Click "Create"

πŸ” API Keys Setup

Step 9: Set Up API Keys as Firebase Secrets πŸ”‘

  1. Set Gemini API Key:

    firebase functions:secrets:set GEMINI_API_KEY

    When prompted, paste your Gemini API key.

    πŸ“ How to get Gemini API Key: Go to Google AI Studio, create an API key by attaching to a project, and copy it. The input is masked, so you won't see the pasted key, but it will be there. Just paste ONCE!

  2. Create local environment file (for development):

    echo "GEMINI_API_KEY=your_actual_gemini_api_key_here" > .env
    cd ..

    Replace your_actual_gemini_api_key_here with your actual Gemini API key.

πŸš€ Deployment and Running

Step 10: Deploy and Run Your Application

  1. Deploy Firebase Functions:

    firebase deploy --only functions

    This will deploy your backend functions to Firebase.

  2. Run the application locally:

    cd ..
    ng serve

    Your app will be available at http://localhost:4200. However, on Firebase Studio, hold Ctrl+Click on the localhost URL to open it in a new tab.

  3. Build for production (optional):

    ng build
    firebase deploy --only hosting

πŸŽ‰ Congratulations! Your Nano Studio app should now be running with full AI-powered image editing capabilities!

This project is built with Angular. If you’re setting it up locally:

  1. Install dependencies
    npm install
  2. Start the development server
    npm start
    # or
    ng serve
  3. Open the app

Usage guide πŸ‘©πŸ½β€πŸ’»πŸ‘¨β€πŸ’»

  • Upload one or more clear JPG or PNG images (well‑lit, uncluttered works best)
  • Remove any unwanted images using the βœ• button on each preview thumbnail
  • Try a quick prompt (Studio, Soft, Noir, Vibrant) or write your own, e.g.:
    • "Soft natural light, wooden tabletop, cozy morning scene"
    • "Vibrant kanga fabric backdrop, soft shadows, minimalist props"
  • Click Generate image
  • Download or share the result; refine the prompt and iterate

Notes

  • Large or multiple images may take longer to process
  • Guests can generate up to 5 images; signed-in users get 10 per day
  • Prompt history shows your last 20 prompts and when they were generated

Notes on the model 🧩

  • "Nano Banana Pro" refers to Google's Gemini 3 Pro image generation and editing model.
  • It enables conversational edits like color changes, adding objects, or texture adjustments.
  • Supports multi-image input β€” all uploaded images are passed to the model simultaneously for richer context.
  • Ideal for product scenarios where consistency, speed, and visual accuracy matter.

Scripts πŸ“œ

Common Angular CLI scripts:

  • Development server
    ng serve
  • Build for production
    ng build
  • Unit tests
    ng test

Roadmap πŸ—ΊοΈ (ideas)

  • Preset prompt templates for common product categories βœ… Done
  • Multi-image upload support βœ… Done
  • Batch processing for product catalogs
  • Export presets for marketplaces (Shopify, Jumia, etc.)
  • Fine-grained controls for lighting and shadow realism


License πŸ“„

This project is for educational and prototyping purposes. Consider reviewing licensing and API terms for any production deployment.

About

This is an AI-powered Angular application, using Google's Gemini 3 Pro "Nano Banana Pro" to transform simple smartphone product photos into studio-quality, culturally rich marketing assets β€” instantly. πŸ“Έβœ¨

Resources

Stars

36 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages