AI-generated pixel art characters → animated sprite sheets → game-ready export
A browser-based tool that uses Gemini 2.5 Flash to generate 2D pixel art game characters and compile them into production-ready animated sprite sheets.
The pipeline generates each animation frame individually — providing the reference image on every API call — then assembles them via canvas code. This approach eliminates the consistency and direction-flipping issues common to single-prompt multi-frame generation.
Prompt → Character (PNG) → Animations (frame-by-frame) → Sprite Sheet → Export (PNG + JSON Atlas)
- Character Generation — Text-to-pixel-art via Gemini image generation with
#00FF00chroma-key background - Frame-by-Frame Animation — Each frame generated independently with reference image for style/direction consistency
- Chroma-Key BG Removal — Per-frame flood-fill with 8-directional edge detection, defringe pass, green-dominance guard
- Auto-Flip Correction — Color-pattern comparison (left/right thirds) catches AI-mirrored frames and corrects them
- Sprite Sheet Compiler — Column-gap detection → frame extraction → max-height normalization → bottom-center alignment
- Live Viewport — Canvas game engine preview with keyboard + touch controls
- Game Engine Export — PNG sprite sheet + TexturePacker-compatible JSON Atlas
src/
├── services/
│ └── ai.ts # Gemini API — character & per-frame animation generation
│ # generateSingleFrame() → removeBackground() → combineFramesIntoStrip()
├── lib/
│ ├── imageUtils.ts # Chroma-key removal: perimeter sampling → flood fill → defringe
│ └── spriteCompiler.ts # Frame extraction, auto-flip detection, normalization, sheet assembly
├── components/
│ └── GameViewport.tsx # Canvas renderer — row/col sprite indexing, keyboard/touch FSM
└── App.tsx # 3-step UI: Character → Animations → Test & Export
| Decision | Rationale |
|---|---|
| Generate 1 frame per API call | Prevents style drift, direction flip, and scale inconsistency across frames |
| Remove BG per-frame before combining | Flood fill works reliably on isolated green backgrounds; fails on combined strips |
| Max-height scaling (not average) | AI draws at same pixel scale per-row; shorter frames are pose-variation, not scale-variation |
| Color-pattern flip detection | Center-of-mass fails on symmetric characters; L/R color avg catches outfit-side flips |
| Idle generated by code | Eliminates 1 API call; avoids AI-introduced style variation in the most-viewed animation |
- Node.js ≥ 18
- Gemini API Key — Get one free
git clone https://github.com/nhatphatt/Genarate-2D-Pixels-Characters.git
cd Genarate-2D-Pixels-Characters
npm installcp .env.example .env.localEdit .env.local:
GEMINI_API_KEY=your_key_herenpm run dev # Start dev server at http://localhost:3000
npm run build # Production build
npm run lint # Type-check (tsc --noEmit)Step 1 — Character
Describe your character in natural language. The AI generates a pixel art sprite with a solid #00FF00 background.
Step 2 — Animations
Click ⚡ Auto-Gen All Missing or generate rows individually. Each animation makes 4 sequential API calls (one per frame) and displays live progress. Use 🔄 Re-Gen All to regenerate everything from scratch.
Step 3 — Test & Export Preview animations in the live canvas viewport, then download:
spritesheet.png— transparent background, losslessspritesheet.json— TexturePacker JSON Hash atlas with frame coordinates and animation tags
| Input | Action |
|---|---|
← → / A D |
Walk |
Shift + direction |
Run |
Space |
Jump |
Z |
Attack |
X |
Hurt |
| Property | Value |
|---|---|
| Frames per row | 4 |
| Layout | Grid (uniform cells) |
| Alignment | Bottom-center per cell |
| Background | Transparent (RGBA) |
| Format | PNG (lossless) |
| Idle source | Code-generated (no AI call) |
Row 0 → Idle
Row 1 → Walk
Row 2 → Run
Row 3 → Attack
Row 4 → Jump
Row 5 → Hurt
Row 6 → Death
Rows are user-configurable — add, remove, or rename animations in the UI.
Download both files from the Test & Export tab, then:
Unity
Import PNG → Texture Type: Sprite → Sprite Mode: Multiple
→ Sprite Editor → Slice → Grid By Cell Size → Apply
Godot
AnimatedSprite2D → SpriteFrames → Add from Sheet
→ select PNG → set grid dimensions → assign frames per animation
Phaser / PixiJS
// Preload
this.load.atlas('hero', 'spritesheet.png', 'spritesheet.json');
// Play animation
this.anims.create({
key: 'walk',
frames: this.anims.generateFrameNames('hero', { prefix: 'walk_', start: 0, end: 3 }),
frameRate: 8,
repeat: -1,
});GameMaker Studio
Import PNG as Sprite → set Frame Width/Height from JSON grid metadata
The chroma-key pipeline in imageUtils.ts follows this sequence:
- Perimeter sampling — detect dominant background color from image edges
- Flood fill (8-directional) from all edge pixels — removes connected background
isBgcheck: per-channel tolerance ANDg >= r && g >= b(prevents removing yellow/orange pixels)- Global strict pass — half tolerance, catches trapped green in small gaps
- Defringe pass — erodes green-dominant pixels adjacent to transparent regions
⚠️ Euclidean distance is intentionally not used — it confuses yellows and oranges with green.
Contributions are welcome. Please follow these conventions:
# Fork → clone → branch
git checkout -b feat/your-feature
# After changes
npm run lint # Must pass with no new errors
git commit -m "feat: description" # Conventional commits preferred
git push origin feat/your-feature
# Open a Pull Requestfeat: New feature
fix: Bug fix
perf: Performance improvement
refactor: Code restructure without behavior change
docs: Documentation only
| Variable | Required | Description |
|---|---|---|
GEMINI_API_KEY |
✅ | Google Gemini API key. Never commit this. |
MIT — free to use in personal and commercial projects.