audio streaming app plyr.fm
37
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: eliminate diagonal banding in live theme ambient gradient (#1298)

the 135-degree gradient with only 7 color stops and a brightness-oscillating
animation created visible diagonal strips (color banding), especially in
dark/low-contrast weather palettes.

three fixes:
- increase gradient interpolation from 7 to 16 stops
- add SVG fractalNoise dither filter (soft-light blend) on the gradient layer
- remove brightness(1.08) from breathing animation (amplified banding)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

authored by

nate nowack
Claude Opus 4.6 (1M context)
and committed by
GitHub
19d8c7c4 c12924e4

+20 -10
+6 -6
frontend/src/lib/ambient.svelte.ts
··· 68 68 return Math.min(1, Math.max(0, (temperature - cold) / (hot - cold))); 69 69 } 70 70 71 - /** interpolate 3 color stops into 7 for smoother gradients (reduces banding) */ 71 + /** interpolate 3 color stops into 16 for smoother gradients (reduces banding) */ 72 72 function smoothGradient(c1: RGB, c2: RGB, c3: RGB): string { 73 73 const lerp = (a: number, b: number, t: number) => Math.round(a + (b - a) * t); 74 74 const stops: string[] = []; 75 - // 4 stops from c1→c2, 4 stops from c2→c3 (c2 shared = 7 total) 76 - for (let i = 0; i <= 3; i++) { 77 - const t = i / 3; 75 + const HALF = 8; // 8 stops per segment, shared midpoint = 15 total 76 + for (let i = 0; i <= HALF; i++) { 77 + const t = i / HALF; 78 78 stops.push(`rgb(${lerp(c1.r, c2.r, t)}, ${lerp(c1.g, c2.g, t)}, ${lerp(c1.b, c2.b, t)})`); 79 79 } 80 - for (let i = 1; i <= 3; i++) { 81 - const t = i / 3; 80 + for (let i = 1; i <= HALF - 1; i++) { 81 + const t = i / (HALF - 1); 82 82 stops.push(`rgb(${lerp(c2.r, c3.r, t)}, ${lerp(c2.g, c3.g, t)}, ${lerp(c2.b, c3.b, t)})`); 83 83 } 84 84 return `linear-gradient(135deg, ${stops.join(', ')})`;
+13 -3
frontend/src/routes/+layout.svelte
··· 450 450 </script> 451 451 </svelte:head> 452 452 453 + <!-- SVG noise filter for gradient dithering (eliminates color banding) --> 454 + <svg style="position:absolute;width:0;height:0" aria-hidden="true"> 455 + <filter id="ambient-noise"> 456 + <feTurbulence type="fractalNoise" baseFrequency="0.7" numOctaves="4" stitchTiles="stitch" /> 457 + <feColorMatrix type="saturate" values="0" /> 458 + <feBlend in="SourceGraphic" mode="soft-light" /> 459 + </filter> 460 + </svg> 461 + 453 462 <div class="app-layout"> 454 463 <main class="main-content" class:with-queue={showQueue && !isEmbed}> 455 464 {@render children?.()} ··· 626 635 -webkit-font-smoothing: antialiased; 627 636 } 628 637 629 - /* ambient weather gradient layer */ 638 + /* ambient weather gradient layer + noise dither to eliminate color banding */ 630 639 :global(body::after) { 631 640 content: ''; 632 641 position: fixed; ··· 641 650 :global(body.ambient-active::after) { 642 651 opacity: 0.4; 643 652 animation: ambient-drift 12s ease-in-out infinite; 653 + filter: url(#ambient-noise); 644 654 } 645 655 646 656 @keyframes -global-ambient-drift { 647 - 0%, 100% { opacity: 0.35; filter: brightness(1); } 648 - 50% { opacity: 0.5; filter: brightness(1.08); } 657 + 0%, 100% { opacity: 0.35; } 658 + 50% { opacity: 0.45; } 649 659 } 650 660 651 661 /* background image with blur effect */
+1 -1
loq.toml
··· 132 132 133 133 [[rules]] 134 134 path = "frontend/src/routes/+layout.svelte" 135 - max_lines = 750 135 + max_lines = 760 136 136 137 137 [[rules]] 138 138 path = "frontend/src/routes/costs/+page.svelte"