API Docs
FullNormies generates full-body pixel art sprites for any Normie NFT on demand, server-side, with no dependencies. Each body is derived from that Normie's on-chain portrait width and traits (type, age, gender, facial hair) plus a deterministic seed — so wide faces get broad shoulders, Agents run tall, Young Normies get longer legs, and bearded characters get a thicker neck. Solid blocky silhouettes only; no clothing patterns carved into the body.
https://fullnormies.vercel.app/api/v1
All endpoints are CORS open (Access-Control-Allow-Origin: *) so you can call them directly from a browser, game client, or any server. Sprites are cached for 1 hour at the CDN and include ETags for cheap revalidation.
| Method | Path | Returns |
|---|---|---|
| GET | /v1/normies/{id}/full.png | Sprite PNG (40×80 px, transparent) |
| GET | /v1/normies/{id}/full-meta.json | Metadata JSON (size, anchor, poses) |
| GET | /v1/normies/{id}/sheet.png | 7-frame atlas PNG (280×80 px) |
| GET | /v1/normies/{id}/sheet.json | Atlas layout (frame indices, anchor) |
| GET | /v1/normies/{id}/face.png | 40×40 face PNG (from normies.art) |
| HEAD | /v1/normies/{id}/full-meta.json | 200 = exists, 404 = not found |
| POST | /v1/normies/full-meta | Batch metadata (up to 50 IDs) |
| Param | Values | Default | Notes |
|---|---|---|---|
| pose | stand · walk · sit · sleep | stand | Pose family |
| frame | 0 · 1 · 2 · 3 | 0 | Walk frame only. 0 = heel-strike R, 1 = passing R, 2 = heel-strike L, 3 = passing L |
All sprites face right. Flip horizontally in canvas (ctx.scale(-1,1)) or CSS (transform: scaleX(-1)) for left-facing art.
Every sprite’s anchor comes from full-meta.json or sheet.json: { x: 20, y: <per normie> } — bottom-center of the feet in the stand pose (native pixels).x is always half the 40px canvas width; y depends on torso height and leg-length seed (typically mid-50s to low-60s). Always use the meta value so characters line up on a floor regardless of build.
// Fetch meta once per normie (or use known values)
const meta = await fetch(`/api/v1/normies/${id}/full-meta.json`).then(r => r.json())
// → { pixelWidth: 40, pixelHeight: 80, anchor: { x: 20, y: <number from API> } }
// Draw at floor position (floorX, floorY) using the anchor
const img = new Image()
img.src = `/api/v1/normies/${id}/full.png?pose=stand`
img.onload = () => {
const scale = 4 // upscale 4× for crisp pixel art
ctx.imageSmoothingEnabled = false
ctx.drawImage(
img,
floorX - meta.anchor.x * scale,
floorY - meta.anchor.y * scale,
meta.pixelWidth * scale,
meta.pixelHeight * scale,
)
}Cycle frame=0 through frame=3 at ~150 ms per frame for a smooth walk loop. The 4 frames are: right heel-strike → right passing → left heel-strike → left passing.
const FRAME_MS = 150
let frame = 0
const img = new Image()
function tick() {
img.src = `/api/v1/normies/${id}/full.png?pose=walk&frame=${frame}`
frame = (frame + 1) % 4
}
tick()
setInterval(tick, FRAME_MS)
img.onload = () => {
ctx.imageSmoothingEnabled = false
ctx.clearRect(x, y, 40 * scale, 80 * scale)
ctx.drawImage(img, x, y, 40 * scale, 80 * scale)
}Load one PNG + one JSON to get all poses in a single HTTP request. The atlas is 280×80 px — 7 frames × 40 px wide.
// Layout (from /sheet.json). anchor.y is per normie — use the fetched value.
{
"frameWidth": 40,
"frameHeight": 80,
"totalFrames": 7,
"frames": {
"walk": [0, 1, 2, 3],
"stand": [4],
"sit": [5],
"sleep": [6]
},
"anchor": { "x": 20, "y": 59 }
}
// Drawing frame N from the atlas:
const frameX = frameIndex * sheet.frameWidth
ctx.drawImage(atlasImg, frameX, 0, 40, 80, destX, destY, 40 * scale, 80 * scale)Prefetch metadata for a full dorm roster in one call. Useful for sizing canvases before images arrive and for checking which normies have pixel data available.
const res = await fetch('/api/v1/normies/full-meta', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ids: [1, 42, 627, 9999] }), // max 50
})
const metas = await res.json()
// → [{ id: 1, exists: true, pixelWidth: 40, ... }, ...]Full schema, request/response examples, and caching docs.