I want to put a horizontal line with a subtle moving-fog opacity effect beneath the headings on a webpage.
<feTurbulence type="fractalNoise"> filter) to add a metallic look.<feTurbulence type="fractalNoise"> filter that is animated so that parts of the line slowly fade slightly-out and back in at random.I've figured out a way to get all but the last of my desired points using a mix of CSS and SVG masks and filters:
html {
background: black;
font-size: 16px;
margin: 0;
}
.first-heading {
margin: 0.9375rem 3.5rem;
font-family: serif;
font-weight: 400;
font-style: normal;
color: #bdbdbd;
}
.page-heading {
width: 100%;
display: inline-flex;
position: relative;
}
.page-heading::after {
content: "";
position: absolute;
left: 0;
width: 100%;
height: 100%;
border-bottom: 0.125rem solid gold;
filter: drop-shadow(0 0 0.5rem gold) drop-shadow(0 0 0.5rem gold) url(#filter_gold);
mask: linear-gradient(0.25turn, black 0%, white 6.25%, white 93.75%, black 100%) luminance no-clip, url(#mask_fog) luminance no-clip repeat;
}
<svg id="svg-defs" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" height="0" style="display: block">
<defs>
<filter id="filter_gold" color-interpolation-filters="sRGB" x="0" y="0" width="512px" height="512px">
<feTurbulence
type="fractalNoise"
stitchTiles="stitch"
baseFrequency="0.0078125"
numOctaves="1"
result="turbulence"
/>
<feColorMatrix
in="turbulence"
type="matrix"
values="0.2126 0.7152 0.0722 0 0
0.2126 0.7152 0.0722 0 0
0.2126 0.7152 0.0722 0 0
0.0000 0.0000 0.0000 0 1"
result="grayscale"
/>
<feComponentTransfer in="grayscale" result="rescaled">
<feFuncR type="linear" slope="3" intercept="-1"/>
<feFuncG type="linear" slope="3" intercept="-1"/>
<feFuncB type="linear" slope="3" intercept="-1"/>
</feComponentTransfer>
<feComponentTransfer color-interpolation-filters="sRGB" in="rescaled" result="duotone">
<feFuncR type="table" tableValues="0.75 0.96875"/>
<feFuncG type="table" tableValues="0.611328125 0.84375"/>
<feFuncB type="table" tableValues="0.22265625 0.486328125"/>
<feFuncA type="table" tableValues="0 1"/>
</feComponentTransfer>
<feComposite
in="duotone"
in2="SourceGraphic"
operator="in"
result="duotone-clipped"
/>
</filter>
<filter id="filter_fog" color-interpolation-filters="sRGB" x="0" y="0" width="512px" height="512px">
<feTurbulence
type="fractalNoise"
stitchTiles="stitch"
baseFrequency="0.00390625"
numOctaves="1"
result="turbulence"
/>
<feColorMatrix
in="turbulence"
type="matrix"
values="0.2126 0.7152 0.0722 0 0
0.2126 0.7152 0.0722 0 0
0.2126 0.7152 0.0722 0 0
0.0000 0.0000 0.0000 0 1"
result="grayscale"
/>
<feComponentTransfer in="grayscale" result="rescaled">
<feFuncR type="linear" slope="2.25" intercept="-0.5"/>
<feFuncG type="linear" slope="2.25" intercept="-0.5"/>
<feFuncB type="linear" slope="2.25" intercept="-0.5"/>
</feComponentTransfer>
</filter>
</defs>
<rect id="mask_fog" x="0" y="0" width="512" height="512" filter="url(#filter_fog)"/>
</svg>
<div class="page-heading">
<h1 class="first-heading">Heading</h1>
</div>
However, I can't figure out how to make the mask_fog work to modify the line's transparency: if I make it the second mask it appears to have no effect, and if I make it the first mask the line completely disappears. I also tried using just the filter_fog <filter> as the mask, with the same results.
Below is a mock-up of what I want to achieve, made with After Effects (using screenshots of the SVG filter effects from the above code):

you can’t directly “animate” an SVG <filter> used as a CSS mask browsers don’t re-render filters unless something inside changes. So your <feTurbulence> is static by default.
The simple fix is to animate the baseFrequency in SVG, not in CSS. Like this:
<feTurbulence id="fogNoise" type="fractalNoise" baseFrequency="0.004" numOctaves="1" stitchTiles="stitch">
<animate attributeName="baseFrequency" dur="8s" values="0.004;0.008;0.004" repeatCount="indefinite"/>
</feTurbulence>
Then reference that filter as your mask:
.page-heading::after {
mask: url(#fogNoise);
-webkit-mask: url(#fogNoise);
}
That’s it — the <animate> tag makes the noise “breathe” over time, giving you that subtle fog shimmer. No JS needed. Have a good day!