Rendering Overview
The Minecraft Heads API uses three image-processing libraries to turn a 64x64
Minecraft skin texture into rendered head, body, and isometric 3D images. Each
library has a distinct role in the pipeline, and they hand off work to one
another depending on the type of render requested.
The Three Image Libraries
Sharp
Sharp is a high-performance Node.js binding
to libvips. The API uses it for:
- Pixel-region extraction -- cropping specific rectangles from the skin
texture (e.g., the 8x8 face at position (8,8)). - Nearest-neighbor resizing -- upscaling small skin regions to the requested
output size while preserving hard pixel edges (no blurring). - PNG optimization -- encoding the final buffer as a compressed PNG.
- Layer compositing -- overlaying the hat layer on top of the base head.
- Lanczos3 resampling -- downscaling the large working canvas of isometric
renders to the final output size with smooth anti-aliasing.
Sharp is the fastest of the three libraries and handles all createHeadRender
calls entirely on its own. It also performs the final resize step for isometric
renders after Canvas has done the 3D transformation work.
Jimp
Jimp is a pure-JavaScript image library
with no native dependencies. The API uses it for:
- Pixel-level manipulation -- reading individual pixels and regions from the
skin texture for body-part extraction. - Clone and crop -- creating independent copies of skin sub-regions for each
body part (head, torso, arms, legs). - Nearest-neighbor resize -- upscaling each body part to the proportionally
correct size usingJimp.RESIZE_NEAREST_NEIGHBOR. - Compositing -- layering body parts and their overlay counterparts onto a
single output canvas in the correct positions. - Horizontal flip -- mirroring the left arm/leg to create the right
arm/leg on legacy 64x32 skins that lack separate right-side textures.
Jimp handles all 2D body renders (createBodyRender) and the avatar render
(createAvatarRender). Its pure-JS nature makes it slightly slower than Sharp
but gives it the flexibility needed for multi-part compositing with per-pixel
control.
Canvas (node-canvas)
node-canvas provides a
Cairo-backed HTML5 Canvas API for Node.js. The API uses it for:
- 2D affine transforms -- skew, scale, and translate operations that project
flat skin faces onto an isometric 3D cube. - Nearest-neighbor upscaling -- scaling the skin texture to a large working
size (imageSmoothingEnabled = false) so that each skin pixel becomes a clean
block of identical pixels before transformation. - Direction flipping -- calling
ctx.scale(-1, 1)to mirror the entire
isometric render for left-facing views. - Buffer export -- writing the canvas contents to a PNG buffer that Sharp
then resizes to the final output dimensions.
Canvas is used exclusively for isometric renders (createIsometricHeadRender
and createIsometricBodyRender). Its affine transform support is essential
for the 3D projection math that Sharp and Jimp cannot do.
How They Work Together
The render pipeline varies by endpoint:
/head --> [Sharp] extract face --> resize --> composite hat --> PNG out
/player --> [Jimp] extract parts --> resize each --> composite all --> PNG out
/avatar, /ioshead, /iosbody -->
[Canvas] load skin buffer as Image
--> scale skin to working size (nearest-neighbor)
--> apply 8-9 affine transforms per cube face
--> export raw PNG buffer
[Sharp] resize raw buffer to final size (Lanczos3)
--> optimize PNG --> out
Head Render Pipeline (Sharp only)
- Download skin PNG from Mojang/GeyserMC.
sharp(skinBuffer).extract({left:8, top:8, width:8, height:8})-- crop face..resize(size, size, {kernel:'nearest'})-- upscale to target.- If hat requested: extract hat layer at (40,8), resize, composite on top.
.png().toBuffer()-- encode and return.
Body Render Pipeline (Jimp)
- Download skin PNG.
Jimp.read(skinBuffer)-- parse into a Jimp image.- For each body part:
.clone().crop(x, y, w, h).resize(...). - Composite each part onto a
new Jimp(size, size*2)output canvas. - If hat requested: extract and composite overlay for each part.
.getBufferAsync(Jimp.MIME_PNG)-- encode and return.
Isometric Render Pipeline (Canvas + Sharp)
- Download skin PNG.
- Load buffer into a
canvas.Imagevia base64 data URL. - Scale skin to working size (multiples of 120 px) with nearest-neighbor.
- Create a working canvas (approx. 2.175x the side length for heads, 2.5x5.1x
for bodies). - Apply 8 affine transforms (head) or 20+ transforms (body) to draw each cube
face. - If direction is
left: pre-applyctx.scale(-1, 1)to mirror everything. - Export the working canvas to a PNG buffer.
sharp(rawBuffer).resize(size, size, {kernel:'lanczos3'}).png()-- final
downscale with smooth resampling.
Render Functions Summary
| Function | Library | Input | Output |
|---|---|---|---|
createHeadRender |
Sharp | skinUrl, size, hat | PNG buffer (size x size) |
createAvatarRender |
Jimp | skinUrl, size | PNG buffer (size x size) |
createBodyRender |
Jimp | skinUrl, size, hat | PNG buffer (size x size*2) |
createIsometricHeadRender |
Canvas + Sharp | skinUrl, size, direction | PNG buffer (size x size) |
createIsometricBodyRender |
Canvas + Sharp | skinUrl, size, direction | PNG buffer (size x size*ratio) |
getRawSkin |
Axios only | skinUrl | Raw skin PNG buffer |
Source Files
All rendering logic lives in utils/imageProcessor.js. Route handlers inroutes/ call these functions after resolving the player profile and checking
the cache.