Skip to content

Background pass — bg_hue, bg_bright, bg_vignette, the ramp and the band

An audio-tintable gradient + vignette backdrop drawn before the scene, engine- wide. bg_bright = 0 (the default) is a black backdrop; raise it to reveal the gradient. bg_hue picks the tint out of the preset’s palette; bg_vignette (0..1) darkens the corners.

bg_hue is a coordinate in your [palette] (ADR-0086). The pass samples the same baked LUT every scene samples, so an ember preset gets an ember sky and a custom gradient tints its own backdrop — and saturation / palette_mix move the backdrop with the figure, so an A/B crossfade carries the whole frame. It is cyclic, with the same wrap trap color_center has: -0.1 and 0.9 are the same place, and on a stop-list palette that seam is the sharpest transition in the gradient. With no [palette] declared the gradient is spectrum, and the twenty-row swatch table in Colour and palettes is that ramp — so bg_hue 0.30 is cornflower blue, 0.45 aqua, 0.85 amber until you declare stops of your own, at which point read the value off your own gradient instead. A bg_hue copied from another preset arrives at whatever colour your gradient holds there, not the colour it was in the preset you took it from. Visible wherever the scene leaves the frame unpainted: behind the sparse scenes (lines, swarm, attractor) where the gaps show through, and in the reaction-diffusion field’s voids (both composite over the backdrop instead of presenting opaque). The fragment field is the one full-screen scene that still draws opaquely, so bg_* has no visible effect there — including the whole ramp and the band below. A dusk ground under a fragment_field preset is not a dim ground, it is an absent one.

The directional ramp — bg_angle, bg_hue_span, bg_shade, bg_shade_end, bg_ramp_gamma

Five params turn that single tint into a gradient across the frame (ADR-0094). The pass sweeps a segment of your [palette] along one axis instead of taking one point of it, with a brightness ramp on the same axis and one exponent shaping both.

ParamDefaultWhat it does
bg_angle0.0The ramp’s direction, in radians. 0 runs bottom-to-top — the same zero-is-up convention launch_angle uses. 1.5708 (π/2) runs left-to-right, 3.1416 (π) top-to-bottom.
bg_hue_span0.0How far the palette coordinate travels from the ramp’s start to its end. bg_hue keeps its meaning as the coordinate at the start. 0 sweeps nowhere, which is the old one-sample behaviour.
bg_shade0.72Brightness factor at the ramp’s start.
bg_shade_end1.0Brightness factor at its end.
bg_ramp_gamma1.0The ramp’s response exponent — eases where things sit along the axis, applied ahead of both the colour and the brightness. Clamped to 0.05 .. 20.

Every default is an arithmetic identity with the picture the engine drew before the ramp existed, not an approximation of it, so a preset that binds none of them renders byte-for-byte unchanged. All five are ordinary bindable params, so a breathing horizon costs nothing extra.

Placement is authored by your [palette] stops’ own at positions. There is deliberately no bg_ramp_center or bg_ramp_width: positions already live in the palette, and a second placement mechanism could disagree with it. Where the horizon sits in the frame is where its colour sits in your gradient. The worked dusk ground:

[palette]
stops = [
{ at = 0.00, color = "#060b24" }, # near-black zenith
{ at = 0.25, color = "#1b2a5e" }, # deep blue
{ at = 0.50, color = "#c74b1d" }, # the ember band
{ at = 0.75, color = "#ff7a1f" },
{ at = 1.00, color = "#ffd06e" }, # the hot horizon
]
[params]
bg_bright = "0.6"
bg_hue = "1.0" # the ramp STARTS at the palette's hot end...
bg_hue_span = "-1.0" # ...and travels backwards to its near-black end
bg_angle = "0" # bottom-to-top, so hot at the bottom of the frame
bg_shade = "1.0" # full brightness at the horizon...
bg_shade_end = "0.25" # ...a quarter of it at the top
bg_ramp_gamma = "2.0" # hold the horizon, then fall away

Move the at = 0.50 stop down and the ember band moves down the frame with it. Raise bg_ramp_gamma and the band holds longer before fading; drop it below 1 and the ramp falls away fast and leaves a long dim tail.

A wide smooth ramp is safe, and the dark tail is not the risky end. A gradient this broad would otherwise band — visibly, on a real display — worst exactly where this control is most useful. The reason is worth carrying: a band is a run of pixels sharing one 8-bit value, so it is widest where the ramp is flattest, and sRGB’s near-black slope makes the dim tail the flattest part of any ramp that reaches toward black. Undithered on the dusk ground at 1080p, bg_ramp_gamma = 0.4 spends 7.5 pixels on each level and holds one value for 58 pixels in its tail. (The intuition that steep is dangerous is backwards — two pixels per level is the healthy state.)

