Side quest 1: give four scenes art direction other than grain

Classic Wave, Silk Ribbon, Kaleido Tunnel and Slow Orb all declared the
`style` trait and honoured it with `col += sigGrain(uv)` and nothing else. A
declared trait is a contract — the disqualification rule in Personality.js is
the only thing keeping off-design scenes out of a track — so honouring it
with dirt meant these four could not take the `texture: 0` opt-out the grain
work introduced. Phase 9 measured their style response at exactly 0. They sat
at texture: 0.35 as a stopgap, which kept speckle on the library's cleanest
scenes purely to keep a gate green.

Each already had the knob; it just was not wired to the track:

- Classic Wave contrasts its wave through a bare smoothstep(0.2, 0.8). The
  transition width now comes from u_sigSoft and u_sigLine, centred on 0.5 so
  changing the hand does not change the exposure. Crest concentration is
  driven separately by u_sigLine, because a track is free to sample the
  scene's own u_softness at zero and the art direction must still show.
- Silk Ribbon's strand width is u_sigLine and its falloff exponent u_sigSoft:
  a sharp track gets a filament with a defined edge, a soft one a haze.
- Kaleido Tunnel drew its grid against a bare 0.42 — a line weight with no
  name. It is now a weight and a feather, both from the track.
- Slow Orb's body edge multiplies the scene's softness by the video's, and
  gains a sigEdge rim so a sharp-handed track gets a defined limb.

All four are now texture: 0. Style response measured against a maximally
soft-handed versus maximally sharp-handed personality: Classic Wave 111,
Silk Ribbon 216, Kaleido Tunnel 206, Slow Orb 102, out of 255 — previously 0.

