11.2 Creating a Drop Shadow

Establishing the Filter's Bounds

  1. The <filter> element has attributes that describe the clipping region for a filter.
    You specify an x, y, width, and height in terms of the percentage of the filtered object's bounding box. (That is the default.)
    Any portion of the resulting output that is outside the bounds will not be displayed.
  2. If you are intending to apply a filter to many objects, you may want to omit these attributes altogether and take the default values of
    x="-10%", y="-10%", "width=120%", height="120%".
    This gives extra space for filters — such as the drop shadow that we're constructing—that produce output larger than their input.
  3. These attributes are in terms of the filtered object's bounding box; specifically, filterUnits has a value of objectBoundingBox by default. .
  4. If you wish to specify boundaries in user units, then set the attribute's value to userSpaceOnUse

Example 1.The first attempt to produce a drop shadow on the flower, using the <feGaussianBlur> filter primitive.

You specify
<svg width="400" height="400" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000">
    <filter id="drop-shadow">
         <feGaussianBlur
            in="SourceAlpha"
            stdDeviation="1"/>
    </filter>
    <use xlink:href="svg_es_11_0_filters_flower.svg#flower" filter="url(#drop-shadow)"/>
 </svg>


Don't be surprised;
remember, the filter returns the output, which is a blurred alpha channel,
instead of the original source graphic.
You can get the effect you want by adding #flower transferred by some vector.

Example 2. Second attempt by adding transferred #flower

<svg width="400" height="400" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000">
   <filter id="drop-shadow">
        <feGaussianBlur
          in="SourceAlpha"
          stdDeviation="1"/>
   </filter>
   <use xlink:href="svg_es_11_0_filters_flower.svg#flower" filter="url(#drop-shadow)"/>
   <use xlink:href="svg_es_11_0_filters_flower.svg#flower" transform="translate(-2,-2)"/>
</svg>

Storing, Chaining, and Merging Filter Results

Example 3. Improved drop shadow filter

<svg width="400" height="400" 
              viewBox="0 0 100 100" 
              xmlns="http://www.w3.org/2000">
                 <filter id="drop-shadow">
                      <feGaussianBlur
                            in="SourceAlpha"
                            stdDeviation="1"
                            result="blur"/>
                      <feOffset
                            in="blur"
                            dx="2" dy="2"
                            result="offsetBlur"/>
                      <feMerge>
                          <feMergeNode in="offsetBlur"/>
                          <feMergeNode in="SourceGraphic"/>
                      </feMerge>
                 </filter>
                 <use xlink:href="svg_es_11_0_filters_flower.svg#flower" 
                 filter="url(#drop-shadow)"/>
              </svg>
  1. The result attribute specifies that the result of this primitive can be referenced later by the name blur.
    This isn't like an XML id; the name you give is a local name that's valid only for the duration of the primitives contained in the current <filter>.
  2. The <feOffset> primitive takes its input, in this case the blur result from the Gaussian blur, offsets it by the specified dx and dy values, and stores the resulting bitmap under the name offsetBlur
  3. The <feMerge> primitive encloses a list of <feMergeNode> elements, each of which specifies an input. The inputs are stacked one on top of another in the order that they appear. In this case, you want the offsetBlur below the original SourceGraphic.
Claude Opus 4.5 - Why the method in Example 3 is better than the method in Example 2 - Demonstration

Demo A: Reusability - Apply shadow to multiple objects with one filter definition

Method 2 (two <use> per object) - 6 elements needed:

<filter id="demo-shadow-2">
    <feGaussianBlur in="SourceAlpha" stdDeviation="1"/>
  </filter>
  <circle cx="30" cy="40" r="15" fill="gray" filter="url(#demo-shadow-2)"/>
  <circle cx="28" cy="38" r="15" fill="red"/>

Method 3 (feMerge) - 3 elements needed:

<filter id="demo-shadow-3">
    <feGaussianBlur in="SourceAlpha" stdDeviation="1" result="blur"/>
    <feOffset in="blur" dx="2" dy="2" result="offsetBlur"/>
    <feMerge>
      <feMergeNode in="offsetBlur"/>
      <feMergeNode in="SourceGraphic"/>
    </feMerge>
  </filter>
  <circle cx="30" cy="40" r="15" fill="red" filter="url(#demo-shadow-3)"/>

Demo B: Animation - Shadow follows automatically with Method 3

Method 2 - Shadow does NOT follow (broken):

<circle cx="25" cy="25" r="12" fill="gray" 
        filter="url(#anim-shadow-2)"/>
  <circle cx="25" cy="25" r="12" fill="orange">
    <animate attributeName="cx" 
    values="25;125;25" 
    dur="3s" repeatCount="indefinite"/>
  </circle>

Method 3 - Shadow follows correctly:

<circle cx="25" cy="25" r="12" fill="orange" 
              filter="url(#anim-shadow-3)">
    <animate attributeName="cx" 
    values="25;125;25" dur="3s" 
    repeatCount="indefinite"/>
  </circle>