TL;DR: Enhancer is an open-source, browser-based AI image studio built with React 19, Vite 6, TypeScript, and Google's Gemini 2.5 Flash image models. Upload any photo, drag a selection over the region you care about, and Enhancer has Gemini describe that region and then generate an AI-upscaled, enhanced version of just the crop. Every zoom step plays as a pixel-dissolve animation, your full zoom history is preserved step by step, and the entire session can be exported as an animated GIF. Apache-2.0 licensed: github.com/Shivay00001/Enhancer.
The problem: AI upscalers are upload-and-pray
Most AI image enhancers work the same way: you upload a whole image, press a button, and get a whole image back. You have no control over which part gets enhanced, no visibility into what the model thought it was looking at, and no record of the process. If you want to zoom into a face in a crowd shot, read text on a distant sign, or inspect a product detail, you are stuck with either pixelation or a black-box "enhance" button that may hallucinate details you never asked for.
Creators have a second problem: the process is the proof. A before/after GIF of a zoom-enhance is worth more on social media and in portfolios than a static result, but producing one usually means screen-recording your session and editing it by hand.
Enhancer was built to fix both: region-selective enhancement with a visible, inspectable pipeline — select, describe, enhance, animate, export — all in the browser, all open source.
What Enhancer does
Enhancer is a Google AI Studio-style interactive demo with a real workflow behind it:
- Upload an image through the drag-and-drop zone.
- Drag a selection rectangle over any region of interest.
- Gemini describes the region. The
gemini-2.5-flashmodel receives the crop plus your selection history and returns two things: a precise "the user selected…" description and a short enhancement prompt written for the image model. - Gemini enhances the crop. The
gemini-2.5-flash-imagemodel receives the cropped region and the enhancement prompt, and generates a high-resolution, detail-rich version — constrained to stay faithful to the reference. - Watch the pixel dissolve. The transition from original to enhanced plays as a canvas pixel-dissolve animation.
- Zoom again. Every step is pushed onto a zoom history, so you can drill deeper, region by region.
- Export a GIF. The full zoom history becomes an animated GIF with smooth eased zoom transitions — ready to share.
There is also a playful easter egg: a "nano banana" 🍌 that the enhancement model is invited to hide subtly in the image, detected by parsing the model's text output for a {"foundTheBanana": true} JSON signal.
How it works: the architecture
Enhancer is a client-side React application. There is no backend server in the loop — the browser talks to the Gemini API directly (more on the security implications of that below).
| File | Role |
|---|---|
App.tsx (~32 KB) | Main state machine: upload → select → describe → enhance → animate |
types.ts | Core types: AppState enum (IDLE → LOADING → LOADED → SELECTING → PROCESSING → ENHANCING → ENHANCED), Rect, ImageDescription, HistoryStep |
components/DropZone.tsx | Image upload surface |
components/SelectionAnimator.tsx | Drag-to-select rectangle UI |
components/ImageDisplay.tsx (~9.7 KB) | Canvas rendering of the current image state |
components/PixelDissolve.tsx | Pixel-dissolve transition animation |
components/StatusBar.tsx | Pipeline status readout |
utils/serviceDescribeImage.ts | Region description via gemini-2.5-flash |
utils/serviceEnhance.ts | Upscale/enhance via gemini-2.5-flash-image |
utils/gifGenerator.ts | Zoom-history → animated GIF via gifenc |
utils/imageUtils.ts | Canvas and data-URL helpers |
utils/extractJson.ts | Robust JSON extraction from model text output |
utils/geminiConfig.ts | Centralized API-key access with a clear error when missing |
The two-model pipeline
The interesting design decision is that Enhancer uses two different Gemini models for two different jobs, instead of one do-everything call:
1. Description (gemini-2.5-flash). serviceDescribeImage.ts converts the crop's data URL into an inlineData part (extracting the real MIME type from the data-URL header) and sends it with a carefully engineered system prompt. The prompt asks for two outputs: a precise "The user selected…" description for internal history, and a short, non-narrative enhancement prompt for the image model — which is treated as a black box that only receives the prompt and the crop, never the history. The prompt rules are genuinely thoughtful: always provide a camera angle, infer the most plausible perspective (external perspective for architectural features unless history says otherwise), and — critically — if the selection is blurry or over-zoomed, suggest a creative but imprecise description ("the faint glow of a distant nebula") rather than inventing sharp details, so the enhancement model infers from pixels instead of from hallucinated prose. Output is requested as strict JSON and parsed with extractJson; every failure path falls back to a safe default description rather than crashing.
2. Enhancement (gemini-2.5-flash-image). serviceEnhance.ts takes the crop plus the description history and calls the image model with responseModalities: [IMAGE, TEXT]. The generation prompt does two things well: it anchors fidelity — "The resulting image should be a clearer, higher-resolution version of the input, and nothing more, and match in shapes and colors. DO NOT add any elements or render outside of the provided reference image subject." — and it only permits creativity when the content genuinely can't be determined. The response parser walks the returned parts, extracts inlineData as the enhanced image, and attempts JSON.parse on any text part to detect the banana easter-egg signal (non-JSON text is logged and ignored). On any API error, the original crop is returned — graceful degradation, not a broken UI.
The GIF engine
gifGenerator.ts is a small piece of real animation engineering. Given the HistoryStep[] zoom history (each step carries its imageSrc and the originalRect selection that produced it), it:
- Renders at 512 px wide, with the aspect ratio taken from the first selection rectangle.
- For each transition: 30 frames (1.0 s) of zoom, interpolating the crop rectangle with an
easeInOutCubiceasing curve, drawing a white dashed selection box that tracks the target region. - Then 15 hold frames (0.5 s) on the resulting enhanced image.
- Quantizes every frame to a 256-color palette with
gifenc'squantize/applyPaletteand writes frames at 30 fps.
The result is a share-ready GIF that shows the viewer exactly what you did and what the AI produced — provenance as a feature.
API key handling — done right, with an honest warning
geminiConfig.ts centralizes key access: the key is injected at build time by vite.config.ts from the GEMINI_API_KEY environment variable (see .env.example, which contains only the placeholder YOUR_GEMINI_API_KEY). It is never hardcoded, .env files are gitignored, and a missing key throws a clear, actionable error naming the exact variable to set. The README goes further and states the uncomfortable truth plainly: because the demo calls Gemini directly from the browser bundle, the built key is visible to anyone who opens the site — so use a key with strict HTTP-referrer and quota restrictions, and move the calls to a server-side endpoint for production. That is the correct posture for a demo, and it's good to see it written down.
How to run it
Prerequisites: Node.js 18+ and a free Gemini API key from Google AI Studio.
git clone https://github.com/Shivay00001/Enhancer.git
cd Enhancer
npm install
cp .env.example .env # then set GEMINI_API_KEY=your_key
npm run dev # opens on http://localhost:3000
Containerized runs are supported too: the repo ships a Dockerfile and docker-compose.yml, and CI is wired with GitHub Actions (node-ci.yml for build checks, deploy.yml for deployment).
Honest status and roadmap
Status: working demo, with one known wiring gap. The React application type-checks cleanly (npx tsc --noEmit passes), the pipeline logic is complete, and without a valid API key the app still renders the upload and selection UI while AI features throw clear errors. The README discloses the current gap honestly: index.html presently serves a static landing page, and the React app (App.tsx, mounted via index.tsx) is not yet wired into it — adding a <div id="root"> and the module script tag before </body> completes the interactive demo. This is a small fix away from being fully live, and the disclosure is exactly the kind of honesty you want in an open-source showcase.
Roadmap (sensible next steps):
- Wire the React app into
index.htmlso the full interactive demo runs out of the box. - Move Gemini calls behind a small server-side endpoint so API keys stop living in the browser bundle.
- Side-by-side before/after compare slider on each history step.
- Export formats beyond GIF — WebP animation and MP4 for social platforms.
- Editable region descriptions, so users can steer the enhancement prompt before generation.
- Batch mode: enhance a folder of crops with one click.
Who should look at this repo
- Frontend developers wanting a clean example of a multi-stage AI pipeline in React with a real state machine (
AppState), canvas animation, and client-side GIF encoding. - AI builders interested in the two-model describe-then-enhance pattern and in prompt engineering that constrains hallucination ("match in shapes and colors," creativity only when content is undeterminable).
- Creators who want before/after zoom GIFs without screen-recording.
Frequently asked questions
Do I need a Gemini API key to run Enhancer?
Yes, for the AI features — you can get one free from Google AI Studio. Without a key, the upload and selection UI still render, but describe/enhance calls throw a clear error telling you exactly what to set.
Is my API key safe in this app?
The key is read from an environment variable at build time and never hardcoded, but because the demo calls Gemini directly from the browser, the built key is visible to anyone who opens the site. Use a key with strict referrer and quota restrictions, and for production move the calls server-side.
Which models does it use?gemini-2.5-flash for region description and gemini-2.5-flash-image for the actual upscaling/enhancement.
Can I use Enhancer commercially?
Yes — it is licensed Apache-2.0. Check the LICENSE file in the repo for the full terms.
The bottom line
Enhancer is a genuinely instructive little build: a region-selective AI enhancement pipeline with honest prompt engineering, graceful failure handling, and a GIF export that turns the process into the deliverable. The two-model describe-then-enhance split and the fidelity-first prompting are patterns worth stealing for any image-AI project. Clone it, set your key, and try zooming into something you photographed this week — the GIF it produces is the review.