104/104 checks pass including the slow set. See SIDE-QUESTS.md §1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Dejvino
2026-08-06 19:35:50 +02:00
co-authored by Claude Opus 5
parent 8dfd8392f3
commit 17a583a87f
11 changed files with 641 additions and 17 deletions
+13 -5
View File
@@ -7,9 +7,8 @@ export const classicWave = {
family: 'flow',
kind: 'fragment',
// Personality: see look/Personality.js.
// Grain is this scene's ONLY expression of the style trait — it draws no
// hard edges to weight — so it keeps a share of it rather than opting out.
texture: 0.35,
// Smooth concentric colour: grain only mutes the ramp it is built on.
texture: 0,
traits: ['shape', 'camera', 'style'],
params: {
@@ -47,9 +46,18 @@ vec4 scene(vec2 uv, vec2 p) {
float spoke = u_spokes > 0 ? sin(angle * float(u_spokes) + t) * u_beat : 0.0;
float v = 0.5 + 0.5 * sin(wave + spoke);
v = mix(v, smoothstep(0.2, 0.8, v), u_softness);
vec3 col = palRamp(t * u_colorRoll + d * 0.25) * v;
// TRAIT style: the track's hand on the crests. The scene's own u_softness
// decides HOW MUCH the wave is contrasted; the track decides what that
// contrast looks like — u_sigSoft widens the transition, u_sigLine tightens
// it. Centred on 0.5 so changing the hand does not change the exposure.
float edge = mix(0.06, 0.42, sat(u_sigSoft)) * mix(1.3, 0.6, sat(u_sigLine));
v = mix(v, smoothstep(0.5 - edge, 0.5 + edge, v), u_softness);
// ...and independently of u_softness, which a track is free to sample at
// zero: a heavier line concentrates the crest rather than letting it bloom
// across the whole ring.
vec3 col = palRamp(t * u_colorRoll + d * 0.25) * pow(v, mix(1.0, 2.2, sat(u_sigLine)));
// Core glow, the part that reads as the "hit".
col += pal(0) * (1.0 - smoothstep(0.0, 0.7, d)) * u_bloomCore * 0.6;
@@ -6,9 +6,8 @@ export const kaleidoTunnel = {
family: 'geometric',
kind: 'fragment',
// Personality: see look/Personality.js.
// Grain is this scene's ONLY expression of the style trait — it draws no
// hard edges to weight — so it keeps a share of it rather than opting out.
texture: 0.35,
// Hard grid lines on flat colour: grain reads as dirt on the lens.
texture: 0,
traits: ['shape', 'camera', 'style'],
params: {
@@ -49,7 +48,13 @@ vec4 scene(vec2 uv, vec2 p) {
float ringLines = abs(fract(z * u_rings * 0.1) - 0.5) * 2.0;
float wallLines = abs(fract(wall * sides) - 0.5) * 2.0;
float grid = smoothstep(0.42, 0.0, ringLines) + smoothstep(0.42, 0.0, wallLines);
// TRAIT style: the grid is the whole image, so the track draws it. The
// threshold was a bare 0.42 — a line weight with no name. u_sigLine sets how
// much of the cell the line occupies, u_sigSoft how far it feathers.
float weight = mix(0.22, 0.6, sat(u_sigLine));
float feather = weight * mix(0.15, 1.0, sat(u_sigSoft));
float grid = smoothstep(weight, max(weight - feather, 0.0), ringLines)
+ smoothstep(weight, max(weight - feather, 0.0), wallLines);
vec3 col = palRamp(z * 0.05 + wall * 0.2) * 0.35;
col += pal(int(mod(floor(z * u_rings * 0.1), 6.0))) * grid * 0.7;
+11 -4
View File
@@ -9,9 +9,8 @@ export const silkRibbon = {
family: 'minimal',
kind: 'fragment',
// Personality: see look/Personality.js.
// Grain is this scene's ONLY expression of the style trait — it draws no
// hard edges to weight — so it keeps a share of it rather than opting out.
texture: 0.35,
// A soft drape of light. Grain furs the one thing it is made of.
texture: 0,
traits: ['camera', 'style'],
params: {
@@ -57,8 +56,16 @@ vec4 scene(vec2 uv, vec2 p) {
best = min(best, d);
}
// TRAIT style: the ribbon is drawn in the track's hand. u_sigLine sets
// how wide the strand is, u_sigSoft how fast it falls off — a sharp
// track gets a filament with a defined edge, a soft one gets a haze.
// This is the scene's whole art direction, so it is not scaled by any
// of its own params: there is no setting at which the track stops
// showing.
float w = u_thickness * mix(0.55, 1.7, sat(u_sigLine));
float falloff = mix(2.6, 0.9, sat(u_sigSoft));
vec3 hc = pal(int(fk) % 3);
col += hc * exp(-best * best / (u_thickness * u_thickness));
col += hc * exp(-pow(best / w, falloff * 2.0));
col += pal(3) * exp(-best * best * 60.0) * u_glow * 0.6;
}
+14 -4
View File
@@ -10,9 +10,10 @@ export const slowOrb = {
family: 'minimal',
kind: 'fragment',
// Personality: see look/Personality.js.
// Grain is this scene's ONLY expression of the style trait — it draws no
// hard edges to weight — so it keeps a share of it rather than opting out.
texture: 0.35,
// One soft body in an empty frame — the emptiest scene in the library, and
// the one where speckle is most visible for being least justified. It keeps
// its own `grain` param, which exists to stop large flat areas banding.
texture: 0,
traits: ['shape', 'camera', 'space', 'style'],
params: {
@@ -44,13 +45,22 @@ vec4 scene(vec2 uv, vec2 p) {
float wobble = fbm(q * 2.4 + t, 4) * u_wobble;
float d = (sigShape(q / max(u_size, 1e-3)) * u_size) * (1.0 + wobble);
float body = smoothstep(u_softness * 0.5, -u_softness * 0.5, d);
// TRAIT style: the body's edge is drawn in the track's hand. The scene's
// u_softness says how soft this orb is; u_sigSoft says how soft this VIDEO
// is, and the two multiply.
float edge = u_softness * mix(0.35, 1.5, sat(u_sigSoft));
float body = smoothstep(edge * 0.5, -edge * 0.5, d);
float glow = exp(-max(d, 0.0) * (5.0 / max(u_halo, 0.05))) * u_halo;
// A rim in the track's line weight, so a sharp-handed track gets a defined
// limb against the halo instead of only a softer gradient.
float rim = sigEdge(d) * u_sigLine;
vec3 col = pal(0) * 0.05;
col = mix(col, pal(1), body * 0.85);
col += pal(2) * body * body * 0.5;
col += pal(3) * glow * 0.35;
col += pal(2) * rim * 0.5;
// Fine grain keeps large flat areas from banding.
col = sigAir(col, p, smoothstep(0.0, 1.6, length(q)));