<!-- part of article: https://utilitybend.com/blog/revisiting-svg-filters-my-forgotten-powerhouse-for-duotones-noise-and-other-effects -->
<svg width="0" height="0" viewBox="0 0 500 350" aria-hidden="true">
<filter id="blurYHard">
<feGaussianBlur stdDeviation="0 0"></feGaussianBlur>
</filter>
<filter id='noiseFilter'>
<feTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch' />
</filter>
</svg>
<div class="wrapper">
<section></section>
<div class="speed">
<label for="slider">Speed</label>
<input type="range" id="slider" min="0" max="10" step="0.1" value="5">
</div>
<div class="checkbox"><input type="checkbox" id="flip-it" /><label for="flip-it">Flip it</label></div>
<div class="hamster-and-parachute">
<svg class="parachute" xmlns="http://www.w3.org/2000/svg" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 402 512.29">
<path fill="seagreen" d="M133.59 360.6 3.33 208.96c-.76-.88-1.35-1.84-1.79-2.85-.43-.26-.8-.63-1.07-1.08a3.24 3.24 0 0 1-.47-1.68c-.02-98.1 51.53-160.36 117.71-187.54C143.55 5.2 171.62-.06 199.7 0c28.09.06 56.23 5.43 82.18 16.06 65.86 26.96 117.75 87.7 119.87 181.34.31 1.44.33 2.93.07 4.39l.01 1.57c0 1.32-.8 2.46-1.93 2.97a11 11 0 0 1-.59.76L269.04 360.81c6.89 1.88 13.07 5.56 17.98 10.47l1.37 1.53c6.53 7.22 10.52 16.74 10.52 27.07v71.91c0 11.05-4.57 21.16-11.9 28.52l-.12.12c-7.36 7.31-17.45 11.86-28.48 11.86H143.4c-11.05 0-21.16-4.56-28.53-11.89l-.12-.12c-7.3-7.35-11.85-17.44-11.85-28.49v-71.91c0-11.08 4.57-21.21 11.88-28.61 5.11-5.1 11.58-8.87 18.81-10.67zm106.84-1.21 134.94-159.22c-17.13-11.34-34.22-16.37-51.24-15.97-15.71.36-31.49 5.34-47.31 14.21l-48.14 160.98h11.75zm-35.51 0 47.58-159.1c-18.87-11.97-36.94-17.26-54.3-16.83-16.54.4-32.57 6-48.15 15.95l48.4 159.98h6.47zm-30.27 0-48.5-160.31c-18.62-10.86-37.1-15.43-55.46-14.88-16.03.48-32.05 4.86-48.04 12.36l139.88 162.83h12.12zm-31.25 19.54h115.01c11.53 0 20.96 9.47 20.96 20.95v71.91c0 11.48-9.48 20.96-20.96 20.96H143.4c-11.48 0-20.96-9.43-20.96-20.96v-71.91c0-11.52 9.43-20.95 20.96-20.95z" />
</svg>
<!-- hamster copy -->
<div class="hamster">
<div class="head">
<div class="ears">
</div>
<div class="eyes">
<div></div>
</div>
<div class="mouth">
<div></div>
<div></div>
</div>
</div>
<div class="body">
<div class="hands">
<div></div>
</div>
<div class="feet"></div>
</div>
</div>
</div>
</div>
:root {
--slider-value: 0;
--base-landscape-duration: 15s;
}
.checkbox {
display: flex;
align-items: center;
position: fixed;
top: 40px;
left: 50%;
z-index: 100;
}
section {
height: 100dvh;
background: skyblue;
background-image: url("https://assets.codepen.io/159218/clouds.svg");
background-size: auto 200vh;
animation: landscape
calc(var(--base-landscape-duration) / (var(--slider-value) * 0.5 + 1))
linear infinite;
filter: url(#blurYHard);
}
.wrapper {
height: 100dvh;
&::after {
position: absolute;
inset: 0;
z-index: 1;
border: 20px solid black;
content: "";
}
}
:root:has(input[type="checkbox"]:checked) section {
animation-name: landscape-reverse;
}
body {
margin: 0;
font-family: Arial, Helvetica, sans-serif;
font-weight: 700;
font-size: 1.5rem;
}
.speed {
position: fixed;
top: 50%;
right: 0;
z-index: 3;
}
#slider {
rotate: -90deg;
accent-color: rebeccapurple;
scale: 2;
}
svg[aria-hidden="true"] {
position: absolute;
height: 0;
width: 0;
visibility: hidden;
}
@keyframes landscape {
from {
background-position-y: 0%;
}
to {
background-position-y: 200%;
}
}
@keyframes landscape-reverse {
from {
background-position-y: 0%;
}
to {
background-position-y: -200%;
}
}
label {
padding-left: 30px;
cursor: pointer;
}
input[type="checkbox"] {
scale: 3;
accent-color: rebeccapurple;
}
/* hamster */
.head,
.eyes,
.feet,
.hands,
.mouth {
&::before,
&::after {
content: "";
}
}
.mouth {
position: absolute;
top: 30%;
inset-inline: 0;
display: flex;
justify-content: center;
align-items: center;
z-index: 2;
&::before {
position: absolute;
top: 50%;
border-radius: 50%;
aspect-ratio: 1;
background: black;
width: calc(var(--base-width) / 40);
}
}
.mouth div {
width: calc(var(--base-width) / 8);
aspect-ratio: 1;
border-radius: 50%;
border-bottom: 5px solid black;
}
.hamster-and-parachute {
position: fixed;
top: 20%;
left: 50%;
animation: updown calc(20s / (var(--slider-value) * 0.5 + 1)) linear infinite;
transform: translateX(-50%);
}
.parachute {
width: 40vmin;
display: block;
z-index: 1;
}
@keyframes updown {
0%,
100% {
translate: 0 0;
rotate: 0;
}
33% {
translate: -2% -2%;
rotate: -5deg;
}
66% {
translate: 2% 2%;
rotate: 5deg;
}
}
.hamster {
--base-width: 20vmin;
--body: calc(var(--base-width) * 1.3);
--color: BurlyWood;
--secondary-color: sienna;
position: absolute;
bottom: -10%;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-inline: auto;
z-index: 2;
}
.head {
position: relative;
width: var(--base-width);
aspect-ratio: 1;
background: var(--color);
border-radius: 48%;
background: radial-gradient(
farthest-corner circle at 100% 0%,
var(--secondary-color) 32% 0%,
31%,
var(--color) 100%
);
&::before,
&::after {
position: absolute;
width: calc(var(--base-width) / 5);
aspect-ratio: 5/ 4;
border-radius: 50%;
background: var(--color);
top: -5%;
left: calc(var(--base-width) / 7);
rotate: -30deg;
z-index: -1;
}
&::after {
inset-inline: auto calc(var(--base-width) / 7);
rotate: 30deg;
background: var(--secondary-color);
}
}
.eyes {
position: absolute;
top: 20%;
width: 60%;
left: 20%;
display: grid;
grid-template-columns: calc(var(--base-width) / 8) auto calc(
var(--base-width) / 8
);
z-index: 1;
&::before,
&::after {
width: calc(var(--base-width) / 10);
aspect-ratio: 1;
border-radius: 50%;
background: black;
}
}
.body {
position: relative;
width: var(--body);
aspect-ratio: 8/6.5;
background: var(--color);
border-radius: 48%;
margin-top: calc(var(--base-width) / -1.6);
}
.hands {
position: absolute;
top: 38%;
width: 60%;
left: 15%;
display: grid;
grid-template-columns: calc(var(--base-width) / 8) auto calc(
var(--base-width) / 8
);
z-index: 1;
&::before,
&::after {
border-right: calc(var(--base-width) / 30) solid black;
border-bottom: calc(var(--base-width) / 30) solid black;
width: calc(var(--body) / 6);
aspect-ratio: 1;
rotate: -45deg;
transform: skew(20deg, 20deg);
}
&::after {
rotate: 135deg;
}
}
.feet {
&::before,
&::after {
position: absolute;
width: calc(var(--base-width) / 7);
aspect-ratio: 3/4;
border-radius: 50%;
background: var(--color);
bottom: -4%;
left: calc(var(--base-width) / 3);
rotate: 20deg;
z-index: -1;
}
&::after {
inset-inline: auto calc(var(--base-width) / 3);
rotate: -20deg;
}
}
const slider = document.getElementById("slider");
const root = document.documentElement;
const blurYHardFilter = document.querySelector("#blurYHard feGaussianBlur");
function updateEffects(value) {
// Update CSS custom property
root.style.setProperty("--slider-value", value);
const blurYHardValue = (value / 10) * 15; // Max value is 10
blurYHardFilter.setAttribute("stdDeviation", `0 ${blurYHardValue}`);
}
// Set initial values
updateEffects(slider.value);
slider.addEventListener("input", function () {
const value = parseFloat(this.value);
updateEffects(value);
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.