SPB Git

spb/svgarden Public

SVGarden — searchable bank of 74 self-contained SVG+CSS animation snippets (svgarden.dev)

HTML 79.2% Astro 10.3% JavaScript 6% CSS 3.7% Shell 0.8%

feat(snippets): add filters category (8)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed 15 h ago (Aug 10, 2026) parent 7ed7bbb

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