Skip to content

feat(shaders): add RadialProgress shader for circular progress / countdown UIs#26

Merged
chiefcll merged 2 commits into
mainfrom
feat/radial-progress-shader
May 22, 2026
Merged

feat(shaders): add RadialProgress shader for circular progress / countdown UIs#26
chiefcll merged 2 commits into
mainfrom
feat/radial-progress-shader

Conversation

@chiefcll
Copy link
Copy Markdown
Contributor

Summary

  • New RadialProgress shader — a stroked ring filled to a progress value, with a gradient swept along the arc, an optional background trackColor, and round or butt end caps. Single-node, one draw call, animatable via progress: 0..1.
  • Implemented for both WebGL and Canvas2D backends, registered alongside LinearGradient / RadialGradient in exports/{webgl,canvas}-shaders.ts and examples/common/installShaders.ts.
  • Companion example at examples/tests/shader-radial-progress.ts showing six variants (defaults, screenshot-recipe countdown, multi-stop gradient sweep, CCW butt caps, full-circle rainbow, custom start angle).

API

```ts
renderer.createShader('RadialProgress', {
width: 14,
progress: 0.7, // 0..1, animatable
startAngle: -Math.PI / 2, // 12 o'clock
direction: 1, // 1 = CW, -1 = CCW
colors: [0x4aa3ffff], // 1..N RGBA swept along the arc
trackColor: 0x1f3a5cff, // 0x00000000 = disabled
cap: 1, // 0 = butt, 1 = round
});
```

Notes for reviewers

  • Why a new shader vs. extending RadialGradient? RadialGradient interpolates by distance from a center (filled disc/ellipse). The countdown ring needs interpolation by angle along a stroked arc — different primitive, different fragment shader. Sibling rather than replacement keeps existing RadialGradient consumers untouched.
  • WebGL composition: fragment outputs vec4(mix(base.rgb, layer.rgb, layer.a), base.a + layer.a*(1-base.a)) rather than vec4(blended, base.a) — this lets the ring render visibly on a node whose base color alpha is 0 (transparent background), which is the common case for a standalone progress component.
  • Canvas2D sweep approximation: Canvas2D has no native sweep gradient, so the arc is drawn as 64 short stroked segments each tinted by interpolating between colors/stops. Segments overlap by 2% to avoid AA seams.
  • Cache markers: colors:N|cap:0|1|track:0|1 so shader programs are reused across compatible prop variations.
  • Round cap discs are gated to render only when `0 < progress < 1` (no caps at empty or full ring).

Test plan

  • `pnpm build` — clean
  • `pnpm exec vitest run` — 182/182 pass (14 new for the template's resolvers)
  • Visual sanity in `pnpm start` (WebGL): six rings render correctly
  • `pnpm test:visual:update` — generate and review the certified snapshot for `shader-radial-progress` before merging
  • Canvas2D path manually verified in `pnpm start` with `?renderMode=canvas`
  • `pnpm start:prod` to confirm transpile-floor compatibility (Chrome 38)

🤖 Generated with Claude Code

chiefcll and others added 2 commits May 22, 2026 00:04
…tdown UIs

A new sibling to LinearGradient / RadialGradient that draws a stroked ring
filled to a `progress` value, with a gradient swept along the arc, a
background track, and configurable round/butt end caps. Implemented for
both WebGL and Canvas2D backends.

Single-node, one draw call, animatable via `progress: 0..1`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@chiefcll chiefcll merged commit 57323de into main May 22, 2026
1 check passed
@chiefcll chiefcll deleted the feat/radial-progress-shader branch May 22, 2026 17:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant