Skip to content
Docs
Open Promoeyes
band-field

band-field

ONE TAG, BOTH FAMILIES — the unified displaced surface.

Read the rig's notes
<heightfield> and <shadergradient> were the same idea twice. This reel is
that idea once: an ordinary primitive wearing a <bands> table, with the
three things they disagreed about turned into values.

  1. UNITS ARE ONE CONVENTION.  freq = cycles across the shape's own extent
     (size-invariant); amp = PEAK MILLIMETRES that band displaces, an
     absolute length; fold = 0..1 toward 1-|n| creases. The old pair
     disagreed on both — heightfield's amp was mm while ShaderGradient's was
     tenths of a 10-unit sheet scaled by `unit`, and their frequencies were
     a factor of 4.3 apart. Neither `unit` nor `waterPlane` survives: size
     is mm, and a waterPlane is a plane with both axes subdivided.

  2. DIRECTION IS A VALUE.  `along=y|z|normal`. A height field pushes along
     one fixed axis; a blob pushes along its own normal — which is the only
     reason <shadergradient> could take a sphere and <heightfield> could
     not. WHERE the field is sampled is a separate question (`sample=`),
     because a flat sheet sampled on its normal would be constant.

  3. SHADING IS A SLOT.  `mix` is the only colour model: stops along `u`,
     optional accent along `v`, optional sun-dot, optional ambient of its
     own (`brightness`, which is why these scenes need no <lights>).
     Terrain is mix(u=height sun=… shade=…). ShaderGradient topology is
     two U endpoints plus accent — mix(u=x v=height) on a sheet, mix(u=y
     v=radius) on a blob. Every axis normalises by the field the scene
     actually has, measured at build, not by a bound it never reaches. No
     shading block, and the author's material stands untouched, which is
     what an animated sea needs for transmission and real HDR
     reflections.

And no new tag was needed. `<bands>` and a shading block are children that
any primitive can carry, so a sheet is <primitive><geometry type="plane">
and a blob is <polyhedron kind="icosahedron">.

THE OLD TAGS ARE STILL IN THE ENGINE. promos/primitives/terrain/terrain.promo
still runs on its own generator. <shadergradient> does too, but nothing
exercises it any more: all ten of its published presets now stand here as
<bands> + mix (S4, S5 and S44–S51), and the two reels that carried them —
backgrounds/shader-gradients.promo and its rows-form twin — are gone.

FIFTY-ONE SCENES, NINE FAMILIES. S01–S25 are the two old tags' own
territory, re-shot on the unified surface; S26–S43 are `mix` on its own
terms — every lever the colour model has, one per scene; S44–S51 are the
ten published ShaderGradient presets, which now live only here.
  S01–S10  SHEETS, BAKED — still, so the field bakes into the geometry:
           exact normals, real shadows, nothing per frame. Strata (mix(u=height)
           with six stops), mesa (`floor`), crackle (an INVERTED spectrum —
           the high band louder than the low, which fBm cannot express
           because gain would have to exceed 1), canyon (`fold` on only the
           top two bands), basin (every amp negative).
  S11–S15  SHEETS IN MOTION — no shading block, so the author's material
           owns the look: drifting dunes (`flow="1 0 0"` walks the field
           sideways instead of boiling it), silk under two soft-boxes,
           molten glass with transmission intact, a checker-roughness metal
           shiver, and a wall standing up (`along=z`) raked by an area light.
  S16–S20  MIX SHEETS — two U stops + accent, on fields ShaderGradient has
           never had. Every one has MORE THAN ONE BAND, which the ten
           presets cannot: two-band interference, a fine weave, one enormous
           slow tide, a folded churn, an ember crust.
  S21–S25  BLOBS — the same tables on a closed surface sampled on the
           NORMAL: a lava lamp seen from inside, five-band folded coral, a
           folded asteroid whose silhouette actually breaks, a cavern, and
           a pearl whose whole field is a hundredth of its radius.
  S26–S33  THE TABLE ITSELF — two colours, the three-colour wash, four, N,
           a LIVE height ramp (a baked one cannot move), six-stop geology
           in motion, and the same table on a blob and a reef.
  S34–S37  THE AXES.  `u` is a VALUE, not "across the sheet": RADIUS as the
           main axis instead of the accent's (concentric rings, not a
           wash), two SCREEN axes crossed (u=x, v=y) for a corner-to-corner
           bilinear, PAIRED `at` values so the table steps instead of
           blending — four flat fields out of eight stops — and ONE
           repeated `at` inside a smooth table, which is a hard terminator
           cutting the frame wherever the field crosses it.
  S38–S41  THE FIELD.  `along=z` pushing straight at the lens so the relief
           rakes against a side light; every amp NEGATIVE, so the accent
           floods the troughs the crests would have worn; the same mix on a
           TORUS, where `radius` genuinely means inner-vs-outer; and a
           still surface that bakes both the relief AND the mix.
  S42–S43  THE LIGHT.  A near-total sun-dot (`shade=0.92`) that darkens
           every face turned from the sun no matter where on the table it
           sits, and the opposite: a scene with no <lights> block at all,
           lit only by the mix's own `brightness`.
  S44–S51  THE TEN PUBLISHED PRESETS — ShaderGradient's own ten, converted
           by two numbers per shape (the family header prints them). Five
           sheets, then three blobs; halo and pensive are already S4 and
           S5. This reel is now their only home.

