diff --git a/package-lock.json b/package-lock.json
index 322930a..f2928f1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -954,9 +954,9 @@
       "license": "BSD-3-Clause"
     },
     "node_modules/bullmq": {
-      "version": "5.34.3",
-      "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.34.3.tgz",
-      "integrity": "sha512-S8/V11w7p6jYAGvv+00skLza/4inTOupWPe0uCD8mZSUiYKzvmW4/YEB+KVjZI2CC2oD3KJ3t7/KkUd31MxMig==",
+      "version": "5.34.4",
+      "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.34.4.tgz",
+      "integrity": "sha512-FPTN5eqsYO5/Blm6vh/bVJ0eADrVLjTBDjMkPCqZN3xx/5GqHov9NwxM6A6M3m6n2Vg+gNAoN98t7bTij5/UEA==",
       "license": "MIT",
       "dependencies": {
         "cron-parser": "^4.6.0",
diff --git a/src/http/avatar.ts b/src/http/avatar.ts
new file mode 100644
index 0000000..878acaf
--- /dev/null
+++ b/src/http/avatar.ts
@@ -0,0 +1,73 @@
+/**
+ * Avatar generator API routes
+ */
+import { Router, Request, Response } from 'express'
+import sharp from 'sharp'
+import fs from 'fs'
+import CharacterRepository from '../repositories/characterRepository'
+import CharacterHairRepository from '../repositories/characterHairRepository'
+import CharacterTypeRepository from '../repositories/characterTypeRepository'
+import { getPublicPath } from '../utilities/storage'
+
+const router = Router()
+
+interface AvatarOptions {
+  characterTypeId: number
+  characterHairId?: number
+}
+
+async function generateAvatar(res: Response, options: AvatarOptions) {
+  try {
+    const characterType = await CharacterTypeRepository.getById(options.characterTypeId)
+    if (!characterType?.spriteId) {
+      return res.status(404).json({ message: 'Character type not found' })
+    }
+
+    const bodySpritePath = getPublicPath('sprites', characterType.spriteId, 'idle_right_down.png')
+    if (!fs.existsSync(bodySpritePath)) {
+      console.error(`Body sprite file not found: ${bodySpritePath}`)
+      return res.status(404).json({ message: 'Body sprite file not found' })
+    }
+
+    let avatar = sharp(bodySpritePath)
+
+    if (options.characterHairId) {
+      const characterHair = await CharacterHairRepository.getById(options.characterHairId)
+      if (characterHair?.spriteId) {
+        const hairSpritePath = getPublicPath('sprites', characterHair.spriteId, 'front.png')
+        if (fs.existsSync(hairSpritePath)) {
+          avatar = avatar.composite([{ input: hairSpritePath, gravity: 'north' }])
+        } else {
+          console.error(`Hair sprite file not found: ${hairSpritePath}`)
+        }
+      }
+    }
+
+    res.setHeader('Content-Type', 'image/png')
+    return avatar.pipe(res)
+  } catch (error) {
+    console.error('Error generating avatar:', error)
+    return res.status(500).json({ message: 'Error generating avatar' })
+  }
+}
+
+router.get('/avatar/:characterName', async (req: Request, res: Response) => {
+  const character = await CharacterRepository.getByName(req.params.characterName)
+  if (!character?.characterType) {
+    return res.status(404).json({ message: 'Character or character type not found' })
+  }
+
+  return generateAvatar(res, {
+    characterTypeId: character.characterType.id,
+    characterHairId: character.characterHair?.id
+  })
+})
+
+router.get('/avatar/s/:characterTypeId/:characterHairId?', async (req: Request, res: Response) => {
+  return generateAvatar(res, {
+    characterTypeId: parseInt(req.params.characterTypeId),
+    characterHairId: req.params.characterHairId ? parseInt(req.params.characterHairId) : undefined
+  })
+})
+
+export default router
\ No newline at end of file
diff --git a/src/repositories/characterHairRepository.ts b/src/repositories/characterHairRepository.ts
index da42794..1790f43 100644
--- a/src/repositories/characterHairRepository.ts
+++ b/src/repositories/characterHairRepository.ts
@@ -12,6 +12,11 @@ class CharacterHairRepository {
       }
     })
   }
+  async getById(id: number): Promise<CharacterHair | null> {
+    return prisma.characterHair.findUnique({
+      where: { id }
+    })
+  }
 }
 
 export default new CharacterHairRepository()
diff --git a/src/repositories/characterTypeRepository.ts b/src/repositories/characterTypeRepository.ts
index 86ea075..69d66f5 100644
--- a/src/repositories/characterTypeRepository.ts
+++ b/src/repositories/characterTypeRepository.ts
@@ -3,7 +3,19 @@ import { CharacterType } from '@prisma/client'
 
 class CharacterTypeRepository {
   async getAll(): Promise<CharacterType[]> {
-    return prisma.characterType.findMany()
+    return prisma.characterType.findMany({
+      include: {
+        sprite: true
+      }
+    })
+  }
+  async getById(id: number): Promise<CharacterType | null> {
+    return prisma.characterType.findUnique({
+      where: { id },
+      include: {
+        sprite: true
+      }
+    })
   }
 }