10.2 Masking
After consulting with Claude Opus 4.5- An
objectBoundingBoxof a shape S is the smallest axis-aligned rectangle that fully contains S. - If a mask has a shape M and it masks a shape S then
- The shape S becomes transparent by default (where there is no mask content)
- The transparency for each pixel of the common part of shape S and shape M is calculated by the formula:
a = (0.2125 * Mred + 0.7154 * Mgreen + 0.0721 * Mblue) * Malpha
where the pixel of the mask shape M has the colorrgba(Mred, Mgreen, Mblue, Malpha)with values normalized from 0 to 1.
- From the above formula you can conclude that:
- White areas in the mask (high luminance) → fully visible
- Black areas in the mask (zero luminance) → fully transparent
- Gray or colored areas → partially transparent
- The
objectBoundingBoxis used for coordinate systems, not visibility:maskUnits(default:objectBoundingBox) — the mask'sx,y,width,heightare relative to the masked object's bounding box (0-1 range)maskContentUnits(default:userSpaceOnUse) — the shapes inside the mask use SVG canvas coordinates- If you set
maskContentUnits="objectBoundingBox", shapes inside the mask use bounding box coordinates where (0,0) is top-left and (1,1) is bottom-right of the masked object
Simple Mask Example
<svg width="400" height="150">
<defs>
<mask id="circleMask">
<rect width="100%" height="100%" fill="red"/>
<circle cx="300" cy="75" r="50" fill="white"/>
</mask>
</defs>
<rect x="10" y="25" width="180" height="100" fill="steelblue"/>
<text x="100" y="145" text-anchor="middle" font-size="12">Original</text>
<rect x="210" y="25" width="180" height="100" fill="steelblue" mask="url(#circleMask)"/>
<text x="300" y="145" text-anchor="middle" font-size="12">With Mask</text>
</svg>
The mask has a red background (partially transparent, ~21% opacity) and a white circle at cx="300" (fully visible). The blue rectangle shows through both areas with different transparency levels.
Four Ways to Achieve the Same Mask Result
In this picture the steelblue rectangle is going to be masked. But here it is not masked yet.
<svg width="200" height="150" style="background-color:orange;"> <rect x="50" y="25" width="100" height="100" fill="steelblue"/> <path d="M 60 30 L 100 70 L 130 40 Z" style="fill:red"/> <rect x="70" y="45" width="60" height="60" fill="none" stroke="red"/> </svg>
Why additionaly orange background and red elements (triangle and empty square)?
- Orange background will show that masked areas become truly transparent (don't confuse transparent with white)
- Red elements will not be not masked — it proves the mask only affects the element it's applied to, not the entire bounding box area
There are four ways to produce the same visual result by masking with a circular shape in the center.
The differences depend on the choice of maskUnits and maskContentUnits.
| Example | maskUnits | maskContentUnits | Circle coordinates |
|---|---|---|---|
| 1 | objectBoundingBox (default) | userSpaceOnUse (default) | cx=100, cy=75, r=30 |
| 2 | userSpaceOnUse | userSpaceOnUse | cx=100, cy=75, r=30 |
| 3 | objectBoundingBox (default) | objectBoundingBox | cx=0.5, cy=0.5, r=0.3 |
| 4 | userSpaceOnUse | objectBoundingBox | cx=0.5, cy=0.5, r=0.3 |
1. Default: maskUnits="objectBoundingBox", maskContentUnits="userSpaceOnUse"
<svg width="200" height="150" style="background-color:orange;">
<defs>
<mask id="mask1">
<circle cx="100" cy="75" r="30" fill="white"/>
</mask>
</defs>
<rect x="50" y="25" width="100" height="100" fill="steelblue" mask="url(#mask1)"/>
<path d="M 60 30 L 100 70 L 130 40 Z" style="fill:red"/>
<rect x="70" y="45" width="60" height="60" fill="none" stroke="red"/>
</svg>
Circle uses SVG canvas coordinates: cx=100 is center of rectangle (50 + 100/2), cy=75 is center (25 + 100/2).
2. maskUnits="userSpaceOnUse", maskContentUnits="userSpaceOnUse"
<svg width="200" height="150" style="background-color:orange;">
<defs>
<mask id="mask2" maskUnits="userSpaceOnUse" x="50" y="25" width="100" height="100">
<circle cx="100" cy="75" r="30" fill="white"/>
</mask>
</defs>
<rect x="50" y="25" width="100" height="100" fill="steelblue" mask="url(#mask2)"/>
<path d="M 60 30 L 100 70 L 130 40 Z" style="fill:red"/>
<rect x="70" y="45" width="60" height="60" fill="none" stroke="red"/>
</svg>
Mask boundary and content both use SVG canvas coordinates.
Note: Unlike Example 1 (which uses default maskUnits="objectBoundingBox"), here we must explicitly specify x="50" y="25" width="100" height="100" because maskUnits="userSpaceOnUse" requires the mask boundary to be defined in SVG canvas coordinates.
3. maskUnits="objectBoundingBox", maskContentUnits="objectBoundingBox"
<svg width="200" height="150" style="background-color:orange;">
<defs>
<mask id="mask3" maskContentUnits="objectBoundingBox">
<circle cx="0.5" cy="0.5" r="0.3" fill="white"/>
</mask>
</defs>
<rect x="50" y="25" width="100" height="100" fill="steelblue" mask="url(#mask3)"/>
<path d="M 60 30 L 100 70 L 130 40 Z" style="fill:red"/>
<rect x="70" y="45" width="60" height="60" fill="none" stroke="red"/>
</svg>
Circle uses bounding box coordinates: cx=0.5 means center, r=0.3 means 30% of bounding box size.
4. maskUnits="userSpaceOnUse", maskContentUnits="objectBoundingBox"
<svg width="200" height="150" style="background-color:orange;">
<defs>
<mask id="mask4" maskUnits="userSpaceOnUse" x="50" y="25" width="100" height="100" maskContentUnits="objectBoundingBox">
<circle cx="0.5" cy="0.5" r="0.3" fill="white"/>
</mask>
</defs>
<rect x="50" y="25" width="100" height="100" fill="steelblue" mask="url(#mask4)"/>
<path d="M 60 30 L 100 70 L 130 40 Z" style="fill:red"/>
<rect x="70" y="45" width="60" height="60" fill="none" stroke="red"/>
</svg>
Mask boundary uses SVG coordinates, but circle inside uses bounding box coordinates.
Summary
- Orange background — Shows that masked areas become truly transparent (not white). You can see through the masked-out parts to whatever is behind.
- Red shapes (not masked) — Proves that the mask only affects the specific element it's applied to (
mask="url(#maskId)"), not the entire bounding box area or other elements in the SVG. - Masking is per-element — The bounding box concept is only used for calculating coordinates (when using
objectBoundingBoxunits), not for determining what gets masked.