diff --git a/AGENTS.md b/AGENTS.md index 8946176..4d5cf2c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -175,7 +175,7 @@ change carries its speed as an index into that list, and it meant both durations at once. `checks.py` guards the constant the way it guarded the menu. -## Ripple moves cells and density, not the field +## Ripple is a post-pass and a painter, not a field motion Loop, Drift and Wind move where the field is sampled. Pulse and Ripple leave the field alone, which is why both work with no field strength @@ -186,20 +186,31 @@ Ripple went through the field first and was invisible. A radial displacement of the sampling coordinates, at any amplitude short of mush, changes almost nothing a dither shows, because the dither stays put and only the shading under it shifts. A lateral wave and a zoom -failed the same way. What reads is either changing how many cells fill -(the crest's swell, as Pulse does) or moving the finished cells -themselves (`rippleWarp`, which pulls each drawn cell along the -water's slope, dither and all). Ripple does both, and the warp is what -makes every texture move under it, traced ones included, so it needs -no per-texture answer. +failed the same way. What reads is changing the finished cells. Ripple +does that in two places, neither of them `buildGrid`: `rippleSwell` +grows and shrinks each finished layer's shapes by a cell along the +rings, and `renderWarped` paints every pixel from the cell under the +refracted point, at pixel resolution, so blocks stretch rather than +step. A cell-level warp was tried between the two and stepped whole +blocks, which is what the pixel painter replaced. + +Two things follow that a new consumer of `generate()` must know. It +returns `warp` alongside the grids, null for every motion but Ripple, +and `renderToCanvas` needs it passed through or a Ripple frame paints +with the swell and no pull; every call site in the app and the +explainers passes it. And the SVG exporter reads the grids only, which +is right, because an SVG is a still. Ripple is rain: several seeded drops, each born at its own moment and living one cycle, summed where the rings cross. Each is periodic on its own so the sum closes, but rings are in flight at phase 0, so Ripple's -first frame is not the still, as Drift's is not. The rings are -evaluated on a lattice `RIPPLE_STEP` cells apart and interpolated; -evaluating every drop at every cell put a frame far over the 80ms -budget. Both layers share A's drops, B at 70% of the swell and pull. +first frame is not the still, as Drift's is not. The preview passes +`loopCycle`, and with it each drop lands somewhere new at every +rebirth, when its ring is at zero; exports leave it out, so their rain +repeats and the loop closes. The rings are evaluated on a lattice +`RIPPLE_STEP` cells apart and interpolated; evaluating every drop at +every cell put a frame far over the 80ms budget. Both layers share the +one surface, B at 70% of the swell and pull. ## Tide is the one motion where the layers touch @@ -226,6 +237,32 @@ way, so the slider worked and looked dead. The edge is now dithered over `CONTOUR_FEATHER` of a level and no more, so the same shift moves the line. Don't widen it to soften the look without watching it move. +## Collide and per-layer motion + +Every motion is resolved per layer in `generate()`: a layer's `motion` +of "same" follows `loopMode`, "none" holds it still, and a kind runs +it, all on the one phase. Anything that reads "the" motion in the app +must say which: `generalMotion()` is what `generate()` gets as +`loopMode`, and `currentMotion()` is whether anything plays and what to +call it, which a layer's own kind can answer when General is None. + +Collide is Tide's push under any motion: `tidePush` reads the pusher's +field as it is that frame, on a lattice two cells apart, because under +Loop, Drift, and Wind it is rebuilt every frame and a full-resolution +gradient put a frame far over budget. Tide's own push is the still +one, cached. Both go through the same `deform` hook. + +## Wind stands, Drift slides, and B circles the other way + +Wind's first octave holds still and only the finer octaves travel +(`WIND_PARALLAX`), with the gust bending the lot; it used to slide +like Drift with the gust on top, and the two were hard to tell apart. +Under Loop, and in Wind's gust circle, layer B turns the opposite way +(`LAYER_B_SPIN`), so the layers slide against each other; same-way +circling read as one sway. Both are how a motion plays, not how a +still looks, so links are untouched, but the Loop and Wind example +clips were re-rendered for them. + ## Six orthogonal controls drive animation Don't re-entangle them. **Motion** (General tab) is what a pass looks like, @@ -272,7 +309,8 @@ field existed", because base64 pads the last byte with zeros and an old link's tail is read straight out of that padding. A tail that is all zeros is not written at all, though nothing leaves it empty today: the two durations are always written, so every new link is the same length -(83 characters since depth and direction joined the tail). +(84 characters since depth, direction, collide, and the layers' +own motions joined the tail). The escape hatch is `COMPACT_VERSION`. Bumping it says the break is deliberate, and both checks stand down: the decoder refuses a version it diff --git a/README.md b/README.md index fe35384..60f27f2 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ no cut. + scale to whatever each column ends up with. Two columns, so each + clip renders large, which these need since the motion is the whole + point of them. --> @@ -42,8 +42,8 @@ no cut. - - + + @@ -54,8 +54,8 @@ no cut. - - + + @@ -66,31 +66,8 @@ no cut. - - - -
LoopDrift
The flow field breathes around a small closed path and comes back where it started, so the stripes of this 8-way kaleidoscope sway in place.The green bands slide one way along their flow direction and wrap into themselves, so they never jump; the sparse purple layer, with almost no field strength, holds still behind them.Each layer's flow field breathes around a small closed path, the two turning opposite ways, so they sway against each other and come back where they started.Orange bands slide one way and a cream nebula behind them slides the other, each along its own flow direction, and both wrap into themselves so they never jump.
WindPulse
The yellow and magenta streaks travel the way Drift's do, but a much broader gust bends the field as they go, rising and settling once across the cycle, so the fine wisps run ahead of the bands they belong to.Density swings on one sine wave, Layer B at 70% of the depth, so the streaks swell and thin without moving.The broad streaks hold their ground while a much broader gust bends them, rising and settling once across the cycle, and their fine detail streams through along the flow.Density swings on one sine wave, Layer B at 70% of the depth, so the contour lines swell and thin in weight and the streaks behind them breathe, without moving.
RippleTide
Rain: seven drops land at their own moments, and each ring bends and swells what it crosses, thins as it spreads, and adds where it meets another, while the field beneath never moves.Each layer's field is pushed along the other's ridges, rising and settling once per cycle, so the blue and pink bands bend around each other and let go.
- -Three more, each thirteen Auto-randomize passes at a two-second interval, -showing three ways to use the locks. - - - - - - - - - - - - - - - - - + +
Full randomBrand campaignKaleidoscope variations
Full randomBrand campaignKaleidoscope variations
Only block size and shape mask locked; every other field rerolls each pass.Fine diagonal streaks fading toward the bottom, colors and flow locked to a brand palette; only density and stretch drift, for a set of on-brand variations.A chunky 8-way kaleidoscope with symmetry, block size, density, and flow locked; seeds and colors reroll each pass, so every frame is a new pattern in the same language.Rain on a marble: seven drops land at their own moments, and each ring bends and swells what it crosses, thins as it spreads, and adds where it meets another, while the field beneath never moves.Each layer's field is pushed along the other's ridges, rising and settling once per cycle, so the strands are swept along the nebula behind them and the nebula bends around the strands.
@@ -136,6 +113,7 @@ of the sidebar or the end of the settings panel on a phone. - Plays on the canvas the moment you pick one, which is exactly what a GIF records - Separate sliders for how fast it moves and how often it rerolls, both from one second to sixteen, with the reroll stepping in doublings - Depth scales how far a motion goes, and Direction runs it backwards, so Drift and Wind can travel against the flow +- Collide makes the layers push on each other under any motion, and each layer can run a motion of its own ### Exporting and sharing diff --git a/animated-backgrounds/index.html b/animated-backgrounds/index.html index 5127fa9..9226f30 100644 --- a/animated-backgrounds/index.html +++ b/animated-backgrounds/index.html @@ -179,7 +179,7 @@

