Color Shifting in CSS The article explains how to create color-shifting effects in CSS for particle animations, starting with random RGB colors before switching to HSL for better control over hue, saturation, and lightness. The author discovered that animating between two HSL colors using keyframes can produce washed-out, greyish intermediate colors, and the tutorial explores workarounds for this CSS limitation. Let’s suppose we’re building this particle effect: This effect is something we build in my brand-new animations course opens in new tab , and there are lots of little details, but in this blog post, I want to zoom in on one surprisingly-interesting facet: the color shifting. Each particle starts on a random color and changes hue as it fades out. This seems pretty straightforward, but appearances can be deceiving. In fact, I discovered a new CSS limitation I wasn’t aware of, and came up with a couple different workarounds. In this tutorial, I’ll share what I’ve learned, and you’ll see how to build some super-cool color-shifting effects in CSS Link to this headingHello color So, in my particle effect, I want every particle to have its own dynamic colors. Let’s start with the simplest possible option, generating a random color using RGB values: // Do this for every particle: const red = Math.round Math.random 255 ; const green = Math.round Math.random 255 ; const blue = Math.round Math.random 255 ; particle.style.backgroundColor = rgb ${red} ${green} ${blue} ; The RGB color format supports 16.7 million possible colors 2563 , and this code will select one of them, totally at random. Here’s what that looks like: Sample Colors ClickTap the heart to generate some particles. The first few colors will be shown here. This looks alright, but we probably don’t want to use completely random colors. It would feel a lot more cohesive if all of our colors had the same general feel eg. all pastel colors, or all neon colors . It’s not at all obvious how we would do this with rgb , but if we switch to hsl , things get a lot easier. We could do something like this: const randomHue = Math.round Math.random 359 ; particle.style.backgroundColor = hsl ${randomHue}deg 100% 80% ; In this updated version, we’re picking a random hue, but we’re keeping the saturation locked to 100% and the lightness locked to 80%. This produces pastel-like tones: Sample Colors ClickTap the heart to generate some particles. The first few colors will be shown here. This feels a lot better to me. The only issue is that randomness can be clumpy; there’s no guarantee that the selected hues will be distributed across the color wheel. But this won’t really be an issue when we start shifting between colors, so let’s not worry about it. Link to this headingShifting between colors Alright, so I’m happy with our single-color solution, but really our goal is for each particle to shift between two different colors. As I mentioned in the intro, this was surprisingly tricky. Here‘s the first thing I tried: const fromHue = Math.round Math.random 359 ; const toHue = fromHue + 180; particle.style.setProperty '--from-color', hsl ${fromHue}deg 100% 80% ; particle.style.setProperty '--to-color', hsl ${toHue}deg 100% 80% ; / And then, in the CSS: / @keyframes colorShift { from { background: var --from-color ; } } .particle { background-color: var --to-color ; animation: colorShift 1500ms linear; } In this updated version, I’m generating two HSL colors that are on opposite sides of the color wheel, and fading between them using a keyframe animation. I’m using a couple of keyframe tricks that I wrote about a few weeks back. I chose a linear timing function since it feels more natural in this particular case; each intermediate color is shown for the same amount of time. Here‘s what this looks like: Sample Colors ClickTap the heart to generate some particles. The first few colors will be shown here. What do you think about this? Take a moment to really examine it. Do the colors look good to you? Personally, I found that the colors seemed a bit washed out. The particles become sorta grey-ish in between the source and destination. 🤔 This is clearer if we zoom in and slow down: If you pause this video while the particles are halfway through their shift, they’re all greyscale Admittedly, it’s hard to tell with everything going on. Here’s a basic button that performs the same color transition on hover/focus: Code Playground