Every parameter, and why it is set where it is
Each of the 36 parameters below is a real control in the panel. The reasoning is
first-hand: where a bound comes from a measured property of the work, that measurement is named.
Beam-a
A inside aInside
colour, default #3a90e0
The colour at the beam's core. Inside and outside are mixed by the field's own distance term, so the pair describes how the light falls off across the beam rather than just what colour it is — set them apart and the beam gains a rim, set them equal and it is flat.
A outside aOutside
colour, default #479ce6
The colour at the beam's edge. The mix is alpha-weighted and linear, not OKLAB — that is the reference's setting for beams, and it matters because the two values here are close: in linear the transition stays neutral, while a perceptual space would bend it.
A start thickness aStartThickness
0–2, default 0.33
Half-width at the start of the segment, scaled by 0.25 in the field. Start and end differ on purpose: that difference is what makes the beam conical rather than a bar, and it is the cheapest way to say "the light comes from over there" without drawing a source.
A end thickness aEndThickness
0–2, default 0.6
Half-width at the far end. Larger than the start here, so the beam opens as it travels. Equal values give a uniform bar; the ratio, not either number alone, is what carries the shape.
A start softness aStartSoftness
0–50, default 27.22
Softness interpolates along the segment too, and that is the half people forget. Interpolating only thickness gives an evenly blurred bar; interpolating both is what makes one end read as near and the other as far. It enters twice — once widening the falloff, once as an exponent on it — so the range is wide and the top end is haze.
A end softness aEndSoftness
0–20, default 14.26
Softness at the far end, lower than the start here — so this beam is vaguest where it begins and firms up as it goes, which is the opposite of beam B and is why the two do not read as a mirrored pair.
Beam-b
B inside bInside
colour, default #e872bb
The warm source. One hue thinning out is a fade; two hues meeting is a room — the band where this one overlaps the cool beam is the only place in the picture that says the light comes from somewhere.
B outside bOutside
colour, default #e872bb
Equal to the inside colour by default, unlike beam A. That is not an oversight in the reference: this beam carries its colour flat and lets its very low end softness do the shaping instead.
B start thickness bStartThickness
0–2, default 0.21
Thinner at the start than beam A and wider at the end, so the two beams open in opposite proportions and their overlap is a band rather than a second, brighter beam.
B end thickness bEndThickness
0–2, default 0.7
The widest of the four thickness values, which is why the warm light occupies more of the lower half than the cool one does of the upper.
B start softness bStartSoftness
0–50, default 27.67
Nearly the same as beam A's start softness — both beams begin as haze. What separates them is where they end.
B end softness bEndSoftness
0–20, default 2.97
The lowest softness anywhere in the picture, and the one value that keeps this from being pure haze. At roughly 3 the warm beam still has no edge you could trace, but it has a definite core — and that core is what the eye settles on. Raise it and the picture loses its only firm thing.
Presentation
Veil veil
0–1, default 0.5
Not yet derived — tuned by eye. See the list below.
Edge fade edgeFade
0–0.6, default 0.1
Not yet derived — tuned by eye. See the list below.
Render scale renderScale
1–8, default 1
Default 1: the scene is computed at one buffer pixel per display pixel. Raising it computes the ground, beams and swirl into a smaller buffer and enlarges them, which costs proportionally less.
⚠️ It defaults to 1 because 2 was measurably wrong, not because it is cheap. An earlier version shipped 2 on the reasoning that none of those layers has an edge, so nothing is lost by computing them coarsely. That reasoning is sound about the *scene* and misses what sits on top of it: the scatter offsets its sample by a random amount each pixel, and on an enlarged texture those offsets land between texels. As the swirl drifts, the interpolation underneath them changes — and the soft edges crawl. The layer that suffers from a coarse buffer was not the one being computed into it.
Drift
Detail swirlDetail
0.3–4, default 1.5
How fine the swirl is. The field is three layers of recursive domain warping, not fbm — each layer warps coordinates the previous layer already warped, so the disturbance follows the shape underneath it instead of being added on top of it. That is why the result reads as filaments rather than as clouds. The ceiling is set by the buffer: the scene is computed at a fraction of the display, so past roughly 4 the third layer is finer than a buffer texel and arrives as noise.
Drift swirlSpeed
0–2, default 0.4
Not yet derived — tuned by eye. See the list below.
Swirl opacity swirlOpacity
0–1, default 0.86
How much of the swirl reaches the picture. It is the only layer here composited in OKLAB rather than plain over, and that is worth a look: OKLAB is perceptually uniform, so a blend between two colours of similar lightness does not sag through a darker middle on the way. In sRGB the same blend between the blue and the magenta passes through a muddy grey-purple. At 0 the layer is gone and the two beams are bare, which is the honest way to see what it contributes.
Blend bias swirlBlend
0–100, default 67
Shifts which half of the swirl pattern becomes the visible colour. It enters as (blend - 50) * 0.006 added to the normalised pattern before a smoothstep, so 50 is neutral and the ends push the threshold past where the pattern reaches — at which point the layer is either entirely one colour or entirely the other, and stops being a pattern.
Swirl colour swirlColor
colour, default #e7cfc5 (dark) / #ffffff (light)
The colour the swirl carries where its pattern is strong; where it is weak the layer is transparent and the beams show through unchanged. It is warm and nearly grey on purpose — the two beams already supply the hue, and a third saturated colour here would compete with the band where they meet, which is the one place the picture says the light comes from somewhere.
This is the one colour that differs between the two themes, and the reason is not symmetry. On a dark page the swirl reads as warm dust lit from the side, so a near-grey warm tone is right. On a light page the whole work is composited onto white at half opacity, and that same warm grey turns muddy — it is darker than the paper it sits on, so instead of catching light it reads as a smudge. Pure white keeps it additive against the page: it can only brighten, never dirty. Every other colour here is the same in both themes; this one is not, and that asymmetry is the point rather than an oversight.
Pointer
Parting parting
0–40, default 7.7
The pointer displaces the picture, it does not draw on it. The frame is treated as a sheet of cloth pinned on a 64×64 lattice of springs; the cursor pushes the nearest part of the sheet along its own direction of travel, and the springs pull it back. This number is how far that displacement is allowed to reach the sampled image. At 0 the pointer does nothing at all, which is the comparison this piece is for: a field that merely drifts, against one that can be touched.
Reach reach
0.25–8, default 2
The ceiling here is not taste, it is where the effect disappears. The force falls off as a gaussian in normalised frame units, and the value is scaled by 0.08 before use — so the default 2 is a radius of 0.16 of the frame. Push it far past that and the falloff is close to 1 everywhere: the force stops being local and becomes a uniform translation of the whole sheet, which the springs’ restoring term then cancels. Measured: at an effective radius of 2 the picture moved by an average of 0.0 pixels under a pointer sweep — identical to the control with the pointer switched off entirely. A parameter whose top end silently turns the feature off is worth bounding.
Stiffness stiffness
0.2–20, default 3
This one has a real ceiling, and it comes from the integrator. The lattice is stepped with explicit Euler, which is only stable while the step is short relative to the stiffness; past that bound the displacement grows every step instead of settling, and the sheet tears itself apart. The frame is split into two substeps precisely to buy headroom here — halving the step doubles the stiffness that stays stable. So the top of this range is not where it stops looking good, it is where the arithmetic stops converging.
Damping damping
0.2–20, default 3
How fast the sheet stops moving. It is applied as a per-substep multiplier clamped into 0..1, so this is a rate and not a time — it means the same thing at any frame rate, which matters because the loop runs at thirty frames a second while the display may offer a hundred and twenty. Near the floor the sheet keeps ringing after the cursor has gone, which reads as jelly rather than cloth; near the ceiling the multiplier hits zero within a single substep and the motion is dead on arrival.
Grain
Grain grain
0–2, default 0.5
The amount is set by the output, not by the eye. Eight bits give 256 steps, so a gradient across a wide frame changes value only every several pixels and those steps read as bands. Noise of about one step scatters the boundary and the eye integrates it back. Below that the banding shows through; far above it the picture turns to sand. It is also static — the seed uses pixel coordinates and no time — because grain that flickers is noise, and grain that holds still is film.
Shadow bias grainBias
0–6, default 2
The grain is weighted by the inverse of luminance, and this is the exponent on that weighting: heavy in shadow, clean in highlight. Real film behaves this way — silver grain is most visible where the emulsion is thin. At 0 the weighting is gone and the grain sits evenly over everything, which immediately reads as an overlay laid on the picture rather than as something the picture is made of.
Texture
Scatter scatter
0–100, default 60
This is what makes a drag read as sand rather than as a smear. The layer is called DiffuseBlur in the reference and it is not a blur: it takes exactly one sample, at a UV pushed a random distance aside. At 0 the picture is the beams and the swirl with nothing on top and the drag looks like wet paint; raising it breaks the sampled position per pixel, and the same displacement now reads as grains moving past each other. It is the texture of the motion, not of the image — which is why it only becomes obvious once the pointer moves.
⚠️ An earlier version of this file hard-coded it at 60 and shipped no control at all. The panel then claimed, by omission, that this was not adjustable — and it is the first thing the author went looking for.
Ground ground
colour, default #0F0217
The work ships its own ground rather than borrowing the page's. Everything above it is composited over this colour, so it is not a backdrop but the darkest value in the picture — and the grain is weighted toward the shadows, which means this colour decides where most of the grain lands.
Chroma
Dispersion Base chromaBase
colour with alpha, default #3a90e0
The colour where the field is disturbed but has no clear direction. It is the floor of the effect: whatever is here shows up everywhere the pointer has been, regardless of which way it went.
Dispersion Upward chromaUp
colour with alpha, default #e7cfc5
The tint where the flow runs upward. The four directions are what make this dispersion rather than chromatic aberration — the usual trick offsets the three channels by a fixed vector, while this picks a colour by which way the sheet was pushed, so the trail remembers the gesture.
Dispersion Downward chromaDown
colour with alpha, default #e7cfc5
The downward tint. It is not in the reference panel even though the value exists in its data — which is exactly the kind of hidden constant this cabinet refuses, so it is exposed here.
Dispersion Leftward chromaLeft
colour with alpha, default #e872bb61
The leftward tint. Left and right are weighted by a smoothstep on the normalised flow, so a diagonal drag mixes two of the four rather than snapping to one.
Dispersion Rightward chromaRight
colour with alpha, default #3a90e066
The rightward tint. Setting only this one and dragging both ways is the quickest way to see that the layer is directional at all.
Dispersion force chromaIntensity
0–5, default 1
How hard the pointer injects into the dispersion field. The field is velocity advected through itself, so this is a rate of injection, not an amount — hold still and it decays to nothing on its own, which is why the layer costs nothing when nobody is moving.
Dispersion reach chromaReach
0–12, default 3
The radius of that injection, scaled by 0.05 and by the square of the pointer speed — so a slow drag disturbs almost nothing and a fast one opens a wide wake. The speed term is what makes it read as a gesture rather than as a brush: the same path drawn slowly and quickly gives two different pictures.
Momentum chromaMomentum
0–120, default 30
How far the field carries itself each step. At 0 the colour sits where it was put and simply fades; raising it makes the disturbance travel along its own velocity, so the wake keeps moving after the cursor has gone. That self-advection is the difference between a stain and a current.