Composable vector effects
SVG filters: blur, shadows, and filter chains
An SVG filter is a small graphics pipeline. Each primitive receives an image, transforms it, and passes a named result to the next step. Start with one visible operation, then build the chain deliberately.
Define a filter and reference its ID
Place the filter element in defs, give it a unique ID, and apply it with filter="url(#id)". The filter definition is not painted by itself. It changes the rendering of the element or group that references it.
<defs>
<filter id="soft-blur">
<feGaussianBlur stdDeviation="3" />
</filter>
</defs>
<circle cx="80" cy="80" r="44"
fill="#2f6fed" filter="url(#soft-blur)" />The W3C filter effects specification describes a filter as a series of graphics operations. The output replaces the normal rendered source, while the source geometry and semantics remain in the document.
Understand SourceGraphic, SourceAlpha, input, and result
SourceGraphic is the rendered colour and alpha of the referenced element. SourceAlpha keeps its alpha shape but treats the colour channels as black. A primitive can store its output with result="name", and a later primitive selects it through in="name".
If in is omitted, the first primitive normally uses the source graphic and each later primitive receives the previous result. Name intermediate results when a chain branches or when you need to merge an effect with the original. Clear names such as blur, offsetShadow, and paint make source easier to debug than relying on implicit order.
Build a transparent drop shadow step by step
A useful manual shadow chain blurs the source alpha, offsets that blurred shape, colours it, then merges the result behind the original graphic. Modern SVG also defines feDropShadow, but the longer chain exposes how data moves between primitives.
<filter id="shadow" x="-25%" y="-25%"
width="150%" height="160%">
<feGaussianBlur in="SourceAlpha"
stdDeviation="4" result="blur" />
<feOffset in="blur" dx="3" dy="6" result="offset" />
<feFlood flood-color="#10203a" flood-opacity="0.45"
result="colour" />
<feComposite in="colour" in2="offset"
operator="in" result="shadowPaint" />
<feMerge>
<feMergeNode in="shadowPaint" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>feComposite with operator="in" keeps the flood colour only where the offset alpha exists. feMerge then controls stacking order: shadow first, source graphic second.
Expand the filter region when effects are clipped
Blur and offset can paint beyond the source bounding box. The filter region limits the area calculated and displayed, so a shadow that ends at a hard rectangle usually needs more room rather than a different blur value. The SVG defaults include some padding, but larger effects can exceed it.
Set x and y to negative values and increase width and height. Test every side at the smallest and largest intended sizes. Excessively large regions create larger temporary images, so use enough padding for the effect instead of an arbitrary canvas-sized region.
Choose filterUnits and primitiveUnits deliberately
The filter region uses filterUnits; primitive coordinates use primitiveUnits. With object-bounding-box units, percentages follow each target's bounds. With userSpaceOnUse, lengths align with the current SVG coordinate system.
Object-relative values make one definition adapt to differently sized objects, but they can stretch effects across very thin or wide shapes. User-space values make a shared blur or displacement consistent across an illustration. When an effect changes character between objects, inspect the units before changing the primitive values.
Use colour primitives without losing the source
feColorMatrix can adjust channels, saturation, hue, and alpha. feComponentTransfer can remap individual channels through identity, table, discrete, linear, or gamma functions. Because filters operate on rendered pixels, these primitives affect fills, strokes, gradients, and images together.
Test colour effects against transparency and the page background. A matrix can create values that are clamped to the allowed range, and filter calculations may use a colour space that differs from the values you expected. Use the MDN filter reference to check current element attributes and linked primitive documentation.
Debug one primitive at a time
- Confirm the filter ID exists and the
url(#id)reference matches it. - Reduce the filter to one primitive and verify the source is visible.
- Name each intermediate
resultand check everyinreference. - Expand the filter region when edges are clipped.
- Check object-bounding-box versus user-space units.
- Merge
SourceGraphicback when the chain should preserve the original. - Test transparency, scale, browser rendering, and export output.
Paste the complete document into SVG Vector Lab and keep the rendered canvas beside the source. Change one primitive at a time. If the effect changes the drawing bounds, use the viewBox guide; if several definitions have conflicting IDs, follow the optimization checklist.
Keep performance and accessibility in view
Most filter primitives produce intermediate RGBA images. Large regions, high blur values, many animated primitives, and effects applied to large groups can increase rendering work. Test on the actual target size and device, and prefer a simpler paint or gradient when it communicates the same visual result.
A filter is presentation, not an accessible name. Keep useful text, titles, descriptions, labels, and contrast in the unfiltered document. Verify that blur, glow, colour shifts, or animation do not make essential information hard to read, and respect reduced-motion preferences for animated effects.