a conversational icon generator for mobile apps. you type what you want, optionally attach a reference, Google's Nano Banana 2 makes the icon, then you remix, iterate, upscale. feels like ChatGPT, not a form.
source for aso.kitchen, MIT-licensed. fork it, self-host it, or just read how a small full-stack AI product is put together.
until recently, making an app icon meant hiring a designer or wrestling with Midjourney, neither of which fits the workflow of someone shipping a mobile app at 1am trying to sort their ASO before a launch. Nano Banana 2 handles the model side cleanly now, transparent 1024px icons on the first try, so the interesting question moved from "can we generate it" to "what should the tool feel like when the model is the easy part".
the answer i landed on is a chat. one input, one canvas, history down the left. no forms, no sliders, no mode picker. attach an image and type, you get a remix. pick a season, you get live-ops. text only, it creates from scratch. mode is inferred from what you actually did, not from a dropdown you had to read first. that's the product thesis and every line in this repo serves it.
three layers, in the order a request flows through them.
the front end is Next.js 16 on the App Router with Tailwind v4, shadcn/ui, and Framer Motion. the UI is wired in components/aso-kitchen.tsx which mounts a WorkspaceProvider (React Context and useReducer for session state), a sidebar with history, and a composer pinned to the bottom. generation runs through lib/use-generation.ts which wraps every request in an AbortController, so the stop button actually stops. history is server-synced and cached in localStorage, with lib/use-history-v2.ts handling a one-time migration from the pre-DB version of the app.
the API routes are plain Next.js route handlers under app/api/. the core ones:
POST /api/generate, auth, rate limit, credit check, Replicate call, R2 upload, credit deduction, DB record, all in one file so the flow reads top to bottomPOST /api/generate/cancel, aborts an in-flight prediction and refunds the creditPOST /api/upscale, re-runs a finished generation at 2K or 4K for one extra creditGET /api/poll/[id], client polls every 2s so generation outlives any serverless function timeout without needing Replicate'sPrefer: waitheaderPOST /api/stripe/webhook, signature-verified, idempotency tracked inprocessed_webhook_eventsPOST /api/v1/generate, Studio-plan public API, auth via SHA-256 hashed API keys
the services were picked for cost and latency, not novelty:
- Replicate runs
google/nano-banana-2for generation andlucataco/remove-bgfor background removal, everything 1:1, PNG, transparent - Railway Postgres with Drizzle is the source of truth, the whole schema lives in
lib/db/schema.ts, read it top to bottom and you have the data model in five minutes - Upstash Redis handles rate limiting tiered by plan (10/30/60 RPM for free/pro/studio), fail-closed, so if Redis is down requests are denied, losing a few beats letting an abuser run free
- Cloudflare R2 stores generated images, S3-compatible API, no egress fees which matters when every user's icon has to be served back to them
- Stripe runs subscriptions, top-ups, and the customer portal
- Auth.js v5 handles Google OAuth with the Drizzle adapter, JWT session strategy
the credit system is atomic, deductions use a WHERE guard in SQL so you can't overdraw under concurrency, and refunds on failure are automatic. the whole thing is lib/credits.ts, about 200 lines, and it's the first file i'd point at if you wanted to see how the business model lives in code.
mode inference is deliberately simple, a handful of conditionals in lib/infer-mode.ts that read workspace state and return create, remix, liveops, or refinement. if you want the product pitch translated into code, that's the file.
there's an admin panel at /admin gated by a comma-separated ADMIN_EMAILS env var, a killswitch in lib/alert.ts that shows a banner when generation is disabled server-side, and a circuit breaker that trips on repeated Replicate failures. none of it is flashy but it's what turned a demo into something worth leaving running overnight.
you'll need Node 20+, Docker for the local Postgres, and accounts on Replicate, Upstash, Cloudflare R2, Stripe, and Google Cloud. first-time setup is a slog because every service needs provisioning and keys pasted into .env, after that it takes 30 seconds.
git clone https://github.com/efekucuk/aso-kitchen.git
cd aso-kitchen
npm install
cp .env.example .env
# fill in every value in .env, none of them have sensible defaults
npm run dev:db # spins up local Postgres via docker compose
npm run db:push # applies the Drizzle schema
npm run dev # next dev on http://localhost:3000first sign-in with Google lands you on the free plan with 10 credits. to unlock /admin, add your email to ADMIN_EMAILS in .env, comma-separated for multiple.
tests run under Vitest:
npm run test # watch mode
npm run test:run # single run
npm run test:coverage # with coverageone note on the DB scripts, npm run db:push and npm run db:migrate are guarded by scripts/db-guard.mjs which refuses to run against known production hosts (Railway, in my case) unless ALLOW_DB_PUSH=true is set explicitly. production data loss is not something i'd like to personally experience. if you fork this, add your own production host to the guard.
screenshots and description generators sit in the sidebar tool nav but aren't implemented. the plumbing is there, tool is already a column on generations, so adding them is mostly a prompts and UI exercise. pull requests welcome.
MIT, see LICENSE. use it for whatever you want, commercial or otherwise. if you build something you're proud of with it, open an issue or find me online.