The six motions

-

Loop breathes around a closed path and returns where it started. Drift travels one way and wraps into itself. Wind travels and bends as it goes, like a gust crossing the frame. Pulse holds still and swells. Ripple drops rain on the surface and lets the rings cross. Tide bends each layer around the other's ridges and lets go.

+

Loop breathes around a closed path and returns where it started, the two layers turning opposite ways. Drift travels one way and wraps into itself. Wind holds its ground while a gust bends it and fine detail streams through. Pulse holds still and swells. Ripple drops rain on the surface and lets the rings cross. Tide bends each layer around the other's ridges and lets go.

How long a cycle lasts

@@ -188,7 +188,7 @@

How long a cycle lasts

An export uses that same number. A GIF records whole cycles. Set four seconds and you get a four second file. Drop it into a page and let it repeat.

-

Two more controls sit beside it. Depth is how far the motion goes, from a quarter of its usual reach to twice it. Direction runs the cycle backwards, which is how Drift and Wind travel against the flow without the still changing.

+

Three more controls sit beside it. Depth is how far the motion goes, from a quarter of its usual reach to twice it. Direction runs the cycle backwards, which is how Drift and Wind travel against the flow without the still changing. Collide makes the two layers push on each other while they move. And each layer tab has a Motion of its own, so one layer can loop while the other drifts.

The slider starts at one second rather than lower. A cycle is one change of brightness across the whole frame, and one a second stays well under the three that WCAG 2.3.1 allows. Worth keeping in mind for whatever you build with the file, too.

@@ -295,7 +295,7 @@

Try it

- + + - + +