Glowing embers climb from an unseen fire, flickering and zigzagging as they cool and fade halfway up a dark backdrop. The climb, the zigzag and the flicker are three separate animations.
Also known as: fire sparks background css, embers animation css, floating sparks css.
<div class="bgx-rising-embers" aria-hidden="true"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
.bgx-rising-embers {
--c: var(--bgx-color, #3b82f6);
font-size: var(--bgx-size, 16px);
position: absolute;
inset: 0;
overflow: hidden;
--ember: color-mix(in oklab, var(--c) 15%, #ff8a3d);
background: linear-gradient(to bottom, #070509, #150a08 65%, #2a1109);}
.bgx-rising-embers::after {
content: "";
position: absolute;
inset: auto -10% 0;
height: 55%;
background: radial-gradient(ellipse 60% 100% at 50% 100%, color-mix(in srgb, var(--ember) 50%, transparent), transparent 75%);
transform-origin: 50% 100%;
animation: bgx-rising-embers-fire 1.8s ease-in-out infinite alternate;
}
.bgx-rising-embers span {
position: absolute;
top: 0;
left: var(--x);
height: 100%;
animation: bgx-rising-embers-climb var(--dur) linear var(--delay) infinite;
}
.bgx-rising-embers span::before {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: var(--s);
height: var(--s);
border-radius: 50%;
background: color-mix(in srgb, var(--ember), #fff 45%);
box-shadow: 0 0 calc(var(--s) * 1.6) calc(var(--s) * 0.4) var(--ember);
opacity: 0;
animation:
bgx-rising-embers-zigzag var(--zig) ease-in-out var(--delay) infinite alternate,
bgx-rising-embers-cool var(--dur) linear var(--delay) infinite;
}
.bgx-rising-embers span:nth-child(1) { --x: 29.3%; --s: 0.17em; --dur: 5.74s; --delay: -1.72s; --zig: 1.37s; }
.bgx-rising-embers span:nth-child(2) { --x: 74.9%; --s: 0.27em; --dur: 3.81s; --delay: -5.65s; --zig: 1.36s; }
.bgx-rising-embers span:nth-child(3) { --x: 62.6%; --s: 0.33em; --dur: 4.15s; --delay: -6.16s; --zig: 1.46s; }
.bgx-rising-embers span:nth-child(4) { --x: 21.3%; --s: 0.16em; --dur: 4.97s; --delay: -3.37s; --zig: 1s; }
.bgx-rising-embers span:nth-child(5) { --x: 6.2%; --s: 0.29em; --dur: 5.29s; --delay: -2.93s; --zig: 1.74s; }
.bgx-rising-embers span:nth-child(6) { --x: 67.4%; --s: 0.24em; --dur: 4.85s; --delay: -6.2s; --zig: 1.07s; }
.bgx-rising-embers span:nth-child(7) { --x: 47.1%; --s: 0.24em; --dur: 5.19s; --delay: -1.36s; --zig: 1.67s; }
.bgx-rising-embers span:nth-child(8) { --x: 70.3%; --s: 0.27em; --dur: 4.19s; --delay: -0.38s; --zig: 1.74s; }
.bgx-rising-embers span:nth-child(9) { --x: 35.5%; --s: 0.35em; --dur: 5.83s; --delay: -0.25s; --zig: 1.69s; }
.bgx-rising-embers span:nth-child(10) { --x: 25.4%; --s: 0.35em; --dur: 4.86s; --delay: -1.72s; --zig: 1.11s; }
.bgx-rising-embers span:nth-child(11) { --x: 93.8%; --s: 0.26em; --dur: 4.91s; --delay: -2.51s; --zig: 1.18s; }
.bgx-rising-embers span:nth-child(12) { --x: 79%; --s: 0.3em; --dur: 5.4s; --delay: -4.27s; --zig: 1.63s; }
.bgx-rising-embers span:nth-child(13) { --x: 43.3%; --s: 0.28em; --dur: 3.86s; --delay: -5.69s; --zig: 1.28s; }
.bgx-rising-embers span:nth-child(14) { --x: 17%; --s: 0.19em; --dur: 4.65s; --delay: -3.51s; --zig: 1.17s; }
.bgx-rising-embers span:nth-child(15) { --x: 56.4%; --s: 0.23em; --dur: 5.62s; --delay: -4.38s; --zig: 0.93s; }
.bgx-rising-embers span:nth-child(16) { --x: 50.7%; --s: 0.19em; --dur: 5.81s; --delay: -5.44s; --zig: 1s; }
.bgx-rising-embers span:nth-child(17) { --x: 83.8%; --s: 0.31em; --dur: 5.21s; --delay: -2.53s; --zig: 0.95s; }
.bgx-rising-embers span:nth-child(18) { --x: 41.1%; --s: 0.23em; --dur: 6.5s; --delay: -6.36s; --zig: 0.89s; }
.bgx-rising-embers span:nth-child(19) { --x: 33.6%; --s: 0.28em; --dur: 5.66s; --delay: -4.35s; --zig: 1.67s; }
.bgx-rising-embers span:nth-child(20) { --x: 60.5%; --s: 0.34em; --dur: 5.56s; --delay: -5.68s; --zig: 0.95s; }
.bgx-rising-embers span:nth-child(21) { --x: 9.7%; --s: 0.32em; --dur: 6.32s; --delay: -2.76s; --zig: 1.52s; }
.bgx-rising-embers span:nth-child(22) { --x: 85.5%; --s: 0.22em; --dur: 4.78s; --delay: -0.18s; --zig: 1.15s; }
.bgx-rising-embers span:nth-child(23) { --x: 91.2%; --s: 0.25em; --dur: 5.86s; --delay: -3.03s; --zig: 1.44s; }
.bgx-rising-embers span:nth-child(24) { --x: 97.1%; --s: 0.32em; --dur: 4.57s; --delay: -6.02s; --zig: 0.98s; }
.bgx-rising-embers span:nth-child(25) { --x: 13.8%; --s: 0.26em; --dur: 4.59s; --delay: -4.79s; --zig: 1.67s; }
.bgx-rising-embers span:nth-child(26) { --x: 1.3%; --s: 0.18em; --dur: 6.08s; --delay: -4.84s; --zig: 0.91s; }
@keyframes bgx-rising-embers-climb {
from { transform: translateY(0.5em); }
to { transform: translateY(-100%); }
}
@keyframes bgx-rising-embers-zigzag {
from { translate: -0.9em 0; }
to { translate: 0.9em 0; }
}
@keyframes bgx-rising-embers-cool {
0% { opacity: 0; }
6% { opacity: 1; }
22% { opacity: 0.6; }
30% { opacity: 1; }
48% { opacity: 0.7; }
70% { opacity: 0; }
100% { opacity: 0; }
}
@keyframes bgx-rising-embers-fire {
from { opacity: 0.7; transform: scaleY(0.92); }
to { opacity: 1; transform: scaleY(1.06); }
}
@media (prefers-reduced-motion: reduce) {
.bgx-rising-embers, .bgx-rising-embers *, .bgx-rising-embers::before, .bgx-rising-embers::after, .bgx-rising-embers *::before, .bgx-rising-embers *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--bgx-color on the element or a parent.
Currently previewing #3b82f6.--bgx-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 16px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.