A Spring AI powered agent for generating detailed pixel art and 2D sprite descriptions with full abstraction for AI models and mock support for local development.
- ๐ฏ Quick Start
- ๐ Key Features
- ๐ Prerequisites
- โ๏ธ Configuration
- ๐๏ธ Model Abstraction
- ๐ Configuration Reference
- ๐ฎ API Endpoints
- ๐ Usage Examples
- ๐๏ธ Architecture
- ๐จ Supported Assets & Styles
- ๐ผ๏ธ Image Generation
- ๐ ๏ธ Development
- ๐ Troubleshooting
- ๐ Deployment
- ๐ค Contributing
- ๐ License
Run locally with mock models (no external dependencies):
./mvnw spring-boot:runTest the API:
curl -X POST http://localhost:8080/api/pixelart/generate \
-H "Content-Type: application/json" \
-d '{
"assetType": "character",
"description": "a warrior with sword",
"style": "16-bit",
"colorPalette": "vibrant",
"size": "32x32"
}'- โ Run Offline - Mock models enabled by default, no external dependencies needed
- ๐ค AI-Powered - Uses Spring AI with Ollama (Qwen 2.5:3b) for intelligent descriptions
- ๐ผ๏ธ Image Generation - Stable Diffusion integration for pixel art sprite creation
- โ๏ธ Configuration-Driven - Switch between mock and real models with a single property
- ๐๏ธ Spritesheet Support - Generate multi-frame animations automatically
- ๐ฎ Game Assets - Characters, weapons, tiles, enemies, items, UI elements
- ๐จ Art Styles - 8-bit, 16-bit, 32-bit, isometric, retro game styles
- ๐งช Fully Testable - Mock providers for instant testing
- ๐ Technical Specs - Color palettes, dimensions, animation suggestions
- ๐ Refinement - Iteratively improve designs with feedback
- Java 17 or higher
- Maven 3.6+
- Optional: Docker (only needed for real Ollama and Stable Diffusion)
- Optional: 16 GB RAM (only for running actual AI services)
By default, the app runs with mock providers that require no external dependencies:
./mvnw spring-boot:runConfiguration:
# In application.properties (default)
pixelart.models.use-mock=trueMock mode features:
- โ Instant responses (milliseconds)
- โ No Ollama or Stable Diffusion needed
- โ Realistic cycling responses (warrior โ chest โ potion)
- โ Perfect for development and testing
To use actual AI models, update your configuration:
# In application.properties
pixelart.models.use-mock=false
pixelart.chat-model.mock-responses-enabled=false
pixelart.image-model.mock-responses-enabled=falseThen start the AI services:
# Terminal 1: Start Ollama
docker run -d --name ollama -p 11434:11434 -v ollama-data:/root/.ollama ollama/ollama
docker exec ollama ollama pull qwen2.5:3b
# Terminal 2: Start Stable Diffusion
docker run -d --name stable-diffusion -p 7860:7860 \
-v sd-models:/data/StableDiffusion \
-e CLI_ARGS="--api --listen --port 7860 --no-half --precision full" \
ghcr.io/neggles/sd-webui-docker:main
# Terminal 3: Start the app
./mvnw spring-boot:runpixelart.models.use-mock=false
pixelart.chat-model.mock-responses-enabled=true # Uses mock chat
pixelart.image-model.mock-responses-enabled=false # Uses real imagesIn application.properties:
pixelart.mock.chat-responses[0]=Your custom warrior description
pixelart.mock.chat-responses[1]=Your custom chest description
pixelart.mock.chat-responses[2]=Your custom potion descriptionOr in application.yml:
pixelart:
mock:
chat-responses:
0: "Custom warrior response"
1: "Custom chest response"
2: "Custom potion response"The project now uses a clean abstraction layer for AI models:
| Model | Purpose | Real Provider | Mock Provider |
|---|---|---|---|
| Chat | Generates pixel art descriptions | OllamaModelProvider |
MockModelProvider |
| Image | Generates pixel art images | StableDiffusionImageProvider |
MockImageProvider |
// Chat model interface
public interface ModelProvider {
String generateResponse(String prompt);
boolean isAvailable();
}
// Image generation interface
public interface ImageProvider {
String generateImage(String prompt, int width, int height);
String generateImage(String prompt, int width, int height, boolean isSpritesheet, int frameCount);
boolean isAvailable();
}The ModelConfig bean automatically selects the correct providers:
@Configuration
public class ModelConfig {
@Bean
public ModelProvider modelProvider(/* ... */) {
if (properties.models.useMock()) {
return new MockModelProvider(mockConfig);
}
return new OllamaModelProvider(chatClient);
}
@Bean
public ImageProvider imageProvider(/* ... */) {
if (properties.models.useMock()) {
return new MockImageProvider();
}
return new StableDiffusionImageProvider();
}
}Services now depend on abstractions, not concrete implementations:
@Service
public class PixelArtAgentService {
private final ModelProvider modelProvider;
public PixelArtAgentService(ModelProvider modelProvider) {
this.modelProvider = modelProvider;
}
public PixelArtResponse generatePixelArt(String prompt) {
// Uses either real or mock provider based on config
String description = modelProvider.generateResponse(prompt);
return new PixelArtResponse(description);
}
}# Server
server.port=8080
# Model Configuration (REFACTORED)
pixelart.models.use-mock=true
pixelart.chat-model.mock-responses-enabled=true
pixelart.image-model.mock-responses-enabled=true
# Ollama Settings
spring.ai.ollama.base-url=http://localhost:11434
spring.ai.ollama.chat.options.model=qwen2.5:3b
spring.ai.ollama.chat.options.temperature=0.8
spring.ai.ollama.chat.options.top-p=0.9
# Image Generation
pixelart.image.generation.enabled=true
pixelart.image.generation.api-url=http://localhost:7860
pixelart.image.generation.model=stable-diffusion
pixelart.image.generation.lora=
pixelart.image.generation.lora-strength=0.8
# Mock Response Configuration
pixelart.mock.chat-responses[0]=Realistic warrior description...
pixelart.mock.chat-responses[1]=Realistic chest description...
pixelart.mock.chat-responses[2]=Realistic potion description...server:
port: 8080
pixelart:
models:
use-mock: true # Master toggle for mock mode
chat-model:
mock-responses-enabled: true # Override for chat model only
image-model:
mock-responses-enabled: true # Override for image model only
mock:
chat-responses:
0: "Warrior description..."
1: "Chest description..."
2: "Potion description..."
spring:
ai:
ollama:
base-url: http://localhost:11434
chat:
options:
model: qwen2.5:3b
temperature: 0.8POST /api/pixelart/generate
Generate a complete pixel art specification with actual sprite image.
{
"assetType": "CHARACTER",
"description": "brave knight with sword and shield",
"style": "16-bit",
"colorPalette": "medieval",
"size": "32x32",
"additionalContext": "animation frames for walk cycle"
}Response:
{
"detailedDescription": "### Character Asset Specification\n\n...",
"suggestedColors": ["#2C3E50", "#E74C3C", "#ECF0F1", "#3498DB"],
"specifications": {
"size": "32x32",
"assetType": "CHARACTER",
"frameCount": 4,
"orientation": "front-facing",
"layers": ["background", "base", "details", "highlights"]
},
"animationSuggestions": ["idle", "walk"],
"style": "16-bit",
"imageData": "iVBORw0KGgoAAAANSUhEUgAA...",
"imageStatus": "spritesheet-generated",
"generatedAt": "2025-11-22T14:00:00"
}POST /api/pixelart/generate/image
Generate only the sprite image without description.
Returns: Binary PNG image file
POST /api/pixelart/generate/variations?count=3
Generate multiple variations of the same concept.
POST /api/pixelart/refine?feedback=make+it+more+colorful
Refine an existing design based on feedback.
GET /api/pixelart/health
Check if all services are running and available.
curl -X POST http://localhost:8080/api/pixelart/generate \
-H "Content-Type: application/json" \
-d '{
"assetType": "CHARACTER",
"description": "brave knight with sword and shield",
"style": "16-bit",
"colorPalette": "medieval",
"size": "32x32",
"additionalContext": "idle and attack animations"
}'$body = @{
assetType = "ITEM"
description = "magical fire sword with flames"
style = "16-bit"
colorPalette = "vibrant"
size = "32x16"
additionalContext = "glowing effect"
} | ConvertTo-Json
$response = Invoke-RestMethod -Method Post `
-Uri "http://localhost:8080/api/pixelart/generate" `
-ContentType "application/json" -Body $body
# Save the generated image
$imageBytes = [Convert]::FromBase64String($response.imageData)
[IO.File]::WriteAllBytes("fire-sword.png", $imageBytes){
"assetType": "ENEMY",
"description": "cute bouncing slime monster",
"style": "8-bit",
"colorPalette": "pastel",
"size": "16x16",
"additionalContext": "bounce and damage animation frames"
}{
"assetType": "ITEM",
"description": "red health potion in glass bottle",
"style": "16-bit",
"colorPalette": "vibrant",
"size": "16x16",
"additionalContext": "sparkle effect"
}{
"assetType": "TILE",
"description": "grass ground tile with flowers",
"style": "16-bit",
"colorPalette": "natural",
"size": "32x32",
"additionalContext": "seamless tiling"
}{
"assetType": "ENEMY",
"description": "fierce red dragon boss with wings",
"style": "32-bit",
"colorPalette": "vibrant",
"size": "64x64",
"additionalContext": "idle, fly, and breathe fire animations"
}{
"assetType": "CHARACTER",
"description": "elderly wizard with staff and pointed hat",
"style": "16-bit",
"colorPalette": "mystical",
"size": "32x32",
"additionalContext": "casting spell animation"
}{
"assetType": "WEAPON",
"description": "futuristic laser gun with neon accents",
"style": "16-bit",
"colorPalette": "neon",
"size": "24x12",
"additionalContext": "sci-fi theme with glowing elements"
}โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ Client โโโโโถโ REST Controller โโโโโถโ Agent Service โ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโ
โผ โผ
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ Spring AI โ โ Image Generation โ
โ ChatClient โ โ Service โ
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ โ
โผ โผ
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ Ollama Server โ โ Stable Diffusion โ
โ (Qwen 2.5:3b) โ โ WebUI API โ
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
See Architecture Documentation for detailed diagrams and explanation.
| Asset Type | Description | Typical Size | Examples |
|---|---|---|---|
| CHARACTER | Player characters, NPCs | 32x32, 64x64 | Knight, wizard, hero |
| ENEMY | Enemy sprites, bosses | 16x16, 32x32, 64x64 | Slime, dragon, skeleton |
| WEAPON | Swords, guns, items | 16x16, 32x16 | Sword, bow, gun |
| TILE | Ground, walls | 16x16, 32x32 | Grass, stone, water |
| ITEM | Collectibles, consumables | 8x8, 16x16 | Coins, potions, keys |
| UI | UI elements, icons | 16x16, 24x24 | Buttons, hearts, stars |
| EFFECT | Visual effects | 16x16, 32x32 | Explosion, sparkle, smoke |
- 8-bit - Classic NES-style (limited colors, chunky pixels)
- 16-bit - SNES/Genesis era (more colors, smoother)
- 32-bit - Advanced pixel art (detailed sprites)
- isometric - 3D-looking isometric perspective
- retro - Generic retro game style
- pixel-art - Modern pixel art style
- Text Generation: Ollama (Qwen 2.5:3b) generates detailed pixel art specifications
- Prompt Enhancement: System adds strong pixel art style markers and constraints
- Image Generation: Stable Diffusion creates the actual sprite image
- Spritesheet Detection: Automatically creates multi-frame layouts for animations
- Response Assembly: Combines description with base64-encoded PNG image
- Steps: 50 (higher for better style adherence)
- CFG Scale: 15 (maximum prompt guidance)
- Sampler: Euler a (good for pixel art)
- Resolution: 8x upscaled, then downscaled for pixel effect
- Negative Prompts: Excludes blur, gradients, anti-aliasing
# PowerShell example
$response = Invoke-RestMethod -Uri "http://localhost:8080/api/pixelart/generate" `
-Method POST -ContentType "application/json" -Body $json
if ($response.imageStatus -eq "generated" -or $response.imageStatus -eq "spritesheet-generated") {
$imageBytes = [Convert]::FromBase64String($response.imageData)
[IO.File]::WriteAllBytes("sprite.png", $imageBytes)
Write-Host "Image saved to sprite.png"
}<!-- HTML example -->
<img src="data:image/png;base64,{{imageData}}" alt="Generated Sprite" /># Enable/disable image generation
pixelart.image.generation.enabled=true
# Stable Diffusion API URL
pixelart.image.generation.api-url=http://localhost:7860
# Model identifier
pixelart.image.generation.model=stable-diffusionpixel-art-agent/
โโโ docs/ # Documentation
โ โโโ ARCHITECTURE.html # Interactive architecture doc
โ โโโ Pixel-Art-Agent-Architecture.pdf
โโโ scripts/ # Utility scripts
โ โโโ setup-github.ps1 # GitHub setup
โ โโโ setup-pixel-art-model.ps1 # Download pixel art LoRA
โ โโโ start-stable-diffusion.bat # Start SD container
โ โโโ start.bat # Start application
โ โโโ test-api.ps1 # Test API endpoints
โโโ src/
โ โโโ main/
โ โ โโโ java/com/pixelart/agent/
โ โ โ โโโ PixelArtAgentApplication.java
โ โ โ โโโ config/
โ โ โ โ โโโ ChatClientConfig.java
โ โ โ โ โโโ PixelArtProperties.java
โ โ โ โโโ controller/
โ โ โ โ โโโ PixelArtController.java
โ โ โ โโโ model/
โ โ โ โ โโโ PixelArtRequest.java
โ โ โ โ โโโ PixelArtResponse.java
โ โ โ โโโ service/
โ โ โ โโโ ImageGenerationService.java
โ โ โ โโโ PixelArtAgentService.java
โ โ โโโ resources/
โ โ โโโ application.properties
โ โโโ test/
โโโ pom.xml
โโโ README.md
- Spring Boot 3.3.5 - Application framework
- Spring AI 1.0.0-M3 - AI integration framework
- Ollama - Local LLM runtime (Qwen 2.5:3b, 1.9 GB)
- Stable Diffusion - Image generation (v1.5 model, 4 GB)
- Apache HttpClient 5 - HTTP communication
- Lombok - Reduce boilerplate code
- Maven - Build tool
# Clean build
mvn clean install
# Skip tests
mvn clean install -DskipTests
# Run in development mode
mvn spring-boot:runSymptom: Connection refused: http://localhost:11434
Solution:
# Check if Ollama is running
docker ps | grep ollama
# Start Ollama if not running
docker start ollama
# Check port mapping
docker port ollamaSymptom: imageStatus: "text-only", 70-byte images
Solution:
# Check Stable Diffusion logs
docker logs stable-diffusion --tail 50
# Verify API is accessible
curl http://localhost:7860/sdapi/v1/sd-models
# Restart with correct flags
.\scripts\start-stable-diffusion.batSymptom: Error: model 'qwen2.5:3b' not found
Solution:
# Pull the model
docker exec ollama ollama pull qwen2.5:3b
# Verify it's available
docker exec ollama ollama listSymptom: Container crashes or system freezes
Solution:
- Increase Docker memory limit (Settings โ Resources โ Memory)
- Close unnecessary applications
- Consider using smaller Qwen model (1.8b instead of 3b)
- Use GPU acceleration if available
Symptom: Takes 30-60 seconds to generate images
Solution:
- This is normal for CPU mode
- For faster generation (2-5 seconds), use GPU:
docker run -d --gpus all --name stable-diffusion ...
- Or disable image generation:
pixelart.image.generation.enabled=false
Symptom: Port 8080 already in use
Solution:
# Change port in application.properties
server.port=8081version: '3.8'
services:
ollama:
image: ollama/ollama
ports:
- "11434:11434"
volumes:
- ollama-data:/root/.ollama
stable-diffusion:
image: ghcr.io/neggles/sd-webui-docker:main
ports:
- "7860:7860"
volumes:
- sd-models:/data/StableDiffusion
environment:
- CLI_ARGS=--api --listen --port 7860 --no-half --precision full --disable-nan-check
pixel-art-agent:
build: .
ports:
- "8080:8080"
depends_on:
- ollama
- stable-diffusion
environment:
- SPRING_AI_OLLAMA_BASE_URL=http://ollama:11434
- PIXELART_IMAGE_GENERATION_API_URL=http://stable-diffusion:7860
volumes:
ollama-data:
sd-models:| Component | RAM | Storage | CPU |
|---|---|---|---|
| Spring Boot App | 1-2 GB | 100 MB | 2 cores |
| Ollama + Qwen 2.5:3b | 4-6 GB | 2 GB | 4 cores |
| Stable Diffusion | 8-16 GB | 6-8 GB | 8+ cores |
| Total Minimum | 16 GB | 10 GB | 8 cores |
With GPU: 12 GB VRAM recommended for optimal Stable Diffusion performance
Contributions are welcome! Areas for improvement:
- ๐จ Additional art styles and templates
- ๐ผ๏ธ Alternative image generation models
- ๐ More comprehensive documentation
- ๐ Bug fixes and performance improvements
- ๐งช Test coverage
- ๐ Additional LLM model support
- ๐ฎ Game engine integrations (Unity, Godot)
- Follow Java conventions
- Use Lombok annotations where appropriate
- Add JavaDoc comments for public methods
- Keep methods focused and concise
- Write meaningful commit messages
- Test thoroughly before submitting
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes
- Test thoroughly
- Commit with clear messages:
git commit -m 'Add amazing feature' - Push to your fork:
git push origin feature/amazing-feature - Open a Pull Request
MIT License - see LICENSE file for details
- Ollama for local LLM inference
- Qwen Team for the excellent Qwen 2.5 model
- AUTOMATIC1111 for Stable Diffusion WebUI
- Spring AI Team for the AI integration framework
- Pixel art community for inspiration and feedback
- ๐ Architecture Documentation
- ๐ Report Issues
- ๐ฌ Discussions
Made with โค๏ธ for game developers and pixel art enthusiasts