A creative workspace that turns a brand profile and a short brief into production-ready marketing visuals, powered by Google Gemini.
π§ Under active development β features and APIs may change between updates. Feedback and contributions are welcome.
The workspace β write a brief on the left, watch on-brand creatives fill the gallery on the right.
AI Creative Studio is an AI-powered design assistant for marketing teams, founders, and creators. You define a brand profile once β name, guidelines, logo β and the studio generates polished, on-brand visuals that respect your look and feel. No design tools, no prompt-engineering expertise required.
Upload a few product shots, describe the campaign, pick an aspect ratio, and generate a full set of ad creatives ready to ship to social, web, or print.
| Feature | Description | |
|---|---|---|
| π·οΈ | Brand Profiles | Save brand name, guidelines, and logo once β every generation stays on-brand. |
| π’ | Ad Creative mode | Turn a campaign brief (goal, copy ideas, product details) into finished ad creatives. |
| πΌοΈ | Asset Generation mode | Generate standalone visual assets from a composition brief, with optional logo + guideline injection. |
| βοΈ | Editor mode | Refine any image with natural-language edit instructions ("remove background", "add neon glow"). |
| π€ | Reference uploads | Add up to 8 product images and 4 style-inspiration images to steer the AI. |
| ποΈ | Per-brand Gallery | Every generated asset is stored and organized by brand. |
| π | Dual environment | Run fully local (SQLite + filesystem) or in the cloud (Supabase DB + Storage). |
| π | Auth-ready | Google OAuth via Supabase, with a one-flag bypass for local development. |
- Social ad campaigns β batch-generate scroll-stopping creatives for Instagram, Facebook, and TikTok.
- Product marketing β turn plain product photos into styled lifestyle and hero shots.
- Brand-consistent content β keep every asset aligned to one set of brand guidelines.
- Rapid concepting β explore visual directions in minutes instead of days.
- Creative editing β iterate on existing assets without reopening a design tool.
| Metric | Value |
|---|---|
| Creative modes | 3 β Ad Creative Β· Asset Gen Β· Editor |
| Aspect ratios | 10 β from 1:1 square to 21:9 ultrawide |
| Max resolution | 4K (1K / 2K / 4K) |
| Assets per batch | Up to 10 |
| Reference inputs | 8 product images + 4 style images |
| Storage backends | 2 β local SQLite or Supabase cloud |
- Frontend: React 18 Β· TypeScript Β· Vite 6
- AI: Google Gemini (
@google/genai) - Backend (local): Express 5 Β· Prisma 7 Β· SQLite
- Backend (cloud): Supabase (PostgreSQL + Storage)
- Node.js 20.19+, 22.12+, or 24+ (22 LTS recommended). Prisma 7 and the Vite React plugin will not run on Node 18, and 20.11 is not enough either.
- npm or yarn
- Gemini API Key with billing enabled β Get one here
- Supabase Account (optional β only for cloud mode) β Sign up free
β οΈ About the Gemini API key: This app generates images usinggemini-3-pro-image(Nano Banana Pro), which is a Pro tier model. A free-tier key from AI Studio will not work for image generation β free tier only covers Flash models. You'll need a key on a billing-enabled GCP project. Enable billing β Image generation is billed per image, and a batch of 10 costs 10 images' worth, so check current pricing before running large batches.
π Where your key ends up: the app calls Gemini from the browser, so
npm run buildcompilesGEMINI_API_KEYinto the JavaScript it ships. Anyone who can load a deployed build can read it. Keep deployments private, or run locally, until generation moves behind a server.
There are two ways to run AI Creative Studio. Pick the one that matches your situation:
| π Local Setup (single machine / personal) | π’ Production Setup (org-wide / team) | |
|---|---|---|
| Who it's for | One person trying it out, or solo development | An agency or team sharing brand profiles + assets |
| Database | SQLite file on your laptop | Supabase Postgres (cloud) |
| Storage | Local storage/ folder |
Supabase Storage bucket |
| Auth | Skipped (APP_ENV=development) |
Google Sign-In via Supabase (APP_ENV=production), optionally restricted to one email domain |
| Setup time | ~5 min (one script) | ~20 min (Supabase + Google OAuth + deploy) |
| Cost | Just Gemini API usage | Same + Supabase (free tier covers small teams) |
π Recommended: start with Local Setup to validate it works for you. Moving to Production later is a
.env.localchange only: setAPP_ENV=productionand add your Supabase credentials. Authentication switches on automatically. No source files to edit.
The fastest path β an interactive setup script that handles everything: prompts for your Gemini key, installs dependencies, runs migrations, creates the storage folder, and starts the dev server. Re-running is safe β already-done steps are skipped.
git clone https://github.com/Arcady-Inc/ai-creative-studio.git
cd ai-creative-studio
bash setup.shgit clone https://github.com/Arcady-Inc/ai-creative-studio.git
cd ai-creative-studio
powershell -ExecutionPolicy Bypass -File setup.ps1π New here? Just run the script. It defaults to local mode (SQLite + filesystem) so no Supabase or Google OAuth needed to try it out. It also tells you what each value is for β including the Gemini billing requirement β before asking.
When it's done it starts the dev server. Open http://localhost:3000.
What the script does (step-by-step)
| Step | What it checks / does |
|---|---|
| 1 | Verifies you're in the project root (package.json) |
| 2 | Verifies Node.js 20.19+/22.12+/24+ is installed (links to download if not) |
| 3 | Creates .env.local from .env.example if missing |
| 4 | Prompts for your Gemini API key β with a note about free vs paid tier (Pro Image model needs billing enabled) |
| 5 | Asks local vs cloud mode; if cloud, prompts for Supabase URL + anon key |
| 6 | Runs npm install if node_modules is missing or stale |
| 7 | Runs prisma migrate deploy to create dev.db (local mode only) |
| 8 | Creates storage/creatives/ folder for asset uploads (local mode only) |
| 9 | Starts the dev server (npm run dev or npm run dev:prod) |
The script validates input format (Supabase URL matches *.supabase.co; the Supabase key is a eyJ... JWT or a sb_publishable_... key; the Gemini key is a plausible key string) so typos are caught before they break the app.
Manual setup (if you prefer to do it by hand)
macOS / Linux
git clone https://github.com/Arcady-Inc/ai-creative-studio.git
cd ai-creative-studio
npm install
cp .env.example .env.local
# Add your GEMINI_API_KEY to .env.local
# (Supabase keys are optional, only needed for cloud mode)
npx prisma migrate deploy # creates dev.db
npx prisma generate # required: the server imports the generated client
npm run devWindows (PowerShell)
git clone https://github.com/Arcady-Inc/ai-creative-studio.git
cd ai-creative-studio
npm install
Copy-Item .env.example .env.local
# Add your GEMINI_API_KEY to .env.local
npx prisma migrate deploy
npx prisma generate
npm run devDo not skip
npx prisma generate. Without itserver/dev-server.tsfails to import@prisma/clientand the API server exits immediately.
Open http://localhost:3000 in your browser.
Use this path when you want your whole agency/team to share brand profiles, gallery assets, and sign in with their work Google account.
You'll wire up three things (~20 min total):
-
ποΈ Supabase project β hosts the Postgres DB and the asset storage bucket. β Follow the Supabase Setup section below (create project, run SQL, create the
creativesbucket). -
π Google OAuth via Supabase β so only your team's email domain can sign in. β Follow GOOGLE_AUTH_SETUP.md (10-min walkthrough with screenshots).
-
π Production-mode toggle β switch the app from "bypass auth, local DB" to "require Google, cloud DB":
- In
.env.local: setAPP_ENV=productionand fill inSUPABASE_URL+SUPABASE_ANON_KEY - In
.env.local: optionally setALLOWED_DOMAINto your team's email domain - Run
npm run dev:prod(or deploy the built app to any host β Vercel, Render, etc.)
- In
π‘ The setup script also handles Production mode. If your Supabase credentials are missing,
setup.sh/setup.ps1offers to collect them and setsAPP_ENV=productionfor you. If you decline andAPP_ENVis alreadyproduction, the script leaves it alone and tells you what is still missing.
π¦ Hosting is not documented yet, on purpose.
npm run buildproduces a static bundle with your Gemini API key compiled into it, so we are not going to tell you to upload that to a public host. Generation is being moved behind a server; deployment guides (Docker, Vercel, and a one-click option) land with it. Until then, run it locally, or host it somewhere only your team can reach.The design for that work, including support for OpenAI, FLUX and OpenRouter alongside Gemini, is in docs/design/multi-provider-image-models.md.
π‘ The setup script writes this file for you. This section is here for reference β read it if you want to know what each variable does or edit
.env.localby hand.
-
Copy the template file:
cp .env.example .env.local
-
Edit
.env.localwith your credentials:# Gemini API Key (Required) GEMINI_API_KEY=your_actual_gemini_api_key # Supabase Credentials (Required for cloud features) SUPABASE_URL=https://your-project-id.supabase.co SUPABASE_ANON_KEY=your_supabase_anon_key
β οΈ Important: Never commit.env.localto version control!
The app supports Google Sign-In via Supabase, with a one-flag bypass for local development.
APP_ENV=development # sign-in skipped
APP_ENV=production # Google sign-in required
ALLOWED_DOMAIN=yourcompany.com # optional: only @yourcompany.com can sign inAPP_ENV |
Behavior |
|---|---|
development |
Auth skipped, app loads immediately. Local SQLite + filesystem. |
production |
Google Sign-In required via Supabase. Cloud DB + storage. |
ALLOWED_DOMAIN is optional. Set it to example.com to accept only
@example.com accounts; leave it empty to accept any Google account that signs in.
β οΈ This is a client-side check. It controls what the UI renders, not what the database allows. A user outside your domain still holds a valid Supabase token after signing in, so enforce the real restriction in your RLS policies too.
To make APP_ENV=production sign-in actually work, you need to wire up Google OAuth in both Supabase and Google Cloud. Follow the step-by-step guide:
π GOOGLE_AUTH_SETUP.md β 10-minute walkthrough with screenshots
It covers: grabbing the Supabase callback URL, creating the Google Cloud OAuth app, where to paste the Authorized redirect URI, restricting access to your Workspace org, and troubleshooting.
Only needed for Production / Org-wide setup. For Local Setup, the app uses SQLite and local file storage out of the box β see DEVELOPMENT.md.
- Go to supabase.com and sign in
- Click "New Project"
- Fill in:
- Name:
creative-studio(or any name) - Database Password: Create a strong password (save it!)
- Region: Choose closest to your users
- Name:
- Click "Create new project"
- Wait for project to initialize (1-2 minutes)
- In your Supabase dashboard, go to Project Settings (gear icon)
- Click API in the left sidebar
- Copy these values to your
.env.local:- Project URL β
SUPABASE_URL - anon/public key β
SUPABASE_ANON_KEY
- Project URL β
Example:
SUPABASE_URL=https://abcdefghijklmnop.supabase.co
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...- In Supabase dashboard, go to Storage (left sidebar)
- Click "New bucket"
- Enter:
- Name:
creatives - Public bucket: β Enable (toggle ON)
- Name:
- Click "Create bucket"
π‘ The bucket name must be exactly
creativesto match the app code.
- In Supabase dashboard, go to SQL Editor (left sidebar)
- Click "New query"
- Copy and paste the ENTIRE SQL script below
- Click "Run" (or press Ctrl/Cmd + Enter)
π Click to expand the complete SQL setup script
-- ===========================================
-- AI CREATIVE STUDIO - DATABASE SETUP
-- ===========================================
-- Run this entire script in Supabase SQL Editor
-- This will create all tables, policies, and storage setup
-- ===========================================
-- STEP 1: CREATE TABLES
-- ===========================================
-- Brand Profiles Table
CREATE TABLE IF NOT EXISTS brand_profiles (
id TEXT PRIMARY KEY,
name TEXT,
guidelines TEXT,
logo_preview TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::TEXT, NOW())
);
-- Generated Assets Table
CREATE TABLE IF NOT EXISTS generated_assets (
id TEXT PRIMARY KEY,
profile_id TEXT NOT NULL,
url TEXT NOT NULL,
prompt_used TEXT,
aspect_ratio TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::TEXT, NOW())
);
-- ===========================================
-- STEP 2: ENABLE ROW LEVEL SECURITY (RLS)
-- ===========================================
-- Enable RLS on brand_profiles
ALTER TABLE brand_profiles ENABLE ROW LEVEL SECURITY;
-- Enable RLS on generated_assets
ALTER TABLE generated_assets ENABLE ROW LEVEL SECURITY;
-- ===========================================
-- STEP 3: CREATE ACCESS POLICIES
-- ===========================================
-- These grant access to SIGNED-IN users only.
--
-- Do not use `USING (true)` without a TO clause here. Postgres defaults such a
-- policy to TO PUBLIC, which includes the `anon` role, and the anon key is
-- readable by anyone who loads the app. That combination lets an unauthenticated
-- caller read, modify and delete every brand profile and asset straight through
-- the REST API, bypassing the login screen entirely.
-- Drop old permissive policies if a previous version of this script ran
DROP POLICY IF EXISTS "Public Profiles Access" ON brand_profiles;
DROP POLICY IF EXISTS "Public Assets Access" ON generated_assets;
DROP POLICY IF EXISTS "Authenticated Profiles Access" ON brand_profiles;
DROP POLICY IF EXISTS "Authenticated Assets Access" ON generated_assets;
-- Signed-in users can read and write brand profiles
CREATE POLICY "Authenticated Profiles Access" ON brand_profiles
FOR ALL
TO authenticated
USING (true)
WITH CHECK (true);
-- Signed-in users can read and write generated assets
CREATE POLICY "Authenticated Assets Access" ON generated_assets
FOR ALL
TO authenticated
USING (true)
WITH CHECK (true);
-- OPTIONAL: restrict to one Google Workspace domain, enforced in the database
-- rather than the browser. Replace both policies above with these, substituting
-- your domain. This is the enforcement that ALLOWED_DOMAIN in the UI cannot provide.
--
-- DROP POLICY IF EXISTS "Authenticated Profiles Access" ON brand_profiles;
-- CREATE POLICY "Domain Profiles Access" ON brand_profiles
-- FOR ALL TO authenticated
-- USING ((auth.jwt() ->> 'email') LIKE '%@yourcompany.com')
-- WITH CHECK ((auth.jwt() ->> 'email') LIKE '%@yourcompany.com');
--
-- DROP POLICY IF EXISTS "Authenticated Assets Access" ON generated_assets;
-- CREATE POLICY "Domain Assets Access" ON generated_assets
-- FOR ALL TO authenticated
-- USING ((auth.jwt() ->> 'email') LIKE '%@yourcompany.com')
-- WITH CHECK ((auth.jwt() ->> 'email') LIKE '%@yourcompany.com');
-- ===========================================
-- STEP 4: SETUP STORAGE BUCKET
-- ===========================================
-- Create the 'creatives' bucket if it doesn't exist
INSERT INTO storage.buckets (id, name, public)
VALUES ('creatives', 'creatives', true)
ON CONFLICT (id) DO NOTHING;
-- ===========================================
-- STEP 5: STORAGE POLICIES
-- ===========================================
-- Drop existing storage policies if they exist
DROP POLICY IF EXISTS "Public Uploads" ON storage.objects;
DROP POLICY IF EXISTS "Public Reads" ON storage.objects;
DROP POLICY IF EXISTS "Public Deletes" ON storage.objects;
DROP POLICY IF EXISTS "Authenticated Uploads" ON storage.objects;
DROP POLICY IF EXISTS "Authenticated Deletes" ON storage.objects;
-- Only signed-in users can upload
CREATE POLICY "Authenticated Uploads" ON storage.objects
FOR INSERT
TO authenticated
WITH CHECK (bucket_id = 'creatives');
-- Only signed-in users can delete
CREATE POLICY "Authenticated Deletes" ON storage.objects
FOR DELETE
TO authenticated
USING (bucket_id = 'creatives');
-- Reads stay public because the app displays images via getPublicUrl().
--
-- Know what this means: every generated creative is readable by anyone with the
-- URL, forever, with no login. For client work under NDA that is usually not
-- acceptable. Making the bucket private requires switching
-- services/db/supabase.ts from getPublicUrl() to createSignedUrl(); that change
-- is tracked with the server-side generation work.
CREATE POLICY "Public Reads" ON storage.objects
FOR SELECT
USING (bucket_id = 'creatives');
-- ===========================================
-- β
SETUP COMPLETE!
-- ===========================================
-- Your database is now ready for AI Creative Studionpm run devOpens at http://localhost:3000
npm run build
npm run previewFor the full local development guide (dual environments, scripts, database management, API endpoints), see DEVELOPMENT.md.
- Check that
SUPABASE_URLandSUPABASE_ANON_KEYare set correctly in.env.local - Restart the dev server after changing environment variables
- Make sure you ran the complete SQL setup script
- Check that RLS policies were created successfully in Supabase Dashboard > Authentication > Policies
- Verify the
creativesstorage bucket exists and is public - Check storage policies in Supabase Dashboard > Storage > Policies
- Ensure
APP_ENV=productionin.env.local, and thatALLOWED_DOMAIN(if set) matches your team's email domain - Follow the full step-by-step in GOOGLE_AUTH_SETUP.md β it covers Supabase + Google Cloud config and the most common
redirect_uri_mismatchfix
If you see errors about process not being found, run:
npm installThese are dev-time warnings and won't affect runtime.
Released under the MIT License β free to use, modify, and distribute. Just keep the copyright notice.
Owner: Arcady Media Inc.
Developer: Ritik
- Gemini API Keys: https://aistudio.google.com/app/apikey
- Supabase: https://supabase.com
- Development Guide: DEVELOPMENT.md