Composite Sprites

Build 32×32 characters from reusable 16×16 parts. Mix and match heads, bodies, and weapons without redrawing anything.

How it works

head L16×16head R16×16body L16×16body R16×1632×32composite sprite

Four small tiles (easy to draw well) assemble into one large character. The parts are reusable — a wizard and a knight can share the same boots.

Why composites?

Note

Drawing a 32×32 character in a text grid means placing 1024 pixels correctly. But four 16×16 tiles? Each one is only 256 pixels — much easier to get right. And each part is reusable across characters.

Defining a composite

Create 16×16 tiles for each body part, then assemble:

[tile.knight_head_l]
palette = "hero"
size = "16x16"
grid = '''
...your head art...
'''

[composite.knight]
size = "32x32"
tile_size = "16x16"
layout = """
knight_head_l    knight_head_l!h
knight_body_l    knight_body_r
"""

The !h suffix flips the tile horizontally — draw one side of a face, flip it for the other. Perfect seam alignment guaranteed.

Variants

Swap parts without redefining the whole character:

[composite.knight.variant.shield]
slot = { "1_0" = "knight_shield_body" }

This replaces just the bottom-left tile. Everything else stays the same. Great for equipment changes, damage states, or color swaps.

Animation

Walk cycles only change the tiles that move:

[composite.knight.anim.walk]
fps = 8
loop = true

[[composite.knight.anim.walk.frame]]
index = 1
# base layout — no changes

[[composite.knight.anim.walk.frame]]
index = 2
swap = { "1_0" = "knight_walk2_l", "1_1" = "knight_walk2_r" }

Frame 1 uses the base. Frame 2 only swaps the leg tiles. Head and torso stay put — no wasted data.

Per-tile offsets

Add bounce effects without changing the tile art:

[composite.knight.offset]
"0_0" = [0, -1]    # head bobs up 1px during walk

Seam checking

PIXL validates that adjacent tiles line up at the boundary:

pixl validate tileset.pax --check-seams
Tip

Use the symmetry shortcut for symmetric characters: knight_head_l knight_head_l!h guarantees the left and right halves match perfectly at the seam.

Rendering

pixl render-composite tileset.pax \
  --composite knight \
  --variant shield \
  --scale 4 \
  --out knight_shield.png

Atlas packing

Composites get their own atlas with every variant and animation frame as separate entries:

knight           → base layout
knight:shield    → shield variant
knight:walk:1    → walk frame 1
knight:walk:2    → walk frame 2

Each has its own coordinates in the TexturePacker JSON metadata.