The tonemap dithers its output by one encoded level (ADR-0096), always, for every gradient in the engine at once — the same frame then spends 2.1 pixels per level and its widest plateau is 20. So author the ramp you want: a long dim tail, a low exponent, a near-black sky under a bright horizon are all ordinary requests, and none of them needs a stop added to break up a step. Nothing to switch on and no param to bind.

Why the exponent exists when stop placement can already shape the colour. Your [palette] is shared with the scene (and with the [layer], under ADR-0090), so re-spacing stops to shape the sky’s falloff re-maps the figure’s colours too. bg_ramp_gamma shapes the backdrop’s mapping onto its axis and touches nothing else. It is also the only shape control the brightness ramp has at all — bg_shade -> bg_shade_end is a straight line and no stop can bend it. It applies to the position rather than to either channel, so colour and brightness always reach their midpoints at the same height: it is one ramp, not two that can drift apart.

The segment wraps if it leaves [0, 1]. [bg_hue, bg_hue + bg_hue_span] is repeat-addressed like every other palette coordinate in the engine, so a span that runs off either end comes back around the other side. bg_hue = 0.8 with bg_hue_span = 0.5 sweeps 0.8 -> 1.3, which paints 0.8 -> 1.0 and then 0.0 -> 0.3 — putting the palette’s hot end at both ends of the sky with a hard seam where it wraps. That is occasionally what you want and usually a surprise; if the ramp has a bright band you did not author, check whether the span left the range. It is not clamped, and deliberately: two shipped presets already drive bg_hue outside [0, 1] and depend on the wrap.

The old fixed brightness tilt is gone. The pass used to multiply its tint by a hardcoded 0.72 -> 1.0 gradient welded to the vertical, always brighter at the top and unexplained by any param. bg_shade / bg_shade_end are where it went — those two numbers are its constants, which is why leaving them alone changes nothing. A backdrop can now be brighter at the bottom, which it never could be.

The backdrop is invisible to every behavioral gate, and the ramp makes that matter more. sanity measures coverage against the scene with the backdrop suppressed (ADR-0067), and the animation gate strips bg_* bindings before scoring motion (ADR-0091). So a world whose ground is painted here earns exactly nothing at either gate: the figure and any [layer] carry the whole burden. This is correct — it is what stops a more capable backdrop being used to game the gates — but it means a frame that looks full to you can still read as sparse to sanity. Give the figure enough to stand on its own before reaching for the ramp to fill the frame.

The backdrop composites correctly under every scene, so a lit bg_bright is worth revisiting on anything in the swarm_* and line families. Additive scenes emit premultiplied alpha (ADR-0056), so a sprite’s falloff is carried by its alpha rather than by its colour alone. Without that, every sprite punches four black rectangular notches beside itself and every stroke draws itself a black rim — invisible at bg_bright = 0, and worse the fatter the stroke. Several presets in these families still sit at a near-black floor tuned under the older behaviour, and their values were never re-tuned.

bg_bright has a ceiling, and it is a choice rather than a property of the engine. A scene that draws into the chain occludes the backdrop by its coverage, whatever light it emits: the frame resolves c * g + bg * (1 - g), so a fragment darkens the backdrop wherever its own light c is dimmer than the backdrop bg. Raise bg_bright past the dimmest emitted luminance in the figure and the dim parts stop fading out and start reading as dark speckle: on the swarm, the depth-parallaxed far particles go first; on a line scene, a stroke dimmed by glow or by a low-amplitude band. Rendered at bg_bright = 0.35, a swarm at brightness = 0.02 is black specks on a lit field. The working limit is the darkest part of the figure you still want visible, not a fixed number — sweep it and look, do not assume.

The one knob for it is occlude, below. It is what decides whether that limit binds at all.

None of this is worse than before: pre-fix the whole sprite quad held the backdrop out, so every value is brighter now than it was.

The curved band — bg_band_amount, bg_band_angle, bg_band_pos, bg_band_width, bg_band_curve, bg_band_hue, bg_band_hue_span

Seven params paint one soft band of light across the sky, over the ground the ramp paints and under everything else (ADR-0095). It was written for a Milky Way arc standing over a dusk horizon: a swell of light along a bowed diagonal, with the scene’s own stars sitting in front of it.

ParamDefaultWhat it does
bg_band_amount0.0The band’s intensity. 0 draws no band at all. Everything else here is inert until you raise it.
bg_band_angle0.0The direction across the band, in radians — the same axis convention bg_angle uses, so 0 runs the band horizontally and 1.5708 (π/2) runs it vertically.
bg_band_pos0.5Where the centreline sits along that across-axis, in the same normalized 0..1 the ramp uses. At bg_band_angle = 0 that axis runs bottom-to-top, so 0 is the bottom of the frame and 1 the top.
bg_band_width0.15The 1/e half-width — see below. Clamped to 0.001 .. 100.
bg_band_curve0.0The arc: how far the centreline bows, in across-axis units, at the middle of the band. 0 is exactly straight.
bg_band_hue0.0The band’s own coordinate in the same [palette] — an absolute coordinate, not an offset from the ground’s.
bg_band_hue_span0.0How far that coordinate travels along the band, so one end can brighten toward a galactic core.

Every default is an arithmetic identity with the picture that shipped before the band existed, so a preset that binds none of them renders byte-for-byte unchanged. All seven are ordinary bindable params.

bg_band_width is a 1/e half-width, not a full width and not an edge. The envelope is a gaussian: it is at full strength on the centreline and has fallen to 1/e (about 37 %) exactly bg_band_width either side of it, still faintly visible for two or three times that. So the visible band is several times wider than the number you type. At 0.15 on a 1080-row frame with bg_band_angle = 0, the 1/e line sits about 160 px from the centre and the band reads roughly a third of the frame tall. Halve it for a tight ribbon; 0.4 is a wash across the whole sky.

The worked Milky Way arc, over the same dusk palette the ramp’s example uses:

[params]
bg_bright = "0.0" # a near-black sky: the band does not need a ground
bg_band_amount = "0.5"
bg_band_angle = "1.2" # the across-axis tilted, so the band runs diagonally
bg_band_pos = "0.55" # a little above centre
bg_band_width = "0.12"
bg_band_curve = "0.18" # bowed — the silhouette that reads as a galaxy
bg_band_hue = "0.30" # the palette's cool blue-white...
bg_band_hue_span = "0.25" # ...warming toward one end

Two levers make it operable: move bg_band_pos and the arc rides up or down the frame; raise bg_band_curve and it bows further. Turn the whole thing with bg_band_angle, remembering that angle names the direction across the band rather than along it — so the band itself runs perpendicular to the number you write.

bg_band_amount > 0 is now enough on its own. The backdrop pass used to skip drawing entirely below a visible bg_bright; it no longer does. A band over a bg_bright = 0 sky paints, which is the near-black configuration this look actually wants — the reference photograph’s sky is almost black away from the horizon. You do not need a lit ground to hang a galaxy on.

The band is additive over the ground and under the scene. It adds light rather than replacing it, which is what unresolved starlight is — so it brightens whatever the ramp already painted rather than covering it, and the scene then draws over both. A fullscreen or opaque scene therefore hides the band exactly as it hides the ramp, and fragment_field hides it completely. A galaxy under a fragment-field preset is not a dim galaxy, it is an absent one.

The band shares your [palette] with the ground and the scene and any [layer], and that is the one real authoring constraint this creates. There is no second palette to reach for — palette_mix already owns the A/B pair for preset crossfade, so pinning the band to B would fight every dissolve. A sky with both a horizon ramp and a galaxy needs stops that hold both sets of colours: the dusk palette in the ramp’s example is fully spent on the horizon, and adding an arc to it means finding room for the band’s colours in the same list. Plan for that before tuning, not after. Because bg_band_hue is absolute, the band keeps one colour along its whole length whatever the ramp underneath it is doing — which is what makes a pale arc over a warm horizon authorable at all.

The band’s coordinate wraps exactly as the ramp’s does: [bg_band_hue, bg_band_hue + bg_band_hue_span] is repeat-addressed, not clamped, so a segment running off either end comes back around the other side with a hard seam where it wraps. If the arc has a colour break you did not author, check whether the span left [0, 1].

And the backdrop is still invisible to every behavioral gate. This bears restating here rather than only under the ramp, because a more capable backdrop makes the temptation to lean on it stronger. sanity measures coverage against the scene with the backdrop suppressed (ADR-0067) and animation strips bg_* bindings before scoring motion (ADR-0091), so a galaxy earns a preset nothing at either. The figure and any [layer] carry both floors, no matter how much of the frame the sky fills.

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