Composition

A composition layers multiple sprites onto a canvas. This is useful for building scenes, tile maps, and complex images from smaller sprite components.

Basic Syntax

{
  type: "composition",
  name: "scene",
  size: [width, height],
  layers: [
    { sprite: "background", x: 0, y: 0 },
    { sprite: "hero", x: 16, y: 16 },
  ],
}

Fields

FieldRequiredDefaultDescription
typeYes-Must be "composition"
nameYes-Unique identifier
sizeYes-Canvas size [width, height] in pixels
layersYes-Array of layers, rendered bottom-to-top

Layer Fields

FieldRequiredDescription
spriteYesSprite name to place
xNoX position (default: 0)
yNoY position (default: 0)
blendNoBlend mode (default: "normal")
opacityNoLayer opacity 0.0-1.0 (default: 1.0)

Simple Example

{
  type: "palette",
  name: "scene",
  colors: {
    _: "transparent",
    g: "#228B22",
    h: "#FFD700",
    t: "#8B4513",
  },
}

{
  type: "sprite",
  name: "grass",
  size: [8, 8],
  palette: "scene",
  regions: { g: { rect: [0, 0, 8, 8] } },
}

{
  type: "sprite",
  name: "hero",
  size: [8, 8],
  palette: "scene",
  regions: {
    h: { rect: [2, 2, 4, 6] },
  },
}

{
  type: "sprite",
  name: "tree",
  size: [8, 8],
  palette: "scene",
  regions: {
    g: { ellipse: [4, 2, 3, 2] },
    t: { rect: [3, 4, 2, 4] },
  },
}

{
  type: "composition",
  name: "scene",
  size: [32, 32],
  layers: [
    { sprite: "grass", x: 0, y: 0 },
    { sprite: "grass", x: 8, y: 0 },
    { sprite: "grass", x: 16, y: 0 },
    { sprite: "grass", x: 24, y: 0 },
    { sprite: "tree", x: 4, y: 8 },
    { sprite: "hero", x: 12, y: 16 },
    { sprite: "tree", x: 20, y: 8 },
  ],
}

Multiple Layers

Layers are rendered bottom-to-top. The first layer is the background:

{
  type: "composition",
  name: "scene",
  size: [64, 64],
  layers: [
    // Background layer
    { sprite: "sky_background", x: 0, y: 0 },
    // Middle layer - ground
    { sprite: "ground", x: 0, y: 48 },
    // Foreground - characters
    { sprite: "hero", x: 24, y: 32 },
    { sprite: "enemy", x: 40, y: 32 },
  ],
}

Blend Modes

Layer blending controls how colors combine when layers overlap:

{
  type: "composition",
  name: "effects",
  size: [32, 32],
  layers: [
    { sprite: "background", x: 0, y: 0 },
    { sprite: "shadow", x: 8, y: 8, blend: "multiply", opacity: 0.5 },
    { sprite: "glow", x: 12, y: 4, blend: "add" },
  ],
}

Blend Mode Reference

ModeDescription
normalStandard alpha compositing
multiplyDarkens - good for shadows
screenLightens - good for glows
overlayEnhances contrast
addAdditive blending - fire, particles
subtractSubtractive blending
differenceAbsolute difference
darkenKeeps darker color
lightenKeeps lighter color

When to Use Each Mode

multiply - Shadows, tinting, darkening

{ sprite: "shadow", x: 4, y: 4, blend: "multiply", opacity: 0.3 }

screen - Glows, highlights, fog

{ sprite: "glow", x: 8, y: 8, blend: "screen" }

add - Fire, particles, magical effects

{ sprite: "particles", x: 0, y: 0, blend: "add" }

CSS Variables in Compositions

Composition layers support CSS variable references:

{
  type: "composition",
  name: "themed_scene",
  size: [32, 32],
  layers: [
    { sprite: "background", x: 0, y: 0 },
    { sprite: "shadow", x: 4, y: 4, blend: "var(--shadow-blend)", opacity: "var(--shadow-opacity)" },
    { sprite: "glow", x: 8, y: 8, blend: "var(--glow-mode, add)" },
  ],
}

Variables are resolved from the palette's variable registry at render time.

Nested Compositions

Compositions can reference other compositions, enabling hierarchical scene construction:

// Forest tile composition
{
  type: "composition",
  name: "forest_tile",
  size: [16, 16],
  layers: [
    { sprite: "grass", x: 0, y: 0 },
    { sprite: "tree", x: 4, y: 0 },
  ],
}

// Main scene using forest_tile
{
  type: "composition",
  name: "scene",
  size: [32, 32],
  layers: [
    { sprite: "forest_tile", x: 0, y: 0 },
    { sprite: "forest_tile", x: 16, y: 0 },
    { sprite: "hero", x: 12, y: 16 },
  ],
}

Cycle Detection

Circular references are detected and produce an error:

// ERROR: Cycle detected
{ type: "composition", name: "A", layers: [{ sprite: "B" }] }
{ type: "composition", name: "B", layers: [{ sprite: "A" }] }

Complete Example

// Palette
{
  type: "palette",
  name: "island",
  colors: {
    _: "transparent",
    grass: "#228B22",
    water: "#4169E1",
    sand: "#F4A460",
  },
}

// Tile sprites
{
  type: "sprite",
  name: "grass_tile",
  size: [8, 8],
  palette: "island",
  regions: { grass: { rect: [0, 0, 8, 8] } },
}

{
  type: "sprite",
  name: "water_tile",
  size: [8, 8],
  palette: "island",
  regions: { water: { rect: [0, 0, 8, 8] } },
}

{
  type: "sprite",
  name: "sand_tile",
  size: [8, 8],
  palette: "island",
  regions: { sand: { rect: [0, 0, 8, 8] } },
}

// Island composition
{
  type: "composition",
  name: "island",
  size: [32, 32],
  layers: [
    // Water background
    { sprite: "water_tile", x: 0, y: 0 },
    { sprite: "water_tile", x: 8, y: 0 },
    { sprite: "water_tile", x: 16, y: 0 },
    { sprite: "water_tile", x: 24, y: 0 },
    { sprite: "water_tile", x: 0, y: 24 },
    { sprite: "water_tile", x: 8, y: 24 },
    { sprite: "water_tile", x: 16, y: 24 },
    { sprite: "water_tile", x: 24, y: 24 },
    // Sand border
    { sprite: "sand_tile", x: 8, y: 8 },
    { sprite: "sand_tile", x: 16, y: 8 },
    { sprite: "sand_tile", x: 8, y: 16 },
    { sprite: "sand_tile", x: 16, y: 16 },
    // Grass center
    { sprite: "grass_tile", x: 12, y: 12 },
  ],
}

This creates a 32x32 pixel island with water around the edges, a sand border, and grass in the center.