Files
prosperon/docs/ops.md
John Alanbrook 83b798e365 Add Hugo website and rewrite docs to match current engine
New Hugo site in website/ with prosperon.dev theme (blue/gold/castle
aesthetic), docs sidebar navigation, and content pages. Rewrote all
doc files to align with the actual codebase: compositor+film2d
rendering, use() modules (no global prosperon object), Pit language,
script+JSON entity model. Added entities.md, front matter to all
70+ API docs, and updated API index for current module architecture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 18:09:55 -06:00

4.7 KiB

title, type
title type
Compositor docs

The Compositor

The compositor is Prosperon's rendering orchestrator. You describe your scene — planes, layers, effects — and the compositor figures out what render passes are needed and executes them.

The Basic Idea

You don't issue draw calls. You create sprites and set their properties. The compositor does the rest:

  1. Queries all registered drawables
  2. Organizes them by plane and layer
  3. Applies effects to tagged groups
  4. Composites everything to the screen

Think of it like a GBA: you declare "sprite here, sprite there," get handles back, and poke their positions. The hardware (in this case, the compositor + GPU backend) handles the actual rendering.

Scene Configuration

A scene is described as a config object passed to the compositor:

var compositor = use('compositor')

var plan = compositor.compile({
  clear: {r: 0, g: 0, b: 0, a: 1},
  planes: [
    {
      name: 'background',
      plane: 'background',
      resolution: {width: 320, height: 240},
      camera: bg_camera,
      presentation: 'integer_scale'
    },
    {
      name: 'game',
      plane: 'default',
      resolution: {width: 320, height: 240},
      camera: game_camera,
      layer_sort: {'5': 'y'},
      presentation: 'integer_scale'
    },
    {
      name: 'hud',
      plane: 'hud',
      resolution: {width: 1280, height: 720},
      camera: hud_camera,
      presentation: 'stretch'
    }
  ]
})

Each plane renders independently at its own resolution and composites onto the screen.

Planes

A plane is a named rendering group. Sprites belong to a plane via their plane property (default: 'default'). Each plane in the compositor config:

  • Has its own resolution (low-res pixel art, native UI, etc.)
  • Has its own camera
  • Has its own layer sorting rules
  • Has a presentation mode (how it maps to the window)

Planes composite in order — later planes draw on top of earlier ones.

Layer Sorting

Within a plane, drawables are sorted by layer (integer). Within each layer, you can choose a sorting mode:

layer_sort: {
  '0': 'explicit',  // engine may reorder for batching efficiency
  '5': 'y'          // sort by Y position (top-down game depth)
}

Y-sorting is essential for top-down games where objects lower on screen should appear in front.

Effects

Effects are applied to groups of sprites. Tag sprites with group names, then define effects for those groups:

var player = sprite({
  image: "player.png",
  groups: ['glow_objects']
})

var plan = compositor.compile({
  planes: [{
    name: 'game',
    plane: 'default',
    camera: cam,
    resolution: {width: 320, height: 240}
  }],
  group_effects: {
    glow_objects: {
      effects: [
        {type: 'bloom', threshold: 0.5, intensity: 1.5, blur_passes: 2}
      ]
    }
  }
})

Available Effects

Bloom — extracts bright areas, blurs them, composites back:

{type: 'bloom', threshold: 0.8, intensity: 1.0, blur_passes: 2}

Mask — uses sprites in a mask group as a stencil:

{type: 'mask', mask_group: 'mask_shapes', channel: 'alpha', invert: false}

Sprites in the mask_group are not drawn directly — they only serve as the mask shape.

Presentation Modes

Each plane specifies how its render target maps to the window:

Mode Behavior
stretch Fill the window exactly (may distort aspect ratio)
letterbox Fit inside window, preserving aspect ratio, with black bars
integer_scale Scale by whole numbers only for pixel-perfect rendering

integer_scale automatically uses nearest-neighbor filtering for crisp pixels.

Execution

The compositor produces a plan — a list of render passes. The plan is executed by the GPU backend:

var result = compositor.execute(plan)
// result.commands is sent to the GPU backend

In practice, core.start({render}) handles this for you. Your render callback returns the compositor result and the engine executes it.

Manual Drawables

You can inject drawables that aren't registered with film2d by adding them directly to a plane config:

planes: [{
  name: 'game',
  plane: 'default',
  camera: cam,
  drawables: [
    {type: 'sprite', image: tex, pos: {x: 0, y: 0}, width: 32, height: 32, layer: 0}
  ]
}]

These are merged with the registered drawables for that plane.

Render Targets

The compositor automatically manages intermediate render targets for effects. You don't need to create or manage GPU textures — the compositor allocates and reuses them as needed.

Debug

Pass debug: 'graph' to core.start config to log the compiled render plan as JSON. Pass debug: 'cmd' to log the final command list sent to the GPU.