From c6fbd82996ee6c41440977b09976676c65a07008 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 2 Nov 2026 12:02:39 +0000
Subject: [PATCH] chore: version packages
---
.changeset/aurora-field-pass.md | 9 ---
.changeset/aurora-slice-palette.md | 7 --
.changeset/fluted-glass.md | 17 -----
.changeset/led-wall-panel-color.md | 5 --
.changeset/liquid-gradient.md | 7 --
.changeset/poster-image-imports.md | 5 --
.changeset/resample-beneath-effect.md | 11 ----
.changeset/scene-max-fps.md | 7 --
.changeset/scene-no-msaa-or-depth.md | 7 --
.changeset/scene-paused.md | 7 --
.changeset/scene-resize-redraw.md | 7 --
.changeset/scene-scroll-push.md | 7 --
.changeset/scene-stroke-at.md | 9 ---
.changeset/shader-monitor-gpu-time.md | 7 --
.changeset/soft-cursors-return.md | 5 --
.changeset/style-dial-defaults-subpath.md | 5 --
.changeset/texture-pass-resize-rebind.md | 5 --
packages/shaders-cli/CHANGELOG.md | 6 ++
packages/shaders-cli/package.json | 2 +-
packages/shaders/CHANGELOG.md | 78 +++++++++++++++++++++++
packages/shaders/package.json | 2 +-
21 files changed, 86 insertions(+), 129 deletions(-)
delete mode 100644 .changeset/aurora-field-pass.md
delete mode 100644 .changeset/aurora-slice-palette.md
delete mode 100644 .changeset/fluted-glass.md
delete mode 100644 .changeset/led-wall-panel-color.md
delete mode 100644 .changeset/liquid-gradient.md
delete mode 100644 .changeset/poster-image-imports.md
delete mode 100644 .changeset/resample-beneath-effect.md
delete mode 100644 .changeset/scene-max-fps.md
delete mode 100644 .changeset/scene-no-msaa-or-depth.md
delete mode 100644 .changeset/scene-paused.md
delete mode 100644 .changeset/scene-resize-redraw.md
delete mode 100644 .changeset/scene-scroll-push.md
delete mode 100644 .changeset/scene-stroke-at.md
delete mode 100644 .changeset/shader-monitor-gpu-time.md
delete mode 100644 .changeset/soft-cursors-return.md
delete mode 100644 .changeset/style-dial-defaults-subpath.md
delete mode 100644 .changeset/texture-pass-resize-rebind.md
diff --git a/.changeset/aurora-field-pass.md b/.changeset/aurora-field-pass.md
deleted file mode 100644
index 9aa85b86..00000000
--- a/.changeset/aurora-field-pass.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-'@camp-dev/shaders': minor
----
-
-`Aurora` draws its field into a texture once per frame, and each march step reads the texture instead of working out five noise octaves, 60 times for every pixel. The texture stores the field before the shaping that makes the thin filaments, so the texture filter blends a smooth value and the filaments stay sharp. Pixel values shift slightly, but the curtains look the same. At 3272 by 1392 device pixels on an M1 Max, Aurora's GPU time per frame drops from about 14.4 ms to about 5.0 ms. Where the renderer cannot draw into a half-float target, on WebGL2 without `EXT_color_buffer_float`, Aurora works out the field at every step as before.
-
-`ShaderContextValue` gains `registerPrePass`, which adds a draw that runs before the scene's meshes on every frame, including the redraw after a resize. A hand-built context, such as a fake one in a test, needs `registerPrePass` too, and `() => () => undefined` registers nothing. `ShaderMonitor`'s GPU time counts these draws along with the scene pass and the output quad.
-
-`TexturePassOptions` gains `format`: `RGBAFormat`, the default, for four channels, or `RedFormat` for one.
diff --git a/.changeset/aurora-slice-palette.md b/.changeset/aurora-slice-palette.md
deleted file mode 100644
index c74211c6..00000000
--- a/.changeset/aurora-slice-palette.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@camp-dev/shaders': minor
----
-
-Add `createTexturePass` and its `TexturePass` and `TexturePassOptions` types. A texture pass draws a TSL node into a render target of its own, and exposes the texture, `render`, `resize`, and `dispose`. It exposes no texture when the renderer cannot render to or bind the requested texel type: on WebGL2 without `EXT_color_buffer_float`, or for a full float on a WebGPU device without `float32-filterable`. After a lost device, it goes inert.
-
-`Aurora` draws its 60 slice colors into a texture once per material build, instead of running its color ramp 60 times for every pixel. It also works out its per-pixel constants before its march loop. It looks the same as before. At 3272 by 1392 device pixels on an M1 Max, Aurora's GPU time per frame drops from about 18.4 ms to about 14.4 ms.
diff --git a/.changeset/fluted-glass.md b/.changeset/fluted-glass.md
deleted file mode 100644
index 4bdf7422..00000000
--- a/.changeset/fluted-glass.md
+++ /dev/null
@@ -1,17 +0,0 @@
----
-'@camp-dev/shaders': minor
----
-
-Add `FlutedGlass`, an Effect that shows the image beneath it through repeating flutes of glass. Mount it after a Source inside a `ShaderScene`. Each flute bends the image under it into a squeezed copy, catches a highlight along the wall that faces the light, and darkens near its grooves where the glass turns steep. Its props:
-
-- `fluteWidth` sets each flute's width in CSS pixels.
-- `angle` sets the direction the flutes run, in the degrees `LinearGradient` uses, so the default of 90 gives vertical flutes.
-- `refraction` sets how far the glass bends the image, and `refraction={0}` leaves it unchanged.
-- `dispersion` splits the image's colors where the glass bends, as a share of the bend. Blue bends further than red, as in real glass, so colors fringe near the grooves and the flute centers stay clean. `dispersion={0}` turns the split off.
-- `crossSection` sets each flute's shape, from a pointed `'prism'` through a `'round'` lens to a `'flat'` top with steep walls, or any number from 0 to 1 between them.
-- `lightAngle` sets where the light comes from, in degrees that turn the same way as `angle`: 0 from the right and 90 from above. It defaults to 135, the upper left. The light stays put while `angle` turns the glass.
-- `highlight` and `shadow` set how strong the highlight and the shadow are, from 0 to 1. `highlightColor` tints the highlight.
-
-Every prop except `highlightColor` takes a static value or an animation signal. Near the canvas edge, the glass mirrors the image back onto the canvas. `FlutedGlass` bends only what Sources drew, never the output of an Effect such as `Grain`, so mount such Effects after it. It works in Mode 1 only.
-
-Add the `fluteSurface` primitive for Mode 2. For a position, it returns the bend to add before you sample your own texture, the slope and steepness of the glass, and how far the position sits from its flute's center.
diff --git a/.changeset/led-wall-panel-color.md b/.changeset/led-wall-panel-color.md
deleted file mode 100644
index d3437c5e..00000000
--- a/.changeset/led-wall-panel-color.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@camp-dev/shaders': minor
----
-
-`LedWall` gains `variation` and `panelColor`. `variation` sets how unevenly the dots are lit: 0 lights every dot the same, and the default, 0.5, keeps each dot's fixed brightness between half and full, as before. `panelColor` is the color of a panel behind the dots. It fills the gaps, and a dim or flickering dot fades toward it. Left unset, the panel is transparent and the wall draws as before.
diff --git a/.changeset/liquid-gradient.md b/.changeset/liquid-gradient.md
deleted file mode 100644
index dc9320d5..00000000
--- a/.changeset/liquid-gradient.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@camp-dev/shaders': minor
----
-
-Add `LiquidGradient`, a Source that streaks bands of color along slow, curling currents, like paint stirred into liquid. Mount it inside a `ShaderScene`. `stops`, `colorSpace`, and `hueInterpolation` pick the colors, as they do on `LinearGradient`. `angle` and `repeat` lay the bands out before the current moves them, and `softness` sets how the colors meet, from hard bands at 0 to full blends at 1. `swirl` sets how far the current carries the colors, `scale` sets roughly how many eddies span the canvas's height, and `speed` sets how fast the current drifts. Each of those six props takes a static value or an animation signal. `seed` picks a different layout with the same look. A literal `speed={0}` holds the current still and lets the scene park. It works in Mode 1 only.
-
-`LiquidGradient` draws the current, a noise field, and its palette into textures before the scene pass, and redraws each texture only when a prop it depends on changes. Where the renderer cannot draw into a half-float target, on WebGL2 without `EXT_color_buffer_float`, every read works its value out from the noise or the palette instead. The result looks the same and costs more GPU time.
diff --git a/.changeset/poster-image-imports.md b/.changeset/poster-image-imports.md
deleted file mode 100644
index 7840d6ad..00000000
--- a/.changeset/poster-image-imports.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@camp-dev/shaders-cli': minor
----
-
-`poster` can now render a scene that imports an image. Its bundler inlines each imported `.png`, `.jpg`, `.jpeg`, or `.webp` file as a data URL string, so the poster server needs no route for the file. Next.js turns the same import into an object with a `src` field, so a scene that also runs in Next.js has to accept both.
diff --git a/.changeset/resample-beneath-effect.md b/.changeset/resample-beneath-effect.md
deleted file mode 100644
index 9e4d759b..00000000
--- a/.changeset/resample-beneath-effect.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-'@camp-dev/shaders': minor
----
-
-An Effect can now take more than one sample of the image beneath it. `useResample` registers a resample, a function that receives the sampler for everything mounted beneath the Effect and returns the sampler that replaces it. The new sampler can call the one beneath as often as it needs, such as three times for a color split. The `Resample` and `Sampler` types describe the two functions. `useBasePassUv` keeps its signature and registers the resample that samples beneath once, at the warped coordinate. `Dither`, `LedWall`, `CursorRipple`, and `CursorSmudge` draw the same as before unless two of them stack.
-
-When two UV warps stack, the last-mounted one is now the top layer, the way every other Effect stacks. Before, the first-mounted UV warp moved the screen coordinate first, so `` pixelated the water along with the image beneath it. Now Dither pixelates the image, and the water bends the pixelated result.
-
-Effects now keep their place in the stack. An Effect that registers again, because a value in its `deps` changed, goes back where it was instead of moving to the top. `CursorRipple` now stacks where it is mounted. Its UV warp and its shine used to register one render late, after every Effect beside it, so `` added the shine over the grain. Now Grain runs over the shine.
-
-`ShaderContextValue` and `OutputStage` replace `registerBaseUvTransform` with `registerResample`. If you call `registerBaseUvTransform` yourself, register `(sampleBeneath) => (coordinate) => sampleBeneath(warp(coordinate))` with `registerResample` instead. `registerResample` and `registerOverlay` take an optional `Slot` as their second argument. A registration under a slot that registered before goes back to that slot's place. `useResample` and `usePostProcessPass` pass one for you.
diff --git a/.changeset/scene-max-fps.md b/.changeset/scene-max-fps.md
deleted file mode 100644
index a1a922fe..00000000
--- a/.changeset/scene-max-fps.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@camp-dev/shaders': minor
----
-
-`` takes a `maxFPS` prop, which sets the scene's frame cap, the most frames per second it draws. With no frame cap, a scene draws at the display's refresh rate, as before. `maxFPS={60}` draws every other frame of a 120 Hz display. A new value applies from the next frame without rebuilding the renderer. Frames that a prop change asks for count against the cap, but a resize still redraws at once. A frame cap never wakes a parked scene or resumes a paused one.
-
-`FrameScheduler.setMaxFPS` sets the cap on a scheduler. The scheduler skips an animation frame that arrives sooner than one interval after its last tick, less a tenth of the interval for timestamp jitter. A tick's `delta` spans the skipped frames. Speed-driven animation keeps its pace while tick intervals stay at or below 0.1 seconds; longer intervals are clamped by `useAnimatableSpeed`, slowing the motion.
diff --git a/.changeset/scene-no-msaa-or-depth.md b/.changeset/scene-no-msaa-or-depth.md
deleted file mode 100644
index c1b78688..00000000
--- a/.changeset/scene-no-msaa-or-depth.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@camp-dev/shaders': minor
----
-
-On WebGPU, a scene no longer keeps MSAA samples or a depth buffer. `createRenderer` defaults `antialias` to false and gives the canvas no depth buffer, and the scene pass inside `` renders without one. Every component draws one quad that covers the canvas and smooths its own edges, so neither changed a pixel. At 3272 by 1392 device pixels, a scene's render targets drop from about 400 MB to about 73 MB, and each resize reallocates the smaller set. The WebGL2 fallback renders as before.
-
-If you call `createRenderer` yourself, pass `antialias: true` to turn MSAA back on. Nothing in a WebGPU scene is depth-tested, so each mesh you draw covers whatever three drew before it.
diff --git a/.changeset/scene-paused.md b/.changeset/scene-paused.md
deleted file mode 100644
index 0dc8e34e..00000000
--- a/.changeset/scene-paused.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@camp-dev/shaders': minor
----
-
-`` takes a `paused` prop, which freezes the scene on its current frame. Time stops while the scene is paused, so it resumes on the frame it stopped on. While paused, a resize draws the frozen frame again at the new size. A scene paused from its mount draws nothing until it resumes.
-
-Paused time no longer counts anywhere. When a scene resumes after a hidden tab or an off-screen canvas, it now carries on from its last frame rather than jumping ahead by the time away. `FrameScheduler`'s first tick after a resume carries no `delta`, and `elapsed` counts only the time the scheduler ran. `FrameScheduler.onPauseChange` tells a listener when the scheduler pauses or resumes. `holdRendererClock` holds the renderer's clock at its current time and returns the function that puts it back, because three's own animation loop advances that clock on every animation frame, rendered or not. The pause watcher's `setPaused` pauses the loop whatever the visibility.
diff --git a/.changeset/scene-resize-redraw.md b/.changeset/scene-resize-redraw.md
deleted file mode 100644
index 7a8c6c28..00000000
--- a/.changeset/scene-resize-redraw.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@camp-dev/shaders': patch
----
-
-A running `` now redraws straight after its canvas resizes. A resize clears the canvas after the frame loop has drawn, so while a canvas changed size on every frame, such as during a window drag, the browser painted a cleared canvas on almost every frame. The redraw ticks no animation, so a `speed` phase still advances once per frame. A scene out of view, or in a hidden tab, skips the redraw and draws at the new size when it resumes.
-
-The scene now updates `useResize`'s size before it redraws, so the redrawn frame has the new aspect ratio. `useResize` returns the scene's size signal from its first render, where it used to return a placeholder size of `[0, 0, 1]` for one render. Every `useResize` call in a scene shares that one signal, in place of a resize observer each.
diff --git a/.changeset/scene-scroll-push.md b/.changeset/scene-scroll-push.md
deleted file mode 100644
index 524cdc71..00000000
--- a/.changeset/scene-scroll-push.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@camp-dev/shaders': minor
----
-
-Add `scrollPush` to `ShaderScene`. With it on, a touch that scrolls the page over the canvas pushes the scene's cursor effects at the finger, and a fling keeps pushing until the page stops. The scene listens passively and never cancels a touch, so the page scrolls and zooms as it would without the scene. A touch then reaches cursor effects only this way, because touch pointer moves no longer move the cursor. A mouse works as before. `CursorRipple` stirs its water with the push. `CursorSpotlight` doesn't read the push, so it ignores touch in a scene with `scrollPush` on.
-
-`CursorInput` gains `setScrollPush`, `getPush`, `isPushing`, and `onPushStart`. The signal `useCursor` returns gains `getPush`, which reads the frame's push stroke. The new `PushStroke` type has the shape of a wave field's stroke. While `scrollPush` is on, `ShaderScene` ticks its shared `CursorInput` once per frame, ahead of the cursor effects that read it, and keeps drawing while a touch pushes.
diff --git a/.changeset/scene-stroke-at.md b/.changeset/scene-stroke-at.md
deleted file mode 100644
index 321d3938..00000000
--- a/.changeset/scene-stroke-at.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-'@camp-dev/shaders': minor
----
-
-Add `strokeAt` to `ShaderScene`, so code can draw a scripted stroke across the scene's cursor effects. Pass an animation signal, such as a `MotionValue`, holding a point from 0 to 1 across the canvas. Each frame strokes from where the point was to where it is, and a move wakes a parked scene. Attaching the point, resuming from a pause, and resizing each start a fresh stroke. While a point is attached, `CursorSmudge` and `CursorRipple` ignore the mouse and scroll push. It works with or without `scrollPush`.
-
-`CursorInput` gains `setStrokePoint`, `getScriptedStroke`, and `isScripting`, and the signal `useCursor` returns gains `getScriptedStroke` and `isScripting`. The new `StrokePoint` type is the shape a point must have.
-
-`CursorInput` also gains `onPushEnd`, which fires once for every `onPushStart`, when the fling settles or the contact ends another way. A scripted stroke never starts or ends a push.
diff --git a/.changeset/shader-monitor-gpu-time.md b/.changeset/shader-monitor-gpu-time.md
deleted file mode 100644
index 55bc05ea..00000000
--- a/.changeset/shader-monitor-gpu-time.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-'@camp-dev/shaders': minor
----
-
-`ShaderMonitor` shows the scene's GPU time per frame under its frame rate, averaged over the same half-second window. Mounting the monitor turns on GPU timing for its scene, and unmounting it turns the timing off. The readout shows a dash on the WebGL2 fallback, and on an adapter without timestamp queries. The time covers the scene pass and the output quad. Where the GPU runs the two passes at once, as Apple silicon does, the overlap counts once. A pass that a component draws on its own, such as `CursorRipple`'s wave field, is not counted.
-
-`ShaderContextValue` gains `timeGpu`, which turns the timing on and returns the off switch. A hand-built context, such as a fake one in a test, needs the field too, and `() => () => undefined` reports nothing.
diff --git a/.changeset/soft-cursors-return.md b/.changeset/soft-cursors-return.md
deleted file mode 100644
index 27ddd624..00000000
--- a/.changeset/soft-cursors-return.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@camp-dev/shaders': minor
----
-
-Add `CursorSmudge`, a cursor effect that pulls the Sources' image along pointer movement and recovers smoothly. Control the pull with `strength`, `radius`, and `decay`, using numbers or animation signals. Set `decay={0}` to hold the distortion. Enable `scrollPush` on `ShaderScene` for touch scrolling.
diff --git a/.changeset/style-dial-defaults-subpath.md b/.changeset/style-dial-defaults-subpath.md
deleted file mode 100644
index 042ef7d2..00000000
--- a/.changeset/style-dial-defaults-subpath.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@camp-dev/shaders': minor
----
-
-`@camp-dev/shaders/defaults` exports `STYLE_DIAL_DEFAULTS`, FractalNoise's contrast and balance for each style, without loading three. Import it from there where the root entry can't load, such as in a server render or a Node test.
diff --git a/.changeset/texture-pass-resize-rebind.md b/.changeset/texture-pass-resize-rebind.md
deleted file mode 100644
index a5b3b1d6..00000000
--- a/.changeset/texture-pass-resize-rebind.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@camp-dev/shaders': patch
----
-
-On WebGPU, resizing a canvas that holds a smudge from `CursorSmudge` no longer logs three's `Bindings._update: binding should be available` error. `createTexturePass` now marks its texture for update when a resize changes the texture's size. A shader that samples the texture before the next draw into it then binds the new allocation.
diff --git a/packages/shaders-cli/CHANGELOG.md b/packages/shaders-cli/CHANGELOG.md
index 84663d0f..3a057198 100644
--- a/packages/shaders-cli/CHANGELOG.md
+++ b/packages/shaders-cli/CHANGELOG.md
@@ -1,5 +1,11 @@
# @camp-dev/shaders-cli
+## 0.23.0
+
+### Minor Changes
+
+- 39470ce: `poster` can now render a scene that imports an image. Its bundler inlines each imported `.png`, `.jpg`, `.jpeg`, or `.webp` file as a data URL string, so the poster server needs no route for the file. Next.js turns the same import into an object with a `src` field, so a scene that also runs in Next.js has to accept both.
+
## 0.22.0
## 0.21.0
diff --git a/packages/shaders-cli/package.json b/packages/shaders-cli/package.json
index 79da505c..3ef7d418 100644
--- a/packages/shaders-cli/package.json
+++ b/packages/shaders-cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@camp-dev/shaders-cli",
- "version": "0.22.0",
+ "version": "0.23.0",
"description": "Dev-time CLI for Shaders. Renders a component tree to a poster image with the poster command.",
"keywords": [
"cli",
diff --git a/packages/shaders/CHANGELOG.md b/packages/shaders/CHANGELOG.md
index c066316a..fc14752b 100644
--- a/packages/shaders/CHANGELOG.md
+++ b/packages/shaders/CHANGELOG.md
@@ -1,5 +1,83 @@
# @camp-dev/shaders
+## 0.23.0
+
+### Minor Changes
+
+- 28b02c2: `Aurora` draws its field into a texture once per frame, and each march step reads the texture instead of working out five noise octaves, 60 times for every pixel. The texture stores the field before the shaping that makes the thin filaments, so the texture filter blends a smooth value and the filaments stay sharp. Pixel values shift slightly, but the curtains look the same. At 3272 by 1392 device pixels on an M1 Max, Aurora's GPU time per frame drops from about 14.4 ms to about 5.0 ms. Where the renderer cannot draw into a half-float target, on WebGL2 without `EXT_color_buffer_float`, Aurora works out the field at every step as before.
+
+ `ShaderContextValue` gains `registerPrePass`, which adds a draw that runs before the scene's meshes on every frame, including the redraw after a resize. A hand-built context, such as a fake one in a test, needs `registerPrePass` too, and `() => () => undefined` registers nothing. `ShaderMonitor`'s GPU time counts these draws along with the scene pass and the output quad.
+
+ `TexturePassOptions` gains `format`: `RGBAFormat`, the default, for four channels, or `RedFormat` for one.
+
+- fee293c: Add `createTexturePass` and its `TexturePass` and `TexturePassOptions` types. A texture pass draws a TSL node into a render target of its own, and exposes the texture, `render`, `resize`, and `dispose`. It exposes no texture when the renderer cannot render to or bind the requested texel type: on WebGL2 without `EXT_color_buffer_float`, or for a full float on a WebGPU device without `float32-filterable`. After a lost device, it goes inert.
+
+ `Aurora` draws its 60 slice colors into a texture once per material build, instead of running its color ramp 60 times for every pixel. It also works out its per-pixel constants before its march loop. It looks the same as before. At 3272 by 1392 device pixels on an M1 Max, Aurora's GPU time per frame drops from about 18.4 ms to about 14.4 ms.
+
+- 5862f45: Add `FlutedGlass`, an Effect that shows the image beneath it through repeating flutes of glass. Mount it after a Source inside a `ShaderScene`. Each flute bends the image under it into a squeezed copy, catches a highlight along the wall that faces the light, and darkens near its grooves where the glass turns steep. Its props:
+
+ - `fluteWidth` sets each flute's width in CSS pixels.
+ - `angle` sets the direction the flutes run, in the degrees `LinearGradient` uses, so the default of 90 gives vertical flutes.
+ - `refraction` sets how far the glass bends the image, and `refraction={0}` leaves it unchanged.
+ - `dispersion` splits the image's colors where the glass bends, as a share of the bend. Blue bends further than red, as in real glass, so colors fringe near the grooves and the flute centers stay clean. `dispersion={0}` turns the split off.
+ - `crossSection` sets each flute's shape, from a pointed `'prism'` through a `'round'` lens to a `'flat'` top with steep walls, or any number from 0 to 1 between them.
+ - `lightAngle` sets where the light comes from, in degrees that turn the same way as `angle`: 0 from the right and 90 from above. It defaults to 135, the upper left. The light stays put while `angle` turns the glass.
+ - `highlight` and `shadow` set how strong the highlight and the shadow are, from 0 to 1. `highlightColor` tints the highlight.
+
+ Every prop except `highlightColor` takes a static value or an animation signal. Near the canvas edge, the glass mirrors the image back onto the canvas. `FlutedGlass` bends only what Sources drew, never the output of an Effect such as `Grain`, so mount such Effects after it. It works in Mode 1 only.
+
+ Add the `fluteSurface` primitive for Mode 2. For a position, it returns the bend to add before you sample your own texture, the slope and steepness of the glass, and how far the position sits from its flute's center.
+
+- d0cf494: `LedWall` gains `variation` and `panelColor`. `variation` sets how unevenly the dots are lit: 0 lights every dot the same, and the default, 0.5, keeps each dot's fixed brightness between half and full, as before. `panelColor` is the color of a panel behind the dots. It fills the gaps, and a dim or flickering dot fades toward it. Left unset, the panel is transparent and the wall draws as before.
+- ec69ebe: Add `LiquidGradient`, a Source that streaks bands of color along slow, curling currents, like paint stirred into liquid. Mount it inside a `ShaderScene`. `stops`, `colorSpace`, and `hueInterpolation` pick the colors, as they do on `LinearGradient`. `angle` and `repeat` lay the bands out before the current moves them, and `softness` sets how the colors meet, from hard bands at 0 to full blends at 1. `swirl` sets how far the current carries the colors, `scale` sets roughly how many eddies span the canvas's height, and `speed` sets how fast the current drifts. Each of those six props takes a static value or an animation signal. `seed` picks a different layout with the same look. A literal `speed={0}` holds the current still and lets the scene park. It works in Mode 1 only.
+
+ `LiquidGradient` draws the current, a noise field, and its palette into textures before the scene pass, and redraws each texture only when a prop it depends on changes. Where the renderer cannot draw into a half-float target, on WebGL2 without `EXT_color_buffer_float`, every read works its value out from the noise or the palette instead. The result looks the same and costs more GPU time.
+
+- b899dd9: An Effect can now take more than one sample of the image beneath it. `useResample` registers a resample, a function that receives the sampler for everything mounted beneath the Effect and returns the sampler that replaces it. The new sampler can call the one beneath as often as it needs, such as three times for a color split. The `Resample` and `Sampler` types describe the two functions. `useBasePassUv` keeps its signature and registers the resample that samples beneath once, at the warped coordinate. `Dither`, `LedWall`, `CursorRipple`, and `CursorSmudge` draw the same as before unless two of them stack.
+
+ When two UV warps stack, the last-mounted one is now the top layer, the way every other Effect stacks. Before, the first-mounted UV warp moved the screen coordinate first, so `` pixelated the water along with the image beneath it. Now Dither pixelates the image, and the water bends the pixelated result.
+
+ Effects now keep their place in the stack. An Effect that registers again, because a value in its `deps` changed, goes back where it was instead of moving to the top. `CursorRipple` now stacks where it is mounted. Its UV warp and its shine used to register one render late, after every Effect beside it, so `` added the shine over the grain. Now Grain runs over the shine.
+
+ `ShaderContextValue` and `OutputStage` replace `registerBaseUvTransform` with `registerResample`. If you call `registerBaseUvTransform` yourself, register `(sampleBeneath) => (coordinate) => sampleBeneath(warp(coordinate))` with `registerResample` instead. `registerResample` and `registerOverlay` take an optional `Slot` as their second argument. A registration under a slot that registered before goes back to that slot's place. `useResample` and `usePostProcessPass` pass one for you.
+
+- 618e2ad: `` takes a `maxFPS` prop, which sets the scene's frame cap, the most frames per second it draws. With no frame cap, a scene draws at the display's refresh rate, as before. `maxFPS={60}` draws every other frame of a 120 Hz display. A new value applies from the next frame without rebuilding the renderer. Frames that a prop change asks for count against the cap, but a resize still redraws at once. A frame cap never wakes a parked scene or resumes a paused one.
+
+ `FrameScheduler.setMaxFPS` sets the cap on a scheduler. The scheduler skips an animation frame that arrives sooner than one interval after its last tick, less a tenth of the interval for timestamp jitter. A tick's `delta` spans the skipped frames. Speed-driven animation keeps its pace while tick intervals stay at or below 0.1 seconds; longer intervals are clamped by `useAnimatableSpeed`, slowing the motion.
+
+- 0af6c42: On WebGPU, a scene no longer keeps MSAA samples or a depth buffer. `createRenderer` defaults `antialias` to false and gives the canvas no depth buffer, and the scene pass inside `` renders without one. Every component draws one quad that covers the canvas and smooths its own edges, so neither changed a pixel. At 3272 by 1392 device pixels, a scene's render targets drop from about 400 MB to about 73 MB, and each resize reallocates the smaller set. The WebGL2 fallback renders as before.
+
+ If you call `createRenderer` yourself, pass `antialias: true` to turn MSAA back on. Nothing in a WebGPU scene is depth-tested, so each mesh you draw covers whatever three drew before it.
+
+- 9feeb7d: `` takes a `paused` prop, which freezes the scene on its current frame. Time stops while the scene is paused, so it resumes on the frame it stopped on. While paused, a resize draws the frozen frame again at the new size. A scene paused from its mount draws nothing until it resumes.
+
+ Paused time no longer counts anywhere. When a scene resumes after a hidden tab or an off-screen canvas, it now carries on from its last frame rather than jumping ahead by the time away. `FrameScheduler`'s first tick after a resume carries no `delta`, and `elapsed` counts only the time the scheduler ran. `FrameScheduler.onPauseChange` tells a listener when the scheduler pauses or resumes. `holdRendererClock` holds the renderer's clock at its current time and returns the function that puts it back, because three's own animation loop advances that clock on every animation frame, rendered or not. The pause watcher's `setPaused` pauses the loop whatever the visibility.
+
+- 846e997: Add `scrollPush` to `ShaderScene`. With it on, a touch that scrolls the page over the canvas pushes the scene's cursor effects at the finger, and a fling keeps pushing until the page stops. The scene listens passively and never cancels a touch, so the page scrolls and zooms as it would without the scene. A touch then reaches cursor effects only this way, because touch pointer moves no longer move the cursor. A mouse works as before. `CursorRipple` stirs its water with the push. `CursorSpotlight` doesn't read the push, so it ignores touch in a scene with `scrollPush` on.
+
+ `CursorInput` gains `setScrollPush`, `getPush`, `isPushing`, and `onPushStart`. The signal `useCursor` returns gains `getPush`, which reads the frame's push stroke. The new `PushStroke` type has the shape of a wave field's stroke. While `scrollPush` is on, `ShaderScene` ticks its shared `CursorInput` once per frame, ahead of the cursor effects that read it, and keeps drawing while a touch pushes.
+
+- 924fe6e: Add `strokeAt` to `ShaderScene`, so code can draw a scripted stroke across the scene's cursor effects. Pass an animation signal, such as a `MotionValue`, holding a point from 0 to 1 across the canvas. Each frame strokes from where the point was to where it is, and a move wakes a parked scene. Attaching the point, resuming from a pause, and resizing each start a fresh stroke. While a point is attached, `CursorSmudge` and `CursorRipple` ignore the mouse and scroll push. It works with or without `scrollPush`.
+
+ `CursorInput` gains `setStrokePoint`, `getScriptedStroke`, and `isScripting`, and the signal `useCursor` returns gains `getScriptedStroke` and `isScripting`. The new `StrokePoint` type is the shape a point must have.
+
+ `CursorInput` also gains `onPushEnd`, which fires once for every `onPushStart`, when the fling settles or the contact ends another way. A scripted stroke never starts or ends a push.
+
+- 8e52969: `ShaderMonitor` shows the scene's GPU time per frame under its frame rate, averaged over the same half-second window. Mounting the monitor turns on GPU timing for its scene, and unmounting it turns the timing off. The readout shows a dash on the WebGL2 fallback, and on an adapter without timestamp queries. The time covers the scene pass and the output quad. Where the GPU runs the two passes at once, as Apple silicon does, the overlap counts once. A pass that a component draws on its own, such as `CursorRipple`'s wave field, is not counted.
+
+ `ShaderContextValue` gains `timeGpu`, which turns the timing on and returns the off switch. A hand-built context, such as a fake one in a test, needs the field too, and `() => () => undefined` reports nothing.
+
+- c921384: Add `CursorSmudge`, a cursor effect that pulls the Sources' image along pointer movement and recovers smoothly. Control the pull with `strength`, `radius`, and `decay`, using numbers or animation signals. Set `decay={0}` to hold the distortion. Enable `scrollPush` on `ShaderScene` for touch scrolling.
+- 378a752: `@camp-dev/shaders/defaults` exports `STYLE_DIAL_DEFAULTS`, FractalNoise's contrast and balance for each style, without loading three. Import it from there where the root entry can't load, such as in a server render or a Node test.
+
+### Patch Changes
+
+- a29b6da: A running `` now redraws straight after its canvas resizes. A resize clears the canvas after the frame loop has drawn, so while a canvas changed size on every frame, such as during a window drag, the browser painted a cleared canvas on almost every frame. The redraw ticks no animation, so a `speed` phase still advances once per frame. A scene out of view, or in a hidden tab, skips the redraw and draws at the new size when it resumes.
+
+ The scene now updates `useResize`'s size before it redraws, so the redrawn frame has the new aspect ratio. `useResize` returns the scene's size signal from its first render, where it used to return a placeholder size of `[0, 0, 1]` for one render. Every `useResize` call in a scene shares that one signal, in place of a resize observer each.
+
+- 170ecc1: On WebGPU, resizing a canvas that holds a smudge from `CursorSmudge` no longer logs three's `Bindings._update: binding should be available` error. `createTexturePass` now marks its texture for update when a resize changes the texture's size. A shader that samples the texture before the next draw into it then binds the new allocation.
+
## 0.22.0
### Minor Changes
diff --git a/packages/shaders/package.json b/packages/shaders/package.json
index 00a59932..ede17734 100644
--- a/packages/shaders/package.json
+++ b/packages/shaders/package.json
@@ -1,6 +1,6 @@
{
"name": "@camp-dev/shaders",
- "version": "0.22.0",
+ "version": "0.23.0",
"description": "React shader components on WebGPU and Three.js TSL: Tier 1 components, ShaderScene, hooks, and the TSL primitives they build on.",
"keywords": [
"background",