Skip to content

The second layer — the [layer] table

A preset may compose one optional second scene. The [layer] table names its own system and carries a full authoring surface — params, easing, structural tables — while the preset’s single [palette] colours both layers, so a world keeps one colour language and one baked LUT (that sharing is deliberate and not configurable; see docs/preset-palettes.md).

system = "fragment_field" # the main scene — the ground
[params]
warp = "0.4"
trails = "0.6" # compositing params live at the TOP level only
[layer] # the second scene
system = "swarm" # any system — the same one twice is legal
join = "under" # "under" (default) or "over"
blend = "screen" # over-join only: add | screen | multiply | overlay
mix = "0.2 + 0.8 * bass" # over-join only: bindable amount, 0..1
[layer.params] # the layer scene's own params, its namespace
size = "2.5"
zoom = "0.9 + 0.3 * onset"
[layer.smoothing] # eases layer bindings — same vocabulary as [smoothing]
zoom = 0.3
mix = { attack = 0.05, release = 0.6 }

The two joins

  • under — the layer draws into the same target as the main scene, before the post chain: both share trails, the kaleidoscope and bloom, and fuse into one substance. A trailed, folded ground carries the layer with it. Cost: one extra scene draw.
  • over — the layer renders into its own offscreen and blends into the chain between the kaleidoscope and bloom: its geometry is never smeared by trails nor folded by the kaleidoscope (crisp at display resolution), but it still blooms and tonemaps with the frame — crisp and glowing. Cost: one offscreen plus one blend pass.

Know your layer’s coverage before picking under. A fullscreen scene that presents with full coverage (a fragment field, reaction-diffusion at high density) drawn under will occlude the main scene entirely — the layer paints over it inside the shared target. under is the sparse-over-dense idiom (particles, lines, an emitter over a field); a fullscreen-over-fullscreen pair wants join = "over" with a blend mode.

The over join’s blend and mix

blend is structural (fixed at load, unknown names reject the preset):

modereads as
addlinear-light addition — the engine’s native idiom, the brightest
screenbounded brightening (the default; cannot blow out)
multiplydarkens where the layer has coverage
overlaymultiply below mid-grey, screen above — contrast

Every mode operates within the layer’s own coverage: a darkening mode darkens only where the layer actually drew. mix is the one bindable lever in the table itself — the blend’s amount, clamped to 0..1, eased through a [layer.smoothing] mix entry — so audio can surge the second layer in and a preset can breathe between one world and two. mix = "0" renders exactly the layerless preset. On an under join, blend is ignored (with a load warning) and mix has no junction to act at.

Dark on light — the multiply route, and its two traps

multiply is how this engine draws a dark figure on a light ground, which is worth stating plainly because the rest of the pipeline adds light and a reader reasonably assumes dark-on-light is unavailable. The full recipe with a worked preset is in Colour and palettes; the two things that will bite you belong here, beside the parameters they are about:

  • In a multiply layer, the darkening operand is the layer’s colour, so brightness runs backwards. brightness = 0 is an opaque black mark; a high brightness clamps to white and does nothing. Nothing warns — the parameter’s meaning is inverted by blend in a different table.
  • The light ground has to be the chain, never bg_*. The backdrop is composited underneath the finished chain, so no blend mode can reach it. At the default occlude = 1 a covering layer makes the frame byte-identical to the same preset over a black backdrop (the backdrop is held out, not darkened); at occlude = 0 it is added after the blend and becomes a floor the layer cannot cross: a multiply layer reaching display luma 18.9 over black reaches only 171.3 over a lit sky.

Which scene to put in the slot is a question of footprint, not of capability — both routes darken:

layer systemcoveragegives you
fragment_field, reaction_diffusionevery pixel (alpha = occlude)the whole frame darkened — a wash, a gradient, a figure and its surround
swarm, emitterinside each mark only (alpha = the mark’s geometric falloff)discrete dark marks on an untouched light ground

The particle route reaches darker: a frozen swarm at brightness = 0 takes a light chain to display luma 0.9, against 18.9 for the field route. (A particle layer can darken, whatever an older note says: a particle’s alpha is its geometric falloff, independent of its colour.)

What the layer does and does not get

  • Its own params and easing[layer.params] reaches the layer’s scene only, evaluated under the same clock, analysis frame and seed salt as the top level. Per-element (index) bindings work, against the layer’s own [layer.spectrum] elements. Binding a compositing name (trails, kaleido_*, bg_*, ink_*…) inside [layer.params] warns at load and does nothing: the chain, backdrop and terminal passes belong to the preset as a whole and take their values from the top level.
  • Its own structural tables[layer.curve], [layer.generator], [layer.particles], [layer.spectrum], with the same per-system rules (a layer L-system still requires its generator table).
  • Its own scene instance — constructed for the preset, so the same system twice is legal (two swarms in counterpoint, two fields at different zooms) and a stateful layer (reaction-diffusion, the attractor) carries its own simulation state.
  • Not its own palette (the preset’s serves both), not its own [feedback] table (the layer gets the defaults; the attractor-as-layer’s internal trail is therefore unwarped), and no third layer — one [layer] table, total.

Heavy-plus-heavy pairings are an authoring responsibility: both tiers render both layers, so an attractor ground under a reaction-diffusion layer costs what the two scenes cost. Measure with --report or the diagnostics overlay before shipping one.

Built from a8ce055 at version 0.115.0. This site tracks main and is not versioned per release.