Skip to content

feat(labels): per-label outline + glow effects with DebugPanel tuning#176

Open
rulkens wants to merge 16 commits into
mainfrom
worktree-label-text-effects
Open

feat(labels): per-label outline + glow effects with DebugPanel tuning#176
rulkens wants to merge 16 commits into
mainfrom
worktree-label-text-effects

Conversation

@rulkens
Copy link
Copy Markdown
Owner

@rulkens rulkens commented May 20, 2026

Summary

Implements the Label Text Effects plan — adds per-label outline (hard outside stroke) and glow (soft outside halo) effects to the MSDF text renderer.

  • Per-label opt-in fields on Label: outlineColor?, outlineEmFrac?, glowColor?, glowEmFrac?. Defaults are zero/transparent so existing producers render unchanged.
  • Straight-RGBA migration of Label.color — renderer now premultiplies at the pack-loop boundary; producers spell colours the natural way.
  • Atlas rebake at distanceRange = 16 (up from 4) so the SDF carries headroom for the up-to-12-px glow falloff tail.
  • Three-band fragment composite over(glow, over(outline, fill)) in premultiplied space. Overlapping bands (glow from glyph edge, outline overlays inner portion).
  • Quad expansion shader-side: the vertex stage grows each corner outward by max(outlineEmFrac, glowEmFrac) * ATLAS_EM_PX so the effect bands aren't clipped.
  • Live-tuning hook: labelStyleOverride module-scoped record + a category dropdown + four controls in a new LabelEffectsSection of the DebugPanel. Monotonic version counter feeds the label director's signature hash so slider edits force a re-flush.

Architecture summary

Layer Change
Type (Label.d.ts) +4 optional fields, color docstring updated to straight-RGBA
Renderer CPU (labelRenderer.ts) LABEL_DATA_BYTES 48 → 96; pack loop writes 24 slots/label with premultiplication
Shader struct (io.wesl) LabelData grows to 6 vec4s; VsOut carries outlineColor/glowColor/widths
Vertex (vertex.wesl) Quad expansion by effect fringe; em-fraction → SDF-units conversion
Fragment (fragment.wesl) Three-band composite with (1 - fillMask) masking on outline
Producers youAreHereSubsystem, poiSubsystem consult labelStyleOverride per frame
Director signatureOf includes override version so edits force re-flush
Debug UI New LabelEffectsSection with category dropdown + 4 controls

Test plan

  • Typecheck: npm run typecheck — clean ✅
  • Tests: npx vitest run — 1647/1647 pass ✅
  • Production build (WESL compile gate): npm run build — succeeds ✅
  • Visual verification — please run the dev server and:
    • Open the DebugPanel ("Skymap Debug"); expand "Label Effects".
    • Pick cluster as the target, adjust the outline-width slider; cluster labels (Coma, Virgo, etc.) gain a visible outline within one frame.
    • Switch target to youAreHere; the cluster outline disappears, the "You are here" label gains the outline.
    • Move target back to (off); every label should look identical to its pre-feature appearance.
    • Watch for: clipped halos at the glyph quad edge, missing fill colour, label aliasing past ~60 px em height.

Out of scope (deferred follow-up)

  • Baking the user's tuned values into POI_STYLES.* and youAreHereSubsystem's constants — a one-line-per-category edit that ships as a separate PR after visual tuning.
  • Removing the deprecated Label.pixelSize field (still required at the type level; ignored everywhere). Separate cleanup PR.

🤖 Generated with Claude Code

rulkens and others added 15 commits May 20, 2026 02:04
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Follow-up to bfade96 — the rebake bumped DISTANCE_RANGE_PX from 4 to 16
but missed updating the registry test's pinned constant.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Comments: timeless and terse — explain current state, not the journey.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The three-band rewrite dropped the educational notes about why median3
exists and how the texture_2d_array binding routes per-glyph fonts.
Restore them — they're orthogonal to the band math and genuinely useful.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Closing the DebugPanel mid-tune left the override active indefinitely,
which surprised users coming from the "(off)" dropdown's clean exit.
Add the cleanup so panel toggle and dropdown reset behave the same.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 20, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
skymap 110bf7b Commit Preview URL

Branch Preview URL
May 20 2026, 12:58 AM

The version counter alone only forces a re-flush IF a frame is already
running.  Render-on-demand sits idle between user inputs, so DebugPanel
slider edits stayed invisible until the user moved the mouse.

Add a registered wake callback; engine bootstrap wires it to
scheduler.requestRender so every set/clear pumps the loop on the next
tick.  Tests can leave the callback unregistered.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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