[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-82662":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":15,"forks30d":15,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":24,"hasPages":22,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":36,"readmeContent":37,"aiSummary":38,"trendingCount":15,"starSnapshotCount":15,"syncStatus":39,"lastSyncTime":40,"discoverSource":41},82662,"image-extender","boona13\u002Fimage-extender","boona13","Seamlessly extend any image in any direction with AI. Open-source web app powered by Gemini via OpenRouter, with Poisson-blended seams and best-of-3 variant picker.","https:\u002F\u002Fgithub.com\u002Fboona13\u002Fimage-extender",null,"TypeScript",976,109,3,0,9,74,913,45,98.12,"MIT License",false,"main",true,[26,27,28,29,30,31,32,33,34,35],"ai","gemini","image-generation","nano-banana","nextjs","openrouter","outpainting","poisson-blending","tailwindcss","typescript","2026-06-12 04:01:38","# Image Extender\n\n> Seamlessly extend any image in any direction with AI — then build whole 2D\n> game art sets (parallax backgrounds, autotiles, sprite animations, and\n> decoration props) from the same studio.\n\nA small open-source web app for AI outpainting **and** 2D game-art generation.\nPowered by Google's Gemini image models via [OpenRouter](https:\u002F\u002Fopenrouter.ai),\nwith a Poisson-blending pipeline that hides the seam between original and\nAI-generated pixels, plus purpose-built pipelines for tiles, sprites, and props.\n\nBring your own OpenRouter API key — it stays in your browser, never on the\nserver.\n\n![A 1024² portrait shot extended into a cinematic wide-angle scene with no visible seam](docs\u002Fscreenshots\u002Fafter.png)\n\n## Before \u002F After\n\nA single 1024 × 1024 phone-style portrait → a wide 16:9 cinematic frame, in\na few clicks. Same colors, same lighting, same wet-pavement reflections —\njust much, much more of them.\n\n| Before · 1024 × 1024 | After · extended L+R into a cinematic wide |\n| --- | --- |\n| ![Square portrait shot of a person in a yellow jacket on a neon-lit Brooklyn corner](docs\u002Fscreenshots\u002Fbefore.png) | ![The same scene extended sideways into a wide cinematic frame](docs\u002Fscreenshots\u002Fafter.png) |\n\n## Five modes — Extender + Parallax + Tiles + Sprites + Props\n\nThe app ships as a tiny studio with five workspaces, switchable from the pill\nin the top bar:\n\n- **Extender** (default) — outpaint any image in any of four directions, with a\n  best-of-3 seam-quality variant picker.\n- **Parallax Studio** — build a real, multi-layer sidescroller background from\n  scratch: separate **Sky \u002F Far \u002F Mid \u002F Near** layers, role-aware AI prompts,\n  chroma-keyed transparent layers, a live multi-layer scrolling preview,\n  auto-extend to a target width, tileable-loop healing, and one-click ZIP export\n  with a JSON manifest.\n- **Tile Studio** — a 13-tile autotile set for 2D platformers (body + 4 edges\n  + 4 outer corners + 4 inner corners) generated in **one** AI call as a 4×4\n  sprite-sheet, with deterministic corner reconciliation and an AI \"art\n  director\" QA\u002Frepaint loop. Palette and texture detail stay locked across the\n  whole set.\n- **Sprite Studio** — character & creature animations as a single AI-call sheet.\n  Pick a **body plan** (humanoid, quadruped, serpent\u002Ffish, flyer\u002Fbird, or blob),\n  pick an animation, describe the creature, and get a keyframe sheet back with a\n  live animation player and engine-ready export. Each body plan drives its own\n  anatomy-specific pose-guide rig and animation set.\n- **Props Studio** — an open-ended, ever-growing library of standalone\n  transparent **decoration sprites** (the kind games layer on top of the tile\n  map) generated 8 at a time, driven by a two-call \"art director → painter\"\n  pipeline that keeps the set varied and never repetitive.\n\n### Parallax Studio\n![Parallax Studio — Sky \u002F Far \u002F Mid \u002F Near depth layers with a live multi-layer scrolling preview and ZIP export](docs\u002Fscreenshots\u002Fmode-parallax.png)\n\n### Tile Studio\n![Tile Studio — a 13-tile autotile set generated in one call, with a live \"how tiles fit together\" platform preview](docs\u002Fscreenshots\u002Fmode-tiles.png)\n\n### Props Studio\n![Props Studio — an open-ended decoration library of transparent scatter sprites grown 8 at a time](docs\u002Fscreenshots\u002Fmode-props.png)\n\n### Sprite Studio\n![Sprite Studio — a character walk-cycle keyframe sheet with a live looping animation player](docs\u002Fscreenshots\u002Fmode-sprite.png)\n\n## Features\n\n- **Click an edge → extend in that direction.** Spatial controls on the image,\n  no dialog-tree UX.\n- **Best-of-3 variant picker.** Every extension generates up to 3 candidates,\n  sorted by seam quality. Cycle through them with `← →` and pick the one\n  you like before accepting.\n- **Poisson-blended seams.** Uses gradient-domain image editing (Pérez et al.\n  2003) with mask-grow + replicate-padded Gauss-Seidel iterations to make the\n  AI-original boundary mathematically invisible.\n- **Pre-correction for low-frequency color drift.** Bulk-shifts the AI output\n  toward the original's color at the seam before blending, which fixes the\n  \"the sky got slightly bluer\" failure mode common to outpainting.\n- **Optional prompt + art style.** Leave the prompt blank for pure scene\n  continuation, or add specific instructions like *\"add an alien moon\n  rising on the horizon\"*.\n- **Custom art styles.** 40+ styles from cinematic and oil painting to\n  Studio Ghibli, cyberpunk, vaporwave, etc.\n- **BYOK (Bring Your Own Key).** Your OpenRouter key is stored only in your\n  browser's `localStorage`. The server proxies your requests to OpenRouter\n  but never logs or persists the key.\n- **Model picker.** Switch between Gemini 3 Pro Image (Nano Banana Pro),\n  Gemini 3 Flash Image (Nano Banana 2), and Gemini 2.5 Flash Image (Nano\n  Banana) from Settings — each with tuned best-of-N and timing.\n- **Keyboard-first.** Arrow keys to extend, `←`\u002F`→` to cycle variants,\n  `Enter` to accept, `R` to regenerate, `Esc` to discard.\n- **Generate from scratch.** Don't have a base image? Generate one with a\n  text prompt first, then extend.\n- **Shared scene brief.** One auto-generated \"scene brief\" (setting, time of\n  day, palette, mood) is distilled from your prompt and reused across Parallax,\n  Tiles, Sprites, and Props so every asset in a project feels like one world.\n\n### Parallax Studio (for game designers)\n\n- **4 real depth layers.** Sky (back, opaque), Far (distant silhouettes), Mid\n  (mid-ground), Near (foreground props). Pick a card on the left, edit just\n  that layer in the canvas — the same extend pipeline as Extender, but the\n  studio knows what each layer is for.\n- **Role-aware AI prompts.** When you generate or extend a layer, the model\n  is told its role and asked to produce only that depth band. Far \u002F Mid \u002F Near\n  come back rendered against a flat magenta key (`#FF00FF`) which the client\n  replaces with real alpha — so each layer composites cleanly over the sky.\n- **Live multi-layer preview.** Stacked, GPU-accelerated `repeat-x` scroll for\n  every populated layer, each at its own adjustable speed (Sky drifts slow,\n  Near runs fast). Tweak per-layer speed sliders and feel the depth in real\n  time before exporting.\n- **Locked horizontal extension.** Only `←` and `→` are exposed — vertical\n  extends would warp the game height, so they're suppressed.\n- **Auto-extend to target width.** Pick a target (e.g. 7680px = 4 × 1080p\n  screens) and walk away. The studio repeatedly extends the active layer\n  right, auto-accepts the best variant, re-applies chroma-keying, and stops\n  when the target width is reached. Click `Stop` to interrupt.\n- **Tileable loop point.** Game engines tile parallax backgrounds with\n  `repeat-x`. The `Tileable` button does the standard \"offset by half \u002F heal\n  the new middle seam \u002F offset back\" pass so the texture loops invisibly. It\n  also **auto-runs at the end of auto-extend** so the default exported result\n  Just Works.\n- **Harmonize seams.** A separate optional pass for the *other* common problem:\n  each AI extend introduces a tiny color\u002Fbrightness shift, and over many\n  extensions those shifts pile up into vertical \"panel banding\". The\n  `Harmonize` button runs a column-mean smoothing that kills the banding while\n  preserving fine detail.\n- **Width presets.** Quick-pick common targets (3840 \u002F 5120 \u002F 7680 \u002F 10240 \u002F\n  15360 px) corresponding to multiples of 720p \u002F 1080p screens.\n- **One-click ZIP export.** Bundle all populated layers as PNGs (with alpha\n  preserved) plus a `parallax.json` manifest listing depth order, scroll\n  speeds, and dimensions — drop the ZIP into Unity \u002F Godot \u002F Phaser and wire\n  it straight into your parallax controller.\n\n### Tile Studio (2D platformer autotiles)\n\n- **Whole 13-tile set in one AI call.** Body + 4 straight edges + 4 outer\n  (convex) corners + 4 inner (concave) corners are generated together as one\n  4×4 sheet, so palette, texture scale, and lighting are **locked** across the\n  set instead of drifting across 13 separate calls.\n- **Template-guided image-to-image.** Rather than ask the model to invent an\n  atlas from text, it restyles a structural reference — a rounded rectangle\n  with a rectangular hole on flat magenta. Each of the 13 roles lives at a known\n  cell, so the client slices them out deterministically after restyle.\n- **Auto-alignment + chroma key.** The restyled output is re-fitted to the\n  template silhouette (fixing the \"AI painted it smaller, centered\" failure),\n  magenta is keyed to alpha with a tile-tuned chroma key, and edge tiles are\n  made tileable along their loop axis.\n- **Deterministic corner reconciliation.** Corners are the hardest tiles, so\n  the app stitches them rather than trusting raw AI cells: outer corners keep\n  their painted cap and get a feathered **edge graft** so the grain matches\n  their straight neighbors in both directions; inner corners preserve the AI\n  art and graft only the seams that touch straight edges. The result is\n  seamless corners run-to-run.\n- **AI \"Art Director\" QA\u002Frepaint loop.** After generation the set is composited\n  into a platform-preview mockup and handed to a *vision* critic that judges it\n  like a senior tileset artist — checking the chroma key actually keyed out\n  (no opaque background blocks), edge-cap consistency, palette\u002Flighting\n  cohesion, body seamlessness, fringe, and blur. If it fails it returns a\n  concise fix report that drives a repaint; if it passes it ships. The critic\n  is deliberately scoped to **painter-fixable** defects (it does not nitpick the\n  app-composited corner geometry).\n- **Keep-best selection.** Every pass is scored and the loop commits the *best*\n  candidate it saw, never just the last one — so a flaky critic + full repaint\n  can only ever improve on, never regress, a clean first generation.\n- **Tileable body, no visible grid.** The repeating body fill gets a stronger\n  2D-seamless pass (full tonal equalization + wide seam blends), and the prompt\n  forbids cell-sized panels \u002F long streaks \u002F hero features, so the interior\n  reads as one continuous surface when repeated.\n- **Live \"how tiles fit together\" preview** and per-tile re-roll. Regenerate a\n  single tile without touching the rest; the set re-reconciles its corners\n  afterward.\n- **Engine-ready atlas export.** Export the padded atlas (with a 2px duplicated\n  **extrude** border around every tile to stop filtering\u002Fsub-pixel bleed in\n  Unity \u002F Godot \u002F Phaser \u002F Tiled) plus per-tile PNGs and a manifest.\n- **14 material presets** — lush meadow, mossy stone, red brick, snowy peak,\n  oak planks, desert sandstone, volcanic rock, glow cave, crystal ice, jungle\n  floor, autumn earth, marble & gold, obsidian, coral reef — each a rich\n  palette\u002Fsurface description you can edit.\n\n### Sprite Studio (character animations)\n\n- **Two-pass anchor → sheet workflow.** Naive single-call multi-panel\n  generation flickers — the character drifts between cells, frames come out at\n  different scales, palettes shift. We adopt the consensus approach from the AI\n  sprite community:\n  - **Pass 1 — Lock character.** Generates a single neutral standing reference\n    image of the character on a flat magenta key.\n  - **Pass 2 — Paint sheet.** Generates the keyframe sheet with the anchor\n    attached as a reference: *\"the attached image is the canonical character —\n    every cell must match it exactly.\"* This is the single biggest lever for\n    cross-frame identity preservation.\n  - The anchor **persists across animation switches**, so the same character can\n    be re-used for idle → walk → run → jump → attack without re-rolling\n    identity. A `Re-roll character` button discards the anchor for a fresh one.\n- **Five body plans.** A humanoid skeleton can't drive a galloping wolf, a\n  slithering eel, a flapping bird, or a bouncing slime — so Sprite mode branches\n  on anatomy. Pick a **body plan** and the studio swaps in the matching pose rig,\n  animation set, starter creatures, and QA expectations, while reusing the same\n  anchor → sheet → align → export pipeline:\n  - **Humanoid** (biped) — knights, mages, goblins, bosses.\n  - **Quadruped** — wolves, big cats, horses, hounds, plus everyday animals\n    (dog, cat, cow, deer, bear, fox, pig, goat).\n  - **Serpent \u002F Fish** — snakes, eels, and marine life (shark, clownfish,\n    pufferfish, anglerfish, swordfish, dolphin, sea serpent, piranha).\n  - **Flyer \u002F Bird** — birds, bats, wyverns, fairies, phoenix.\n  - **Blob** — slimes, oozes, elementals, ghosts (pure squash & stretch).\n- **Anatomy-specific pose-guide rigs.** Each body plan ships a deterministic,\n  code-generated pose-guide sheet (a \"ControlNet-style\" mannequin) drawn fresh\n  for the chosen action and fed in as structural reference. Rigs use near\u002Ffar\n  value separation + dark outlines so overlapping limbs stay readable, and they\n  render real anatomy — a quadruped spine + 4 legs with a head-bob gait, a\n  serpent spine wave with an open-jaw strike, filled bird **wing membranes**\n  with a proper flap\u002Fdive, and blob squash-&-stretch arcs.\n- **Deterministic twin detector.** A pixel-analysis pass scans each cell's alpha\n  for two creatures in one frame (a common multi-panel failure), including a\n  morphological-opening step that splits *fused* duplicates, and forces a\n  repaint when it finds them. Sprite generation leans on these deterministic\n  checks rather than a vision critic, so it isn't blocked waiting on a QA model.\n- **Scale normalization.** The model redraws the creature at a slightly\n  different size in every cell, so the silhouette \"breathes\" during playback.\n  A pass measures each frame's tight silhouette (bbox diagonal), takes the\n  median as the intended scale, and rescales each frame toward it — within a\n  tolerance + clamp so genuine pose extension (a run reach, an attack lunge)\n  keeps its shape.\n- **Baseline & horizontal alignment.** Frames are foot-baseline aligned to one\n  shared in-cell floor and horizontally centered so playback doesn't bounce or\n  slide. Grounded gaits (idle \u002F walk \u002F **run**) plant *every* frame on the floor\n  line; only truly ballistic actions (jump, pounce) keep their airborne lift via\n  a rigid shift, so a galloping run can't split into a high row and a low row.\n- **Per-body-plan animation sets.** Humanoid: idle \u002F walk \u002F run \u002F jump \u002F attack \u002F\n  hurt \u002F death. Quadruped: idle \u002F walk \u002F run \u002F jump \u002F pounce \u002F hurt \u002F death \u002F\n  sleep. Serpent: idle \u002F slither \u002F strike \u002F coil \u002F hurt \u002F death. Flyer: idle \u002F\n  flap \u002F glide \u002F dive \u002F hurt \u002F death. Blob: idle \u002F hop \u002F bounce \u002F lunge \u002F hurt \u002F\n  death — each with tuned choreography and a sensible default FPS.\n- **Live animation player.** Looping \u002F one-shot playback at the anim's native\n  FPS with play\u002Fpause and a frame scrubber; the FPS slider tunes the feel before\n  exporting.\n- **Creature preset chips.** One-click archetypes per body plan seed the prompt\n  (humanoid knight\u002Fninja\u002Fwizard…, quadruped wolf\u002Fbear\u002Fcat…, marine shark\u002Fkoi…,\n  flyer hawk\u002Fwyvern…, blob slime\u002Fooze…).\n- **Engine-ready export.** Download the grid sheet, a horizontal strip\n  (preferred by Phaser \u002F Unity 2D \u002F Godot \u002F Defold), or a ZIP with both sheets,\n  one PNG per frame, and a `manifest.json` listing FPS, loop flag, frame size,\n  and per-frame grid + strip coordinates.\n\n### Props Studio (scatter decoration)\n\n- **Open-ended, growing library.** Instead of a fixed sheet of dictated items,\n  the model freely invents decoration props for your biome. Each \"add more\"\n  press paints another batch of **8** props in one AI call and **appends** them\n  — the library grows without bound and never re-rolls what already exists.\n- **Two-call \"art director → painter\" pipeline.** Call #1 is a *text\u002Freasoning*\n  model acting as art director: given the biome and the categories already in\n  the library, it invents the next batch of brand-new, distinct prop ideas\n  (deliberately reaching across plants, minerals, bones, debris, totems,\n  containers, creature traces, light sources, etc.). Call #2 is the image model,\n  which paints exactly what the director decided. Splitting *ideation* from\n  *rendering* is what stops the \"same lanterns\u002Fpots\u002Fnests loop.\"\n- **Cheap text de-duplication.** Each prop reports a one-word category that's\n  tallied and fed back as a text budget hint, so new batches avoid look-alikes\n  without ever shipping the whole library back as images.\n- **Style-locked across batches.** A small montage of existing props is attached\n  as a visual style anchor so palette \u002F lighting \u002F rendering stay consistent as\n  the library grows.\n- **Curate freely.** Re-roll or delete any single prop; everything is generated\n  on transparency.\n- **8 biome presets** — forest glade, glowing cave, desert oasis, snowy peaks,\n  volcanic, jungle ruins, misty swamp, candy land — these set palette\u002Fmood only,\n  never specific items, so the model stays free to invent.\n- **Descriptive names in the manifest.** Because the art director already named\n  each prop (the kind it decided to paint), the export uses that instead of\n  anonymous `prop_001`: the manifest carries a human `name` per prop and the\n  files are named after it (`lantern.png`, `mushroom.png`, with `_02`\u002F`_03`\n  suffixes on repeats), so the atlas is self-documenting in your engine.\n- **Atlas + ZIP export.** Export the whole library as a packed transparent atlas\n  PNG with a JSON manifest, or a ZIP of individual transparent PNGs + atlas +\n  manifest.\n\n## The AI \"Art Director\" QA pattern\n\nTiles and Props share a two-call reasoning-vs-rendering pattern that\nconsistently beats a single blind generation:\n\n- **Props** run it *forward*: a reasoning model decides **what** to make, then\n  the image model renders it.\n- **Tiles** run it *in reverse*: the image model generates first, then a vision\n  model reviews the composited result and, if needed, sends a concise fix report\n  back for a repaint — with keep-best selection so the loop can only improve the\n  output. (The review is auto-skipped on slow models like GPT image to avoid\n  multi-minute blind waits.)\n- **Sprites** lean on **deterministic** post-process checks instead of a vision\n  critic — scale normalization, baseline grounding, horizontal centering, and a\n  pixel-level twin\u002Fspillover detector that forces a repaint on duplicates. This\n  keeps sprite generation fast and predictable rather than blocked on a QA model.\n\nCritics are scoped to defects the painter can actually fix, run at low\ntemperature for consistency, and fail-open (a flaky critic never blocks you).\n\n## How extension works\n\n```\n┌─────────────┐   1. expand canvas with        ┌───────────────────┐\n│  original   │ ──  light-gray blank area ──▶  │  expanded canvas  │\n└─────────────┘     in chosen direction        └─────────┬─────────┘\n                                                         │\n                                                         ▼\n                                              ┌─────────────────────┐\n                                              │  Gemini outpaints   │\n                                              │  the blank region   │\n                                              └─────────┬───────────┘\n                                                        │\n                                                        ▼\n                                              ┌─────────────────────┐\n                                              │  pre-correct color  │\n                                              │  drift at seam      │\n                                              └─────────┬───────────┘\n                                                        │\n                                                        ▼\n                                              ┌─────────────────────┐\n                                              │  Poisson blend with │\n                                              │  grown mask         │\n                                              └─────────┬───────────┘\n                                                        │\n                                                        ▼\n                                              ┌─────────────────────┐\n                                              │  measure seam       │\n                                              │  residual, repeat   │\n                                              │  ×3, sort, present  │\n                                              └─────────────────────┘\n```\n\nFor horizontal extensions we run the pipeline up to 3 times in parallel\nattempts (each at a different temperature), measure the seam residual of\neach blended result, and present the candidates sorted best-blend first.\nYou're free to cycle and pick a different one if you prefer the AI's\ncontent choices over the cleanest seam.\n\nVertical extensions use a different chunked path that's deterministic\nenough that 1 attempt usually suffices.\n\n## Quick start\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fboona13\u002Fimage-extender.git\ncd image-extender\nnpm install\nnpm run dev\n```\n\nOpen [http:\u002F\u002Flocalhost:3000](http:\u002F\u002Flocalhost:3000). On first load the app\nwill prompt for your OpenRouter API key — paste it once, it's stored locally,\nyou'll never see the prompt again unless you clear it from Settings.\n\nGet a key at [openrouter.ai\u002Fkeys](https:\u002F\u002Fopenrouter.ai\u002Fkeys). It costs\nabout **$0.03 per Gemini extension** (Nano Banana 2 Flash Image).\n\n### Optional: server-side env fallback\n\nIf you'd rather not enter the key in the browser (or you're hosting a demo\nwhere you want to provide the key for visitors), copy `.env.example` to\n`.env.local` and fill in your key:\n\n```bash\ncp .env.example .env.local\n# edit .env.local and add your OPENROUTER_API_KEY\n```\n\nWhen set, the server will use this key for any request that doesn't\ninclude a client-provided one.\n\n## Usage\n\n| Action | How |\n| --- | --- |\n| **Switch mode** | Click `Extender` \u002F `Parallax` \u002F `Tiles` \u002F `Sprite` \u002F `Props` pill in the top bar |\n| **Upload image** | Drag & drop, click the dropzone, or generate one from text |\n| **Extend** | Click one of the four edge handles, or press `↑` `↓` `←` `→` (parallax mode: `←` `→` only) |\n| **Cycle variants** | `←` `→` arrow keys (or chevrons in the pill below the image) |\n| **Accept \u002F Regenerate \u002F Discard \u002F Download** | `Enter` \u002F `R` \u002F `Esc` \u002F `Download` button |\n| **Pick a parallax layer** | Click a card in the left panel (Sky \u002F Far \u002F Mid \u002F Near) |\n| **Adjust per-layer scroll speed** | Drag the slider on each layer card — preview updates live |\n| **Auto-extend (parallax)** | Set a target width, click `Auto-extend`, click `Stop` to interrupt |\n| **Make tileable \u002F Harmonize (parallax)** | `Tileable` heals the loop seam (auto-runs after auto-extend); `Harmonize` flattens cumulative drift |\n| **Export project (parallax)** | Click `ZIP` → all layers + `parallax.json` manifest |\n| **Generate a tile set** | Describe the material, click generate — one AI call, auto corner-reconcile + QA review |\n| **Re-roll one tile** | Click the spark on a single tile cell (re-reconciles corners after) |\n| **Export tile set** | Atlas (with extrude padding) + per-tile PNGs + manifest |\n| **Pick a sprite body plan** | Choose `Humanoid` \u002F `Quadruped` \u002F `Serpent \u002F Fish` \u002F `Flyer \u002F Bird` \u002F `Blob` — swaps the rig, anim set, and presets |\n| **Pick a sprite animation** | Click an animation chip (the set depends on the body plan) |\n| **Lock a sprite character** | Pick a starter chip (or describe one), click `Lock character + \u003Canim>` — runs both passes |\n| **Re-roll a sprite anim \u002F character** | `Re-roll \u003Canim>` runs Pass 2 only (identity preserved); `Re-roll character` runs both from scratch |\n| **Play \u002F pause sprite, adjust FPS** | Play button + scrubber under the live player; drag the `FPS` slider |\n| **Export sprite project** | `Sheets + manifest` for grid + strip PNGs, or `ZIP` for everything |\n| **Generate props** | Pick a biome preset (or describe one), click add — paints 8 new distinct props and appends them |\n| **Add more props** | Press add again — another batch of 8, deduped against the existing library |\n| **Curate props** | Hover a prop to re-roll or delete it |\n| **Export props** | `Atlas + manifest` for the packed transparent atlas, or `ZIP` for individual PNGs + atlas + manifest |\n\nOptional custom prompt and art style live in the bottom command bar.\n\n## Tech stack\n\n- **[Next.js 14](https:\u002F\u002Fnextjs.org\u002F)** (App Router) + React 18 + TypeScript\n- **[Tailwind CSS](https:\u002F\u002Ftailwindcss.com\u002F)** for the dark studio theme\n- **HTML Canvas** for all client-side image manipulation\n  ([app\u002Futils\u002FimageProcessor.ts](app\u002Futils\u002FimageProcessor.ts))\n- **[JSZip](https:\u002F\u002Fstuk.github.io\u002Fjszip\u002F)** for in-browser project bundling\n- **[OpenRouter](https:\u002F\u002Fopenrouter.ai)** for model access\n  - Image: `google\u002Fgemini-3.1-flash-image-preview` (Nano Banana 2, default),\n    `google\u002Fgemini-3-pro-image-preview` (Nano Banana Pro),\n    `google\u002Fgemini-2.5-flash-image` (Nano Banana), and\n    `openai\u002Fgpt-5.4-image-2` (GPT-5.4 Image 2 — high fidelity, slower)\n  - Reasoning \u002F vision QA (scene brief, prop art director, tile review):\n    `google\u002Fgemini-2.0-flash-001`\n\n## Project structure\n\n```\napp\u002F\n├── api\u002F\n│   ├── extend\u002Froute.ts        Outpainting endpoint (proxies OpenRouter)\n│   ├── generate\u002Froute.ts      Text-to-image + tile-sheet + sprite-sheet prompts\n│   ├── scene-brief\u002Froute.ts   Distill a shared scene brief for a project\n│   ├── prop-brief\u002Froute.ts    Props \"art director\" — invents the next prop batch\n│   ├── tile-review\u002Froute.ts   Tile QA \"art director\" (vision critic)\n│   └── sprite-review\u002Froute.ts Sprite QA \"art director\" (vision critic)\n├── components\u002F                UI split by workspace\n│   ├── TopBar \u002F CommandBar \u002F Workspace \u002F VariantSelector \u002F Modals \u002F icons\n│   ├── ParallaxStudio.tsx\n│   ├── TileStudio.tsx         + PlatformPreview compositor\n│   ├── SpriteStudio.tsx\n│   └── PropStudio.tsx\n├── lib\u002F                       Domain logic & constants\n│   ├── app.ts \u002F models.ts \u002F artStyles.ts\n│   ├── parallax.ts \u002F tileset.ts \u002F sprite.ts \u002F props.ts\n│   └── bodyPlans.ts           Sprite body-plan registry (anims, presets, rigs)\n├── utils\u002F\n│   ├── imageProcessor.ts      Canvas: chunking, Poisson blend, chroma key,\n│   │                          tileability, seam scoring, sprite align\u002Fscale\n│   ├── poseRig.ts             Dispatches to the body-plan rig + measures subject\n│   ├── rigCore.ts             Shared rig primitives (capsule\u002Fdot\u002Fprojection…)\n│   └── rigs\u002F                  Per-body-plan pose rigs (biped, quadruped,\n│                              serpent, flyer, blob)\n├── globals.css                Dark \"studio\" design system\n├── layout.tsx                 Root layout, Inter font\n└── page.tsx                   App shell: state, generation pipelines, QA loops\n```\n\n## Configuration knobs\n\nA few small values you might want to tune:\n\n| Constant | Where | Default | Meaning |\n| --- | --- | --- | --- |\n| `EXTENSION_PERCENT` | `app\u002Flib\u002Fapp.ts` | `38` | How much of the current dimension each extension adds |\n| `maxAttempts` | per model in `app\u002Flib\u002Fmodels.ts` | `1`–`3` | Best-of-N candidates per horizontal extension |\n| `MAX_TILE_REVIEW_PASSES` | `app\u002Fpage.tsx` | `2` | Extra tile repaint passes the QA art director may trigger |\n| `TILESET_TILE_SIZE` | `app\u002Flib\u002Ftileset.ts` | `512` | Per-tile resolution in the 4×4 sheet |\n| `TILESET_ATLAS_EXTRUDE_PX` | `app\u002Flib\u002Ftileset.ts` | `2` | Duplicated border around each exported atlas tile |\n| `PROP_BATCH` | `app\u002Flib\u002Fprops.ts` | `8` | Props painted per \"add more\" press |\n| `GROW_PX` | `app\u002Futils\u002FimageProcessor.ts` | `8` | Pixels to grow the Poisson mask into the original |\n| `iterations` | `app\u002Futils\u002FimageProcessor.ts` | `250` | Max Gauss-Seidel iterations |\n\n## Privacy & security\n\n- The OpenRouter API key entered in the UI is stored **only** in your\n  browser's `localStorage`. It is never written to the server's disk and\n  never logged. The server uses it once per request to proxy the call to\n  OpenRouter, then discards it.\n- The server-side `OPENROUTER_API_KEY` env var is **optional** and acts only\n  as a fallback for requests that don't include a client-provided key.\n- No analytics, no telemetry, no tracking.\n\n## Acknowledgments\n\n- Poisson image editing technique: **Pérez, Gangnet, and Blake (2003) —\n  \"Poisson Image Editing\"**, SIGGRAPH.\n- Google for the Gemini image models, OpenRouter for the unified API.\n\n## License\n\n[MIT](LICENSE)\n","Image Extender 是一个基于AI的开源网页应用，能够无缝地在任何方向扩展图像。它利用了Google的Gemini图像模型通过OpenRouter提供支持，并采用Poisson混合技术来平滑原始图像与生成部分之间的接缝，同时内置了针对瓷砖、精灵和道具等2D游戏资源生成的专业管道。用户可以携带自己的OpenRouter API密钥进行操作，确保数据安全。此工具特别适合于需要创建或扩展2D游戏素材（如背景图、自动瓷砖集、角色动画序列及装饰元素）的开发者使用。",2,"2026-06-11 04:08:52","CREATED_QUERY"]