A web app for generating 256x256 pixel art avatars from photos using OpenAI's gpt-image-1 model. Built for Layercode team members to create consistent, stylized profile pictures.
- Framework: Next.js 16 with App Router
- Language: TypeScript
- Styling: Tailwind CSS 4 with macOS Aqua theme
- Storage: Browser localStorage (no server database)
- AI: OpenAI API (gpt-image-1)
- Image Processing: Sharp
- Deployment: Vercel
- Upload photo (human or pet)
- AI generates 6 pixel art variants using style reference
- User picks favorite, assigns to team member
- All data persists in browser localStorage
| Path | Purpose |
|---|---|
app/page.tsx |
Main generator UI |
app/history/page.tsx |
Generation history browser |
app/team/page.tsx |
Team member management |
app/api/generate/route.ts |
Avatar generation endpoint (OpenAI) |
lib/storage.ts |
localStorage helpers + default prompts |
lib/openai.ts |
OpenAI image generation |
public/style-reference.png |
Style reference image |
team_members- Team info + official avatarprompts- Saved generation prompts (defaults bundled)generations- Generation recordsvariants- 6 variants per generation
npm install
npm run devLive URL: https://avatar-app-tau.vercel.app
Deployed on Vercel (Layercode team). Auto-deploys on push to main.
Vercel Dashboard: https://vercel.com/layercode-5ca82bb0/avatar-app
- Always add backlog items to Linear when the user mentions a feature idea or task and says to add it to the backlog
- Use the Linear GraphQL API to create issues in this project
- Periodically review and categorize tasks in Linear based on:
- Estimated time to implement (quick fix, medium, large)
- Complexity (simple, moderate, complex)
- Priority (P1 urgent, P2 high, P3 medium, P4 low)
- Rewrite unclear tasks when you see tasks added with notes that could be clearer
- Update the task title and description in Linear to capture the full intent
- Ask for clarification before starting a task if you need more information to do the best job possible
- Don't assume - it's better to ask and get it right the first time
After every commit that the user asks for:
-
Increment the app version (patch): Update
versioninpackage.json(e.g., 0.1.0 → 0.1.1) -
Post a Linear project update:
curl -s -X POST https://api.linear.app/graphql \ -H "Content-Type: application/json" \ -H "Authorization: $LINEAR_API_KEY" \ -d '{ "query": "mutation CreateProjectUpdate($projectId: String!, $body: String!) { projectUpdateCreate(input: { projectId: $projectId, body: $body }) { success } }", "variables": { "projectId": "249727f6-1055-45b9-9591-eae594dfe3a4", "body": "## v<VERSION> - <TITLE>\n\n### Changes\n- <bullet points of changes>" } }'
-
Mark related Linear issues as Done if the commit implements a tracked issue:
curl -s -X POST https://api.linear.app/graphql \ -H "Content-Type: application/json" \ -H "Authorization: $LINEAR_API_KEY" \ -d '{ "query": "mutation UpdateIssue($issueId: String!, $stateId: String!) { issueUpdate(id: $issueId, input: { stateId: $stateId }) { success } }", "variables": { "issueId": "<ISSUE_ID>", "stateId": "ab1941f6-466d-46b9-8505-10625c97efa6" } }'
To find issues:
curl -s -X POST https://api.linear.app/graphql \ -H "Content-Type: application/json" \ -H "Authorization: $LINEAR_API_KEY" \ -d '{"query": "{ project(id: \"249727f6-1055-45b9-9591-eae594dfe3a4\") { issues { nodes { id title identifier state { name } } } } }"}'
-
Commit the version bump separately
- Wait until all tasks are done before committing when the user asks to complete multiple tasks at once
- Only increment the version number once after all tasks are complete
- Include all changes in a single project update
- Include Linear issue ID in commit messages when implementing a tracked issue
- Format:
LAY-XX: Brief description of change - Example:
LAY-12: Add pet avatar mode - Linear automatically links commits to issues
- When posting project updates, include not just what changed but why
- Document decisions made and trade-offs considered
- Create separate Linear issues for bugs discovered during other work
- Don't fix bugs silently - log them as issues first
- Add comments to Linear issues during implementation with:
- Key files modified
- Architectural decisions made
- Gotchas or things to watch out for
- Add a closing comment when marking issues Done summarizing:
- What was actually implemented
- Any follow-up work needed
- Testing performed
Tag issues by area:
generator- Core avatar generationui- Frontend/stylingapi- Backend endpointsstorage- localStorage/datateam- Team member featureshistory- History/search features
| Setting | Value |
|---|---|
| API Key | $LINEAR_API_KEY in .env.local |
| Project ID | 249727f6-1055-45b9-9591-eae594dfe3a4 |
| Team ID | eff783c0-7479-4001-8002-6c2aede5f1ca |
| Team Key | LAY (issues are LAY-XX) |
| Done State ID | ab1941f6-466d-46b9-8505-10625c97efa6 |
avatar-app/
├── app/
│ ├── api/
│ │ └── generate/route.ts
│ ├── history/page.tsx
│ ├── team/page.tsx
│ ├── layout.tsx
│ ├── page.tsx
│ └── globals.css
├── components/
│ ├── ColorPicker.tsx
│ ├── GenerationCard.tsx
│ ├── GridOverlay.tsx
│ ├── ImageUpload.tsx
│ ├── Navigation.tsx
│ ├── PromptEditor.tsx
│ └── VariantGrid.tsx
├── lib/
│ ├── openai.ts
│ └── storage.ts
├── public/
│ └── style-reference.png
└── package.json