S26–S43 ARE BACKGROUNDS in the way S01–S25 are mostly landscapes: from S34
on there is no horizon anywhere — the surface fills the frame and the stop
table IS the picture. They also carry two matched glow-sprite haze lobes
(haze-a / haze-b) over dark gradients, so the lobes travel the cuts too.

ONE GRADIENT BECOMES THE NEXT. `transition=match-move` used to carry a
matched surface's POSE across a cut and leave its look behind — the field
lives in a uniform table and the colours in custom uniforms, so neither the
transform lerp nor `material.color` reached them, and two gradients held
their old palette while the compositor dissolved underneath. Now the table
itself travels: bands slide in frequency, grow and shrink in amplitude, and
crease as `fold` rises, while the three colours and the noise seed move with
them. A band one side does not have is paired with a ZERO-AMPLITUDE band at
its own frequency, so it grows out of silence at the right scale instead of
sweeping in from nothing. Only pairs with the same along/sample/shading can
do it (anything else is a different compiled program), and only fields that
move — a baked one keeps its shape in the vertex buffer, not in a uniform.
S16→S20 are five in a row that all qualify; watch the gaps with
`npx tsx scripts/gap-shot.mts <this file> s16-beat s17-weave <out>`, because
a filmstrip samples scene mid-points and never looks inside a transition.

ONE LIMIT, STATED: `floor` is bake-only. A moving field rides in the
material, and there is nothing to bake a height clamp into — so a scene
that moves shapes itself without one, which is what S11–S15 do. `mix`
itself is not limited that way: a STILL field bakes it as vertex colours
(exact normals, real shadows) and a moving one runs it per pixel, off the
same measured axis frame either way.
Preview: /packages/runtime/index.html?project=promos/primitives/backgrounds/band-field.promo
Scenes
51
Kind
reference rig
//
    ONE TAG, BOTH FAMILIES — the unified displaced surface.

    <heightfield> and <shadergradient> were the same idea twice. This reel is
    that idea once: an ordinary primitive wearing a <bands> table, with the
    three things they disagreed about turned into values.

      1. UNITS ARE ONE CONVENTION.  freq = cycles across the shape's own extent
         (size-invariant); amp = PEAK MILLIMETRES that band displaces, an
         absolute length; fold = 0..1 toward 1-|n| creases. The old pair
         disagreed on both — heightfield's amp was mm while ShaderGradient's was
         tenths of a 10-unit sheet scaled by `unit`, and their frequencies were
         a factor of 4.3 apart. Neither `unit` nor `waterPlane` survives: size
         is mm, and a waterPlane is a plane with both axes subdivided.

      2. DIRECTION IS A VALUE.  `along=y|z|normal`. A height field pushes along
         one fixed axis; a blob pushes along its own normal — which is the only
         reason <shadergradient> could take a sphere and <heightfield> could
         not. WHERE the field is sampled is a separate question (`sample=`),
         because a flat sheet sampled on its normal would be constant.

      3. SHADING IS A SLOT.  `mix` is the only colour model: stops along `u`,
         optional accent along `v`, optional sun-dot, optional ambient of its
         own (`brightness`, which is why these scenes need no <lights>).
         Terrain is mix(u=height sun=… shade=…). ShaderGradient topology is
         two U endpoints plus accent — mix(u=x v=height) on a sheet, mix(u=y
         v=radius) on a blob. Every axis normalises by the field the scene
         actually has, measured at build, not by a bound it never reaches. No
         shading block, and the author's material stands untouched, which is
         what an animated sea needs for transmission and real HDR
         reflections.

    And no new tag was needed. `<bands>` and a shading block are children that
    any primitive can carry, so a sheet is <primitive><geometry type="plane">
    and a blob is <polyhedron kind="icosahedron">.

    THE OLD TAGS ARE STILL IN THE ENGINE. promos/primitives/terrain/terrain.promo
    still runs on its own generator. <shadergradient> does too, but nothing
    exercises it any more: all ten of its published presets now stand here as
    <bands> + mix (S4, S5 and S44–S51), and the two reels that carried them —
    backgrounds/shader-gradients.promo and its rows-form twin — are gone.

    FIFTY-ONE SCENES, NINE FAMILIES. S01–S25 are the two old tags' own
    territory, re-shot on the unified surface; S26–S43 are `mix` on its own
    terms — every lever the colour model has, one per scene; S44–S51 are the
    ten published ShaderGradient presets, which now live only here.
      S01–S10  SHEETS, BAKED — still, so the field bakes into the geometry:
               exact normals, real shadows, nothing per frame. Strata (mix(u=height)
               with six stops), mesa (`floor`), crackle (an INVERTED spectrum —
               the high band louder than the low, which fBm cannot express
               because gain would have to exceed 1), canyon (`fold` on only the
               top two bands), basin (every amp negative).
      S11–S15  SHEETS IN MOTION — no shading block, so the author's material
               owns the look: drifting dunes (`flow="1 0 0"` walks the field
               sideways instead of boiling it), silk under two soft-boxes,
               molten glass with transmission intact, a checker-roughness metal
               shiver, and a wall standing up (`along=z`) raked by an area light.
      S16–S20  MIX SHEETS — two U stops + accent, on fields ShaderGradient has
               never had. Every one has MORE THAN ONE BAND, which the ten
               presets cannot: two-band interference, a fine weave, one enormous
               slow tide, a folded churn, an ember crust.
      S21–S25  BLOBS — the same tables on a closed surface sampled on