feat(snippets): add filters category (8)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Showing 8 changed files with +634 and −0
added
snippets/filters/displacement-glitch.html
+85 −0
@@ -0,0 +1,85 @@ | ||
| 1 | +<!-- | |
| 2 | + ============================================================ | |
| 3 | + SVGarden — https://www.svgarden.dev | |
| 4 | + Author : Simon-Pierre Boucher | |
| 5 | + Contact: contact@spboucher.ai | |
| 6 | + File : snippets/filters/displacement-glitch.html | |
| 7 | + Desc : Periodic glitch distortion pulses on a logo mark | |
| 8 | + ============================================================ | |
| 9 | +--> | |
| 10 | +<!--svgarden | |
| 11 | +title: Displacement glitch | |
| 12 | +slug: displacement-glitch | |
| 13 | +category: filters | |
| 14 | +tags: [filter, glitch, displacement, logo] | |
| 15 | +difficulty: advanced | |
| 16 | +techniques: [feDisplacementMap, "feTurbulence (fractalNoise)", SMIL-keyTimes-discrete, rgb-split] | |
| 17 | +how_it_works: > | |
| 18 | + The tear comes from feTurbulence tuned anisotropically — baseFrequency is | |
| 19 | + nearly zero on X and high on Y, producing horizontal noise bands — which | |
| 20 | + feDisplacementMap converts into sideways slice shifts, the signature CRT | |
| 21 | + glitch. A SMIL animate drives the displacement scale through a values list | |
| 22 | + with calcMode="discrete", so the distortion jumps between states instead of | |
| 23 | + easing, arriving in stuttering bursts. Two offset copies of the mark in | |
| 24 | + broadcast magenta and cyan flash in sync via stepped CSS keyframes to fake | |
| 25 | + the RGB channel split of a failing signal. | |
| 26 | +customizable: | |
| 27 | + - { var: "--sg-color", label: "Mark color", type: color, default: "#7F77DD" } | |
| 28 | + - { var: "--sg-size", label: "Size", type: range, min: 48, max: 200, default: 96, unit: px } | |
| 29 | +created: 2026-08-10 | |
| 30 | +--> | |
| 31 | +<div class="sg-displacement-glitch" style="--sg-color:#7F77DD; --sg-size:96px;"> | |
| 32 | + <svg viewBox="0 0 32 32" aria-hidden="true" focusable="false"> | |
| 33 | + <defs> | |
| 34 | + <g id="sg-displacement-glitch-mark"> | |
| 35 | + <rect x="14.7" y="13" width="2.6" height="15" rx="1.3" /> | |
| 36 | + <path d="M16 18 C 16 12, 10 12, 7 7 C 14 7, 17 11, 16 18 Z" /> | |
| 37 | + <path d="M16 22 C 16 17, 21 17, 25 12 C 18 12, 15 16, 16 22 Z" /> | |
| 38 | + </g> | |
| 39 | + <filter id="sg-displacement-glitch-f" x="-30%" y="-30%" width="160%" height="160%"> | |
| 40 | + <feTurbulence type="fractalNoise" baseFrequency="0.002 0.55" numOctaves="1" seed="8" result="bands" /> | |
| 41 | + <feDisplacementMap in="SourceGraphic" in2="bands" xChannelSelector="R" yChannelSelector="G" scale="0"> | |
| 42 | + <animate attributeName="scale" dur="2.6s" repeatCount="indefinite" calcMode="discrete" | |
| 43 | + values="0; 0; 9; 2; 7; 0; 4; 0" keyTimes="0; 0.55; 0.6; 0.65; 0.7; 0.75; 0.78; 0.82" /> | |
| 44 | + </feDisplacementMap> | |
| 45 | + </filter> | |
| 46 | + </defs> | |
| 47 | + <use class="sg-displacement-glitch-r" href="#sg-displacement-glitch-mark" /> | |
| 48 | + <use class="sg-displacement-glitch-b" href="#sg-displacement-glitch-mark" /> | |
| 49 | + <use class="sg-displacement-glitch-main" href="#sg-displacement-glitch-mark" | |
| 50 | + filter="url(#sg-displacement-glitch-f)" /> | |
| 51 | + </svg> | |
| 52 | +</div> | |
| 53 | +<style> | |
| 54 | + .sg-displacement-glitch { | |
| 55 | + width: var(--sg-size); | |
| 56 | + height: var(--sg-size); | |
| 57 | + } | |
| 58 | + .sg-displacement-glitch svg { | |
| 59 | + display: block; | |
| 60 | + width: 100%; | |
| 61 | + height: 100%; | |
| 62 | + } | |
| 63 | + .sg-displacement-glitch-main { | |
| 64 | + fill: var(--sg-color); | |
| 65 | + } | |
| 66 | + .sg-displacement-glitch-r, | |
| 67 | + .sg-displacement-glitch-b { | |
| 68 | + opacity: 0; | |
| 69 | + animation: sg-displacement-glitch-split 2.6s steps(2, jump-none) infinite; | |
| 70 | + } | |
| 71 | + .sg-displacement-glitch-r { | |
| 72 | + fill: #f43f7d; | |
| 73 | + transform: translateX(-0.045em); | |
| 74 | + } | |
| 75 | + .sg-displacement-glitch-b { | |
| 76 | + fill: #2fd2f0; | |
| 77 | + transform: translateX(0.045em); | |
| 78 | + animation-delay: 0.05s; | |
| 79 | + } | |
| 80 | + @keyframes sg-displacement-glitch-split { | |
| 81 | + 0%, 54% { opacity: 0; } | |
| 82 | + 58%, 74% { opacity: 0.85; } | |
| 83 | + 82%, 100% { opacity: 0; } | |
| 84 | + } | |
| 85 | +</style> | |
added
snippets/filters/electric-border.html
+77 −0
@@ -0,0 +1,77 @@ | ||
| 1 | +<!-- | |
| 2 | + ============================================================ | |
| 3 | + SVGarden — https://www.svgarden.dev | |
| 4 | + Author : Simon-Pierre Boucher | |
| 5 | + Contact: contact@spboucher.ai | |
| 6 | + File : snippets/filters/electric-border.html | |
| 7 | + Desc : Crackling electric outline with a soft glow | |
| 8 | + ============================================================ | |
| 9 | +--> | |
| 10 | +<!--svgarden | |
| 11 | +title: Electric border | |
| 12 | +slug: electric-border | |
| 13 | +category: filters | |
| 14 | +tags: [filter, electric, glow, border] | |
| 15 | +difficulty: advanced | |
| 16 | +techniques: [feTurbulence, feDisplacementMap, "SMIL seed cycling", feMerge-glow] | |
| 17 | +how_it_works: > | |
| 18 | + A clean rounded rect goes through feTurbulence + feDisplacementMap so its | |
| 19 | + stroke wanders like an arc of electricity. The crackle is time-based but not | |
| 20 | + smooth on purpose: a SMIL animate cycles the turbulence seed through discrete | |
| 21 | + values, and every seed is an entirely new noise field, so the border snaps | |
| 22 | + between jitter states like real discharge. feGaussianBlur then makes a halo | |
| 23 | + from the displaced stroke and feMerge stacks glow twice under the crisp copy — | |
| 24 | + layering the same result is the cheap way to intensify a glow without | |
| 25 | + another primitive. | |
| 26 | +customizable: | |
| 27 | + - { var: "--sg-color", label: "Arc color", type: color, default: "#57C7F0" } | |
| 28 | + - { var: "--sg-size", label: "Width", type: range, min: 120, max: 340, default: 200, unit: px } | |
| 29 | +created: 2026-08-10 | |
| 30 | +--> | |
| 31 | +<div class="sg-electric-border" style="--sg-color:#57C7F0; --sg-size:200px;"> | |
| 32 | + <svg viewBox="0 0 200 120" aria-hidden="true" focusable="false"> | |
| 33 | + <defs> | |
| 34 | + <filter id="sg-electric-border-f" x="-25%" y="-25%" width="150%" height="150%"> | |
| 35 | + <feTurbulence type="turbulence" baseFrequency="0.045 0.09" numOctaves="2" seed="2" result="n"> | |
| 36 | + <animate attributeName="seed" values="2;7;12;17;23;29;2" dur="0.7s" | |
| 37 | + calcMode="discrete" repeatCount="indefinite" /> | |
| 38 | + </feTurbulence> | |
| 39 | + <feDisplacementMap in="SourceGraphic" in2="n" scale="8" | |
| 40 | + xChannelSelector="R" yChannelSelector="G" result="jag" /> | |
| 41 | + <feGaussianBlur in="jag" stdDeviation="3" result="glow" /> | |
| 42 | + <feMerge> | |
| 43 | + <feMergeNode in="glow" /> | |
| 44 | + <feMergeNode in="glow" /> | |
| 45 | + <feMergeNode in="jag" /> | |
| 46 | + </feMerge> | |
| 47 | + </filter> | |
| 48 | + </defs> | |
| 49 | + <rect class="sg-electric-border-frame" x="18" y="18" width="164" height="84" rx="12" /> | |
| 50 | + <rect class="sg-electric-border-arc" x="18" y="18" width="164" height="84" rx="12" | |
| 51 | + filter="url(#sg-electric-border-f)" /> | |
| 52 | + <path class="sg-electric-border-bolt" d="M106 38 L88 64 h11 l-7 20 19-28 h-11 z" /> | |
| 53 | + </svg> | |
| 54 | +</div> | |
| 55 | +<style> | |
| 56 | + .sg-electric-border { | |
| 57 | + width: var(--sg-size); | |
| 58 | + } | |
| 59 | + .sg-electric-border svg { | |
| 60 | + display: block; | |
| 61 | + width: 100%; | |
| 62 | + } | |
| 63 | + .sg-electric-border-frame { | |
| 64 | + fill: none; | |
| 65 | + stroke: var(--sg-color); | |
| 66 | + stroke-width: 1; | |
| 67 | + opacity: 0.25; | |
| 68 | + } | |
| 69 | + .sg-electric-border-arc { | |
| 70 | + fill: none; | |
| 71 | + stroke: var(--sg-color); | |
| 72 | + stroke-width: 2.2; | |
| 73 | + } | |
| 74 | + .sg-electric-border-bolt { | |
| 75 | + fill: var(--sg-color); | |
| 76 | + } | |
| 77 | +</style> | |
added
snippets/filters/frosted-glass-card.html
+104 −0
@@ -0,0 +1,104 @@ | ||
| 1 | +<!-- | |
| 2 | + ============================================================ | |
| 3 | + SVGarden — https://www.svgarden.dev | |
| 4 | + Author : Simon-Pierre Boucher | |
| 5 | + Contact: contact@spboucher.ai | |
| 6 | + File : snippets/filters/frosted-glass-card.html | |
| 7 | + Desc : Frosted glass card — backdrop blur over drifting color blobs + SVG grain | |
| 8 | + ============================================================ | |
| 9 | +--> | |
| 10 | +<!--svgarden | |
| 11 | +title: Frosted glass card | |
| 12 | +slug: frosted-glass-card | |
| 13 | +category: filters | |
| 14 | +tags: [filter, glass, backdrop, noise] | |
| 15 | +difficulty: advanced | |
| 16 | +techniques: [backdrop-filter, "feTurbulence (grain)", "@supports", SMIL-drift] | |
| 17 | +how_it_works: > | |
| 18 | + Two saturated blobs drift behind the card on SMIL timers; the card itself | |
| 19 | + paints nothing but a translucent white and lets backdrop-filter blur and | |
| 20 | + saturate whatever passes underneath — that live refraction is what separates | |
| 21 | + real glass from a static screenshot. A second SVG lays feTurbulence fractal | |
| 22 | + noise over the card at 5% opacity, the micro-grain that makes frosted glass | |
| 23 | + read as a material instead of a gradient. Everything sits behind an @supports | |
| 24 | + gate: browsers without backdrop-filter get a more opaque card that stays | |
| 25 | + perfectly legible, just not blurred. | |
| 26 | +support: > | |
| 27 | + backdrop-filter needs Chrome 76+, Safari 9+ (-webkit-), Firefox 103+. Without | |
| 28 | + it the card falls back to a solid translucent white via @supports. | |
| 29 | +customizable: | |
| 30 | + - { var: "--sg-color", label: "Blob A", type: color, default: "#7F77DD" } | |
| 31 | + - { var: "--sg-color2", label: "Blob B", type: color, default: "#3E8ED0" } | |
| 32 | + - { var: "--sg-size", label: "Width", type: range, min: 180, max: 380, default: 260, unit: px } | |
| 33 | +created: 2026-08-10 | |
| 34 | +--> | |
| 35 | +<div class="sg-frosted-glass-card" style="--sg-color:#7F77DD; --sg-color2:#3E8ED0; --sg-size:260px;"> | |
| 36 | + <svg class="sg-frosted-glass-card-blobs" viewBox="0 0 260 170" aria-hidden="true" focusable="false"> | |
| 37 | + <circle class="sg-frosted-glass-card-blob1" cx="70" cy="60" r="55"> | |
| 38 | + <animate attributeName="cx" values="70;180;70" dur="9s" repeatCount="indefinite" /> | |
| 39 | + <animate attributeName="cy" values="60;110;60" dur="7s" repeatCount="indefinite" /> | |
| 40 | + </circle> | |
| 41 | + <circle class="sg-frosted-glass-card-blob2" cx="190" cy="115" r="48"> | |
| 42 | + <animate attributeName="cx" values="190;80;190" dur="11s" repeatCount="indefinite" /> | |
| 43 | + <animate attributeName="cy" values="115;55;115" dur="8s" repeatCount="indefinite" /> | |
| 44 | + </circle> | |
| 45 | + </svg> | |
| 46 | + <div class="sg-frosted-glass-card-pane"> | |
| 47 | + <svg class="sg-frosted-glass-card-grain" aria-hidden="true" focusable="false"> | |
| 48 | + <filter id="sg-frosted-glass-card-noise"> | |
| 49 | + <feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2" /> | |
| 50 | + <feColorMatrix type="saturate" values="0" /> | |
| 51 | + </filter> | |
| 52 | + <rect width="100%" height="100%" filter="url(#sg-frosted-glass-card-noise)" /> | |
| 53 | + </svg> | |
| 54 | + <strong>Frosted glass</strong> | |
| 55 | + <span>blur happens live, behind the pane</span> | |
| 56 | + </div> | |
| 57 | +</div> | |
| 58 | +<style> | |
| 59 | + .sg-frosted-glass-card { | |
| 60 | + position: relative; | |
| 61 | + width: var(--sg-size); | |
| 62 | + aspect-ratio: 26 / 17; | |
| 63 | + } | |
| 64 | + .sg-frosted-glass-card-blobs { | |
| 65 | + position: absolute; | |
| 66 | + inset: 0; | |
| 67 | + width: 100%; | |
| 68 | + height: 100%; | |
| 69 | + } | |
| 70 | + .sg-frosted-glass-card-blob1 { fill: var(--sg-color); } | |
| 71 | + .sg-frosted-glass-card-blob2 { fill: var(--sg-color2); } | |
| 72 | + .sg-frosted-glass-card-pane { | |
| 73 | + position: absolute; | |
| 74 | + inset: 14% 12%; | |
| 75 | + border-radius: 14px; | |
| 76 | + border: 1px solid rgba(255, 255, 255, 0.65); | |
| 77 | + background: rgba(255, 255, 255, 0.62); | |
| 78 | + color: #1c1c24; | |
| 79 | + display: flex; | |
| 80 | + flex-direction: column; | |
| 81 | + justify-content: center; | |
| 82 | + gap: 0.2em; | |
| 83 | + padding: 1em 1.2em; | |
| 84 | + overflow: hidden; | |
| 85 | + font-family: inherit; | |
| 86 | + } | |
| 87 | + @supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { | |
| 88 | + .sg-frosted-glass-card-pane { | |
| 89 | + background: rgba(255, 255, 255, 0.28); | |
| 90 | + -webkit-backdrop-filter: blur(12px) saturate(160%); | |
| 91 | + backdrop-filter: blur(12px) saturate(160%); | |
| 92 | + } | |
| 93 | + } | |
| 94 | + .sg-frosted-glass-card-grain { | |
| 95 | + position: absolute; | |
| 96 | + inset: 0; | |
| 97 | + width: 100%; | |
| 98 | + height: 100%; | |
| 99 | + opacity: 0.05; | |
| 100 | + pointer-events: none; | |
| 101 | + } | |
| 102 | + .sg-frosted-glass-card-pane strong { font-size: 1.05em; } | |
| 103 | + .sg-frosted-glass-card-pane span { font-size: 0.8em; opacity: 0.72; } | |
| 104 | +</style> | |
added
snippets/filters/goo-menu.html
+85 −0
@@ -0,0 +1,85 @@ | ||
| 1 | +<!-- | |
| 2 | + ============================================================ | |
| 3 | + SVGarden — https://www.svgarden.dev | |
| 4 | + Author : Simon-Pierre Boucher | |
| 5 | + Contact: contact@spboucher.ai | |
| 6 | + File : snippets/filters/goo-menu.html | |
| 7 | + Desc : Gooey action menu — dots merge and separate like liquid | |
| 8 | + ============================================================ | |
| 9 | +--> | |
| 10 | +<!--svgarden | |
| 11 | +title: Goo menu | |
| 12 | +slug: goo-menu | |
| 13 | +category: filters | |
| 14 | +tags: [filter, goo, liquid, blob] | |
| 15 | +difficulty: advanced | |
| 16 | +techniques: [feGaussianBlur, "feColorMatrix (alpha threshold)", feBlend, filter-pipeline] | |
| 17 | +how_it_works: > | |
| 18 | + The gooey look is a two-step filter pipeline. feGaussianBlur first smears every | |
| 19 | + dot into a soft cloud, so where two dots come near, their alpha halos overlap | |
| 20 | + and add up. feColorMatrix then multiplies alpha by 19 and subtracts 9, a hard | |
| 21 | + threshold that crushes the blur back to a crisp edge — but the overlapping | |
| 22 | + halos survive the cut, becoming the liquid bridge between shapes. A final | |
| 23 | + feBlend redraws the original sharp circles on top so the dots stay clean while | |
| 24 | + the goo happens between them. | |
| 25 | +customizable: | |
| 26 | + - { var: "--sg-color", label: "Color", type: color, default: "#7F77DD" } | |
| 27 | + - { var: "--sg-size", label: "Size", type: range, min: 80, max: 240, default: 140, unit: px } | |
| 28 | + - { var: "--sg-duration", label: "Cycle", type: range, min: 1.5, max: 6, step: 0.1, default: 2.8, unit: s } | |
| 29 | +created: 2026-08-10 | |
| 30 | +--> | |
| 31 | +<div class="sg-goo-menu" style="--sg-color:#7F77DD; --sg-size:140px; --sg-duration:2.8s;"> | |
| 32 | + <svg viewBox="0 0 120 120" aria-hidden="true" focusable="false"> | |
| 33 | + <defs> | |
| 34 | + <filter id="sg-goo-menu-goo" x="-30%" y="-30%" width="160%" height="160%"> | |
| 35 | + <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur" /> | |
| 36 | + <feColorMatrix in="blur" mode="matrix" | |
| 37 | + values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 19 -9" result="goo" /> | |
| 38 | + <feBlend in="SourceGraphic" in2="goo" /> | |
| 39 | + </filter> | |
| 40 | + </defs> | |
| 41 | + <g filter="url(#sg-goo-menu-goo)"> | |
| 42 | + <circle class="sg-goo-menu-dot" cx="60" cy="60" r="15" /> | |
| 43 | + <circle class="sg-goo-menu-sat sg-goo-menu-sat1" cx="60" cy="60" r="11" /> | |
| 44 | + <circle class="sg-goo-menu-sat sg-goo-menu-sat2" cx="60" cy="60" r="11" /> | |
| 45 | + <circle class="sg-goo-menu-sat sg-goo-menu-sat3" cx="60" cy="60" r="11" /> | |
| 46 | + </g> | |
| 47 | + </svg> | |
| 48 | +</div> | |
| 49 | +<style> | |
| 50 | + .sg-goo-menu { | |
| 51 | + width: var(--sg-size); | |
| 52 | + height: var(--sg-size); | |
| 53 | + } | |
| 54 | + .sg-goo-menu svg { | |
| 55 | + display: block; | |
| 56 | + width: 100%; | |
| 57 | + height: 100%; | |
| 58 | + } | |
| 59 | + .sg-goo-menu circle { | |
| 60 | + fill: var(--sg-color); | |
| 61 | + } | |
| 62 | + .sg-goo-menu-sat { | |
| 63 | + animation-duration: var(--sg-duration); | |
| 64 | + animation-timing-function: ease-in-out; | |
| 65 | + animation-iteration-count: infinite; | |
| 66 | + } | |
| 67 | + .sg-goo-menu-sat1 { animation-name: sg-goo-menu-up; } | |
| 68 | + .sg-goo-menu-sat2 { animation-name: sg-goo-menu-right; } | |
| 69 | + .sg-goo-menu-sat3 { animation-name: sg-goo-menu-left; } | |
| 70 | + @keyframes sg-goo-menu-up { | |
| 71 | + 0%, 12% { transform: translate(0, 0); } | |
| 72 | + 45%, 65% { transform: translate(0, -35px); } | |
| 73 | + 100% { transform: translate(0, 0); } | |
| 74 | + } | |
| 75 | + @keyframes sg-goo-menu-right { | |
| 76 | + 0%, 12% { transform: translate(0, 0); } | |
| 77 | + 45%, 65% { transform: translate(31px, 18px); } | |
| 78 | + 100% { transform: translate(0, 0); } | |
| 79 | + } | |
| 80 | + @keyframes sg-goo-menu-left { | |
| 81 | + 0%, 12% { transform: translate(0, 0); } | |
| 82 | + 45%, 65% { transform: translate(-31px, 18px); } | |
| 83 | + 100% { transform: translate(0, 0); } | |
| 84 | + } | |
| 85 | +</style> | |
added
snippets/filters/heat-shimmer.html
+73 −0
@@ -0,0 +1,73 @@ | ||
| 1 | +<!-- | |
| 2 | + ============================================================ | |
| 3 | + SVGarden — https://www.svgarden.dev | |
| 4 | + Author : Simon-Pierre Boucher | |
| 5 | + Contact: contact@spboucher.ai | |
| 6 | + File : snippets/filters/heat-shimmer.html | |
| 7 | + Desc : Heat-haze shimmer rising over a flame | |
| 8 | + ============================================================ | |
| 9 | +--> | |
| 10 | +<!--svgarden | |
| 11 | +title: Heat shimmer | |
| 12 | +slug: heat-shimmer | |
| 13 | +category: filters | |
| 14 | +tags: [filter, heat, shimmer, flame] | |
| 15 | +difficulty: intermediate | |
| 16 | +techniques: [feTurbulence, feOffset-scrolling-noise, feDisplacementMap, gradient-mask] | |
| 17 | +how_it_works: > | |
| 18 | + Heat haze is displacement that travels: feTurbulence generates a stretched | |
| 19 | + noise field (stitchTiles keeps it seamless), a SMIL-animated feOffset scrolls | |
| 20 | + that field upward, and feDisplacementMap warps the flame with whatever noise | |
| 21 | + is currently passing through — so the wobble visibly rises like hot air. | |
| 22 | + The trick that sells it is the second, unfiltered flame on top, masked by a | |
| 23 | + vertical gradient: opaque at the base and transparent at the tip, it keeps | |
| 24 | + the bottom rock-solid so the shimmer appears to grow with height, the way | |
| 25 | + real convection does. | |
| 26 | +customizable: | |
| 27 | + - { var: "--sg-color", label: "Flame", type: color, default: "#E8853C" } | |
| 28 | + - { var: "--sg-size", label: "Size", type: range, min: 60, max: 220, default: 110, unit: px } | |
| 29 | +created: 2026-08-10 | |
| 30 | +--> | |
| 31 | +<div class="sg-heat-shimmer" style="--sg-color:#E8853C; --sg-size:110px;"> | |
| 32 | + <svg viewBox="0 0 120 140" aria-hidden="true" focusable="false"> | |
| 33 | + <defs> | |
| 34 | + <path id="sg-heat-shimmer-flame" | |
| 35 | + d="M60 16 C 72 38, 92 52, 92 84 A32 32 0 0 1 28 84 C 28 52, 48 38, 60 16 Z" /> | |
| 36 | + <filter id="sg-heat-shimmer-f" x="-30%" y="-40%" width="160%" height="180%"> | |
| 37 | + <feTurbulence type="fractalNoise" baseFrequency="0.03 0.12" numOctaves="2" | |
| 38 | + seed="3" stitchTiles="stitch" result="n" /> | |
| 39 | + <feOffset in="n" dx="0" dy="0" result="sn"> | |
| 40 | + <animate attributeName="dy" from="0" to="-50" dur="2.4s" repeatCount="indefinite" /> | |
| 41 | + </feOffset> | |
| 42 | + <feDisplacementMap in="SourceGraphic" in2="sn" scale="9" | |
| 43 | + xChannelSelector="R" yChannelSelector="G" /> | |
| 44 | + </filter> | |
| 45 | + <linearGradient id="sg-heat-shimmer-grad" x1="0" y1="1" x2="0" y2="0"> | |
| 46 | + <stop offset="0.25" stop-color="white" /> | |
| 47 | + <stop offset="0.85" stop-color="black" /> | |
| 48 | + </linearGradient> | |
| 49 | + <mask id="sg-heat-shimmer-solid"> | |
| 50 | + <rect x="0" y="0" width="120" height="140" fill="url(#sg-heat-shimmer-grad)" /> | |
| 51 | + </mask> | |
| 52 | + </defs> | |
| 53 | + <use class="sg-heat-shimmer-hazy" href="#sg-heat-shimmer-flame" filter="url(#sg-heat-shimmer-f)" /> | |
| 54 | + <use class="sg-heat-shimmer-base" href="#sg-heat-shimmer-flame" mask="url(#sg-heat-shimmer-solid)" /> | |
| 55 | + <path class="sg-heat-shimmer-core" d="M60 62 C 66 76, 76 82, 76 96 A16 16 0 0 1 44 96 C 44 82, 54 76, 60 62 Z" /> | |
| 56 | + </svg> | |
| 57 | +</div> | |
| 58 | +<style> | |
| 59 | + .sg-heat-shimmer { | |
| 60 | + width: var(--sg-size); | |
| 61 | + } | |
| 62 | + .sg-heat-shimmer svg { | |
| 63 | + display: block; | |
| 64 | + width: 100%; | |
| 65 | + } | |
| 66 | + .sg-heat-shimmer-hazy, | |
| 67 | + .sg-heat-shimmer-base { | |
| 68 | + fill: var(--sg-color); | |
| 69 | + } | |
| 70 | + .sg-heat-shimmer-core { | |
| 71 | + fill: rgba(255, 255, 255, 0.5); | |
| 72 | + } | |
| 73 | +</style> | |
added
snippets/filters/ink-bleed-reveal.html
+71 −0
@@ -0,0 +1,71 @@ | ||
| 1 | +<!-- | |
| 2 | + ============================================================ | |
| 3 | + SVGarden — https://www.svgarden.dev | |
| 4 | + Author : Simon-Pierre Boucher | |
| 5 | + Contact: contact@spboucher.ai | |
| 6 | + File : snippets/filters/ink-bleed-reveal.html | |
| 7 | + Desc : Content revealed by an organically spreading ink blot | |
| 8 | + ============================================================ | |
| 9 | +--> | |
| 10 | +<!--svgarden | |
| 11 | +title: Ink bleed reveal | |
| 12 | +slug: ink-bleed-reveal | |
| 13 | +category: filters | |
| 14 | +tags: [filter, mask, ink, reveal] | |
| 15 | +difficulty: advanced | |
| 16 | +techniques: [animated-mask, feTurbulence, feDisplacementMap, feMorphology] | |
| 17 | +how_it_works: > | |
| 18 | + The reveal is a plain white circle growing inside a mask — but the circle | |
| 19 | + passes through a filter before the mask samples it, and that changes | |
| 20 | + everything. feTurbulence + feDisplacementMap chew the circle's rim into an | |
| 21 | + irregular coastline, and feMorphology dilates the result so thin tendrils | |
| 22 | + thicken like ink wicking into paper. Because masks read luminance, whatever | |
| 23 | + the filter does to the white shape directly sculpts what's visible: filter | |
| 24 | + the mask, not the artwork, and any reveal can borrow any texture. | |
| 25 | +customizable: | |
| 26 | + - { var: "--sg-color", label: "Ink", type: color, default: "#7F77DD" } | |
| 27 | + - { var: "--sg-size", label: "Width", type: range, min: 140, max: 380, default: 240, unit: px } | |
| 28 | +created: 2026-08-10 | |
| 29 | +--> | |
| 30 | +<div class="sg-ink-bleed-reveal" style="--sg-color:#7F77DD; --sg-size:240px;"> | |
| 31 | + <svg viewBox="0 0 200 100" aria-hidden="true" focusable="false"> | |
| 32 | + <defs> | |
| 33 | + <filter id="sg-ink-bleed-reveal-rough" x="-40%" y="-40%" width="180%" height="180%"> | |
| 34 | + <feTurbulence type="fractalNoise" baseFrequency="0.09" numOctaves="3" seed="7" result="n" /> | |
| 35 | + <feDisplacementMap in="SourceGraphic" in2="n" scale="28" xChannelSelector="R" yChannelSelector="G" result="torn" /> | |
| 36 | + <feMorphology in="torn" operator="dilate" radius="1.4" /> | |
| 37 | + </filter> | |
| 38 | + <mask id="sg-ink-bleed-reveal-m"> | |
| 39 | + <rect width="200" height="100" fill="black" /> | |
| 40 | + <circle cx="100" cy="50" r="2" fill="white" filter="url(#sg-ink-bleed-reveal-rough)"> | |
| 41 | + <animate attributeName="r" values="2;78;78;2" keyTimes="0;0.45;0.8;1" dur="6s" repeatCount="indefinite" /> | |
| 42 | + </circle> | |
| 43 | + </mask> | |
| 44 | + </defs> | |
| 45 | + <g mask="url(#sg-ink-bleed-reveal-m)"> | |
| 46 | + <rect class="sg-ink-bleed-reveal-panel" x="10" y="14" width="180" height="72" rx="10" /> | |
| 47 | + <text class="sg-ink-bleed-reveal-word" x="100" y="61">ink bleed</text> | |
| 48 | + </g> | |
| 49 | + </svg> | |
| 50 | +</div> | |
| 51 | +<style> | |
| 52 | + .sg-ink-bleed-reveal { | |
| 53 | + width: var(--sg-size); | |
| 54 | + } | |
| 55 | + .sg-ink-bleed-reveal svg { | |
| 56 | + display: block; | |
| 57 | + width: 100%; | |
| 58 | + } | |
| 59 | + .sg-ink-bleed-reveal-panel { | |
| 60 | + fill: var(--sg-color); | |
| 61 | + opacity: 0.14; | |
| 62 | + } | |
| 63 | + .sg-ink-bleed-reveal-word { | |
| 64 | + font-family: inherit; | |
| 65 | + font-size: 32px; | |
| 66 | + font-weight: 700; | |
| 67 | + letter-spacing: 1px; | |
| 68 | + text-anchor: middle; | |
| 69 | + fill: var(--sg-color); | |
| 70 | + } | |
| 71 | +</style> | |
added
snippets/filters/paper-cutout.html
+71 −0
@@ -0,0 +1,71 @@ | ||
| 1 | +<!-- | |
| 2 | + ============================================================ | |
| 3 | + SVGarden — https://www.svgarden.dev | |
| 4 | + Author : Simon-Pierre Boucher | |
| 5 | + Contact: contact@spboucher.ai | |
| 6 | + File : snippets/filters/paper-cutout.html | |
| 7 | + Desc : Layered paper cut-out depth from stacked feDropShadow | |
| 8 | + ============================================================ | |
| 9 | +--> | |
| 10 | +<!--svgarden | |
| 11 | +title: Paper cutout | |
| 12 | +slug: paper-cutout | |
| 13 | +category: filters | |
| 14 | +tags: [filter, paper, layers, depth] | |
| 15 | +difficulty: intermediate | |
| 16 | +techniques: [feDropShadow, layered-composition, translucent-stacking, drift-animation] | |
| 17 | +how_it_works: > | |
| 18 | + Each wavy layer carries its own feDropShadow filter, and the offsets grow | |
| 19 | + with depth — 1.5px of shadow for the top sheet, 6px for the deepest — which | |
| 20 | + is exactly how physically stacked paper casts light, so the eye reads | |
| 21 | + elevation without any 3D transform. The sheets share one fill color and | |
| 22 | + differ only in opacity; where translucent layers overlap they compound into | |
| 23 | + darker shades, giving a free tonal ramp that adapts to any background. A slow | |
| 24 | + horizontal drift at a different speed per layer keeps the parallax alive so | |
| 25 | + the depth reads even in peripheral vision. | |
| 26 | +customizable: | |
| 27 | + - { var: "--sg-color", label: "Paper", type: color, default: "#7F77DD" } | |
| 28 | + - { var: "--sg-size", label: "Width", type: range, min: 160, max: 400, default: 250, unit: px } | |
| 29 | + - { var: "--sg-duration", label: "Drift", type: range, min: 2, max: 12, step: 0.5, default: 6, unit: s } | |
| 30 | +created: 2026-08-10 | |
| 31 | +--> | |
| 32 | +<div class="sg-paper-cutout" style="--sg-color:#7F77DD; --sg-size:250px; --sg-duration:6s;"> | |
| 33 | + <svg viewBox="0 0 240 140" aria-hidden="true" focusable="false"> | |
| 34 | + <defs> | |
| 35 | + <filter id="sg-paper-cutout-s1"><feDropShadow dx="0" dy="1.5" stdDeviation="1.5" flood-opacity="0.3" /></filter> | |
| 36 | + <filter id="sg-paper-cutout-s2"><feDropShadow dx="0" dy="2.5" stdDeviation="2.5" flood-opacity="0.3" /></filter> | |
| 37 | + <filter id="sg-paper-cutout-s3"><feDropShadow dx="0" dy="4" stdDeviation="3.5" flood-opacity="0.3" /></filter> | |
| 38 | + <filter id="sg-paper-cutout-s4"><feDropShadow dx="0" dy="6" stdDeviation="5" flood-opacity="0.3" /></filter> | |
| 39 | + </defs> | |
| 40 | + <path class="sg-paper-cutout-l1" filter="url(#sg-paper-cutout-s1)" | |
| 41 | + d="M-20 52 C 30 30, 90 66, 130 48 C 170 32, 210 52, 260 40 V150 H-20 Z" /> | |
| 42 | + <path class="sg-paper-cutout-l2" filter="url(#sg-paper-cutout-s2)" | |
| 43 | + d="M-20 78 C 40 60, 90 94, 140 76 C 185 62, 215 82, 260 70 V150 H-20 Z" /> | |
| 44 | + <path class="sg-paper-cutout-l3" filter="url(#sg-paper-cutout-s3)" | |
| 45 | + d="M-20 102 C 35 88, 95 118, 145 100 C 190 86, 220 106, 260 96 V150 H-20 Z" /> | |
| 46 | + <path class="sg-paper-cutout-l4" filter="url(#sg-paper-cutout-s4)" | |
| 47 | + d="M-20 124 C 45 112, 100 136, 150 122 C 195 110, 225 128, 260 120 V150 H-20 Z" /> | |
| 48 | + </svg> | |
| 49 | +</div> | |
| 50 | +<style> | |
| 51 | + .sg-paper-cutout { | |
| 52 | + width: var(--sg-size); | |
| 53 | + } | |
| 54 | + .sg-paper-cutout svg { | |
| 55 | + display: block; | |
| 56 | + width: 100%; | |
| 57 | + overflow: hidden; | |
| 58 | + } | |
| 59 | + .sg-paper-cutout path { | |
| 60 | + fill: var(--sg-color); | |
| 61 | + animation: sg-paper-cutout-drift var(--sg-duration) ease-in-out infinite alternate; | |
| 62 | + } | |
| 63 | + .sg-paper-cutout-l1 { opacity: 0.32; } | |
| 64 | + .sg-paper-cutout-l2 { opacity: 0.5; animation-duration: calc(var(--sg-duration) * 1.35); } | |
| 65 | + .sg-paper-cutout-l3 { opacity: 0.72; animation-duration: calc(var(--sg-duration) * 1.7); } | |
| 66 | + .sg-paper-cutout-l4 { animation-duration: calc(var(--sg-duration) * 2.1); } | |
| 67 | + @keyframes sg-paper-cutout-drift { | |
| 68 | + from { transform: translateX(-5px); } | |
| 69 | + to { transform: translateX(5px); } | |
| 70 | + } | |
| 71 | +</style> | |
added
snippets/filters/turbulence-water.html
+68 −0
@@ -0,0 +1,68 @@ | ||
| 1 | +<!-- | |
| 2 | + ============================================================ | |
| 3 | + SVGarden — https://www.svgarden.dev | |
| 4 | + Author : Simon-Pierre Boucher | |
| 5 | + Contact: contact@spboucher.ai | |
| 6 | + File : snippets/filters/turbulence-water.html | |
| 7 | + Desc : Living water ripple via animated feTurbulence displacement | |
| 8 | + ============================================================ | |
| 9 | +--> | |
| 10 | +<!--svgarden | |
| 11 | +title: Turbulence water | |
| 12 | +slug: turbulence-water | |
| 13 | +category: filters | |
| 14 | +tags: [filter, water, ripple, turbulence] | |
| 15 | +difficulty: advanced | |
| 16 | +techniques: [feTurbulence, feDisplacementMap, SMIL-on-filter-primitives, xChannelSelector] | |
| 17 | +how_it_works: > | |
| 18 | + feTurbulence generates a field of Perlin noise, and feDisplacementMap uses that | |
| 19 | + field as a map: each pixel of the source is pushed sideways by the red channel | |
| 20 | + and vertically by the green channel, so smooth noise becomes a watery warp. | |
| 21 | + The ripple lives because a SMIL animate retunes baseFrequency in a slow | |
| 22 | + loop — changing the noise's spatial scale continuously reshapes the whole | |
| 23 | + field, which reads as water moving. The pale stripes inside the shape exist | |
| 24 | + purely to give the displacement visible material to bend. | |
| 25 | +customizable: | |
| 26 | + - { var: "--sg-color", label: "Water", type: color, default: "#3E8ED0" } | |
| 27 | + - { var: "--sg-size", label: "Width", type: range, min: 100, max: 320, default: 180, unit: px } | |
| 28 | +created: 2026-08-10 | |
| 29 | +--> | |
| 30 | +<div class="sg-turbulence-water" style="--sg-color:#3E8ED0; --sg-size:180px;"> | |
| 31 | + <svg viewBox="0 0 160 110" aria-hidden="true" focusable="false"> | |
| 32 | + <defs> | |
| 33 | + <filter id="sg-turbulence-water-f" x="-15%" y="-15%" width="130%" height="130%"> | |
| 34 | + <feTurbulence type="turbulence" baseFrequency="0.014 0.045" numOctaves="2" seed="4" result="noise"> | |
| 35 | + <animate attributeName="baseFrequency" dur="7s" repeatCount="indefinite" | |
| 36 | + values="0.014 0.045; 0.02 0.06; 0.014 0.045" /> | |
| 37 | + </feTurbulence> | |
| 38 | + <feDisplacementMap in="SourceGraphic" in2="noise" scale="11" | |
| 39 | + xChannelSelector="R" yChannelSelector="G" /> | |
| 40 | + </filter> | |
| 41 | + </defs> | |
| 42 | + <g filter="url(#sg-turbulence-water-f)"> | |
| 43 | + <rect class="sg-turbulence-water-pool" x="14" y="14" width="132" height="82" rx="18" /> | |
| 44 | + <path class="sg-turbulence-water-line" d="M26 38 H134" /> | |
| 45 | + <path class="sg-turbulence-water-line" d="M26 56 H134" /> | |
| 46 | + <path class="sg-turbulence-water-line" d="M26 74 H134" /> | |
| 47 | + </g> | |
| 48 | + </svg> | |
| 49 | +</div> | |
| 50 | +<style> | |
| 51 | + .sg-turbulence-water { | |
| 52 | + width: var(--sg-size); | |
| 53 | + } | |
| 54 | + .sg-turbulence-water svg { | |
| 55 | + display: block; | |
| 56 | + width: 100%; | |
| 57 | + } | |
| 58 | + .sg-turbulence-water-pool { | |
| 59 | + fill: var(--sg-color); | |
| 60 | + opacity: 0.85; | |
| 61 | + } | |
| 62 | + .sg-turbulence-water-line { | |
| 63 | + fill: none; | |
| 64 | + stroke: rgba(255, 255, 255, 0.55); | |
| 65 | + stroke-width: 3; | |
| 66 | + stroke-linecap: round; | |
| 67 | + } | |
| 68 | +</style> | |
| 69 | ||