⭐ If you would like to buy me a coffee, well thank you very much that is mega kind! : https://www.buymeacoffee.com/honeyvig Hire a web Developer and Designer to upgrade and boost your online presence with cutting edge Technologies
Showing posts with label Animation. Show all posts
Showing posts with label Animation. Show all posts

Tuesday, December 9, 2025

Ambient Animations In Web Design: Practical Applications (Part 2)

 

Motion can be tricky: too much distracts, too little feels flat. Ambient animations sit in the middle. They’re subtle, slow-moving details that add atmosphere without stealing the show. In part two of his series, web design pioneer Andy Clarke shows how ambient animations can add personality to any website design.

First, a recap:

Ambient animations are the kind of passive movements you might not notice at first. However, they bring a design to life in subtle ways. Elements might subtly transition between colours, move slowly, or gradually shift position. Elements can appear and disappear, change size, or they could rotate slowly, adding depth to a brand’s personality.

In Part 1, I illustrated the concept of ambient animations by recreating the cover of a Quick Draw McGraw comic book as a CSS/SVG animation. But I know not everyone needs to animate cartoon characters, so in Part 2, I’ll share how ambient animation works in three very different projects: Reuven Herman, Mike Worth, and EPD. Each demonstrates how motion can enhance brand identity, personality, and storytelling without dominating a page.

Reuven Herman

Los Angeles-based composer Reuven Herman didn’t just want a website to showcase his work. He wanted it to convey his personality and the experience clients have when working with him. Working with musicians is always creatively stimulating: they’re critical, engaged, and full of ideas.

Design for LA-based composer Reuven Herman
My design for LA-based composer Reuven Herman. (Large preview)

Reuven’s classical and jazz background reminded me of the work of album cover designer Alex Steinweiss.

Album cover designs by Alex Steinweiss
Album cover designs by Alex Steinweiss. (Large preview)

I was inspired by the depth and texture that Alex brought to his designs for over 2,500 unique covers, and I wanted to incorporate his techniques into my illustrations for Reuven.

Illustrations for Reuven Herman
Two of my illustrations for Reuven Herman. (Large preview)

To bring Reuven’s illustrations to life, I followed a few core ambient animation principles:

  • Keep animations slow and smooth.
  • Loop seamlessly and avoid abrupt changes.
  • Use layering to build complexity.
  • Avoid distractions.
  • Consider accessibility and performance.

You can view this ambient animation in my lab. For Reuven’s site:

  • Sheet music stave lines morph between wavy and straight states.
  • Notes drift at different speeds to create parallax-like depth.
  • Piano keys appear to float.

My first step is always to optimise my SVGs for animation by exporting and optimising one set of elements at a time — always in the order they’ll appear in the final file and building the master SVG gradually. Working forwards from the background, I exported the sheet music stave lines, first in their wavy state.

Sheet music stave lines (wavy)
Sheet music stave lines (wavy). (Large preview)

…followed by their straight state:

Sheet music stave lines (straight)
Sheet music stave lines (straight). (Large preview)

The first step in my animation is to morph the stave lines between states. They’re made up of six paths with multi-coloured strokes. I started with the wavy lines:

<!-- Wavy state -->
<g fill="none" stroke-width="2" stroke-linecap="round">
<path id="p1" stroke="#D2AB99" d="[…]"/>
<path id="p2" stroke="#BDBEA9" d="[…]"/>
<path id="p3" stroke="#E0C852" d="[…]"/>
<path id="p4" stroke="#8DB38B" d="[…]"/>
<path id="p5" stroke="#43616F" d="[…]"/>
<path id="p6" stroke="#A13D63" d="[…]"/>
</g>

Although CSS now enables animation between path points, the number of points in each state needs to match. GSAP doesn’t have that limitation and can animate between states that have different numbers of points, making it ideal for this type of animation. I defined the new set of straight paths:

<!-- Straight state -->
const Waves = {
  p1: "[…]",
  p2: "[…]",
  p3: "[…]",
  p4: "[…]",
  p5: "[…]",
  p6: "[…]"
};

Then, I created a GSAP timeline that repeats backwards and forwards over six seconds:

const waveTimeline = gsap.timeline({
  repeat: -1,
  yoyo: true,
  defaults: { duration: 6, ease: "sine.inOut" }
});

Object.entries(Waves).forEach(([id, d]) => {
  waveTimeline.to(`#${id}`, { morphSVG: d }, 0);
});

Another ambient animation principle is to use layering to build complexity. Think of it like building a sound mix. You want variation in rhythm, tone, and timing. In my animation, three rows of musical notes move at different speeds:

<path id="notes-row-1"/>
<path id="notes-row-2"/>
<path id="notes-row-3"/>
Three rows of musical notes
Three rows of musical notes. (Large preview)

The duration of each row’s animation is also defined using GSAP, from 100 to 400 seconds to give the overall animation a parallax-style effect:

const noteRows = [
  { id: "#notes-row-1", duration: 300, y: 100 }, // slowest
  { id: "#notes-row-2", duration: 200, y: 250 }, // medium
  { id: "#notes-row-3", duration: 100, y: 400 }  // fastest
];

[]
Animated shadow
Animated shadow. (Large preview)

The next layer contains a shadow cast by the piano keys, which slowly rotates around its centre:

gsap.to("shadow", {
  y: -10,
  rotation: -2,
  transformOrigin: "50% 50%",
  duration: 3,
  ease: "sine.inOut",
  yoyo: true,
  repeat: -1
});
Animated piano keys
Animated piano keys. (Large preview)

And finally, the piano keys themselves, which rotate at the same time but in the opposite direction to the shadow:

gsap.to("#g3-keys", {
  y: 10,
  rotation: 2,
  transformOrigin: "50% 50%",
  duration: 3,
  ease: "sine.inOut",
  yoyo: true,
  repeat: -1
});

The complete animation can be viewed in my lab. By layering motion thoughtfully, the site feels alive without ever dominating the content, which is a perfect match for Reuven’s energy.

Mike Worth

As I mentioned earlier, not everyone needs to animate cartoon characters, but I do occasionally. Mike Worth is an Emmy award-winning film, video game, and TV composer who asked me to design his website. For the project, I created and illustrated the character of orangutan adventurer Orango Jones.

Design for Mike Worth
My design for Emmy award-winning composer Mike Worth. (Large preview)

Orango proved to be the perfect subject for ambient animations and features on every page of Mike’s website. He takes the reader on an adventure, and along the way, they get to experience Mike’s music.

Illustration for Mike Worth
Another of my illustrations for Mike Worth. (Large preview)

For Mike’s “About” page, I wanted to combine ambient animations with interactions. Orango is in a cave where he has found a stone tablet with faint markings that serve as a navigation aid to elsewhere on Mike’s website. The illustration contains a hidden feature, an easter egg, as when someone presses Orango’s magnifying glass, moving shafts of light stream into the cave and onto the tablet.

My SVG is deliberately structured, and from back to front, it includes the cave, light shaft, Orango, and navigation:

<svg data-lights="lights-off">
  <g id="cave">[…]</g>
  <path id="light-shaft" d="[…]"></path>
  <g id="orango">[…]</g>
  <g id="nav">[…]</g>
</svg>
The cave background
The cave background. (Large preview)

I also added an anchor around a hidden circle, which I positioned over Orango’s magnifying glass, as a large tap target to toggle the light shafts on and off by changing the data-lights value on the SVG:

<a href="javascript:void(0);" id="light-switch" title="Lights on/off">
  <circle cx="700" cy="1000" r="100" opacity="0" />
</a>
Orango isolated
Orango isolated. (Large preview)

Then, I added two descendant selectors to my CSS, which adjust the opacity of the light shafts depending on the data-lights value:

[data-lights="lights-off"] .light-shaft {
  opacity: .05;
  transition: opacity .25s linear;
}

[data-lights="lights-on"] .light-shaft {
  opacity: .25;
  transition: opacity .25s linear;
}

A slow and subtle rotation adds natural movement to the light shafts:

@keyframes shaft-rotate {
  0% { rotate: 2deg; }
  50% { rotate: -2deg; }
  100% { rotate: 2deg; }
}

Which is only visible when the light toggle is active:

[data-lights="lights-on"] .light-shaft {
  animation: shaft-rotate 20s infinite;
  transform-origin: 100% 0;
}
Light shafts isolated
Light shafts isolated.

When developing any ambient animation, considering performance is crucial, as even though CSS animations are lightweight, features like blur filters and drop shadows can still strain lower-powered devices. It’s also critical to consider accessibility, so respect someone’s prefers-reduced-motion preferences:

@media screen and (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
}

When an animation feature is purely decorative, consider adding aria-hidden="true" to keep it from cluttering up the accessibility tree:

<a href="javascript:void(0);" id="light-switch" aria-hidden="true">
  […]
</a>

With Mike’s Orango Jones, ambient animation shifts from subtle atmosphere to playful storytelling. Light shafts and soft interactions weave narrative into the design without stealing focus, proving that animation can support both brand identity and user experience. See this animation in my lab.

EPD #

Moving away from composers, EPD is a property investment company. They commissioned me to design creative concepts for a new website. A quick search for property investment companies will usually leave you feeling underwhelmed by their interchangeable website designs. They include full-width banners with faded stock photos of generic city skylines or ethnically diverse people shaking hands.

For EPD, I wanted to develop a distinctive visual style that the company could own, so I proposed graphic, stylised skylines that reflect both EPD’s brand and its global portfolio. I made them using various-sized circles that recall the company’s logo mark.

Design for the property investment company
My design for the property investment company EPD. (Large preview)

The point of an ambient animation is that it doesn’t dominate. It’s a background element and not a call to action. If someone’s eyes are drawn to it, it’s probably too much, so I dial back the animation until it feels like something you’d only catch if you’re really looking. I created three skyline designs, including Dubai, London, and Manchester.

Illustrations showing the skylines of Manchester and London
Manchester and London. Two of my illustrations for EPD. (Large preview)

In each of these ambient animations, the wheels rotate and the large circles change colour at random intervals.

To begin optimising this illustration for animation, I exported the base paths containing every element except the wheel:

<g id="banner-base>
  <path d="[…]"/>
  <path d="[…]"/>
  <path d="[…]"/>
  […]
</g>
Manchester illustration base layer
My Manchester illustration base layer. (Large preview)

Next, I exported a layer containing the circle elements I want to change colour.

<g id="banner-dots">
  <circle class="data-theme-fill" […]/>
  <circle class="data-theme-fill" […]/>
  <circle class="data-theme-fill" […]/>
  […]
</g>
Random-looking circles in Manchester illustration
Random-looking circles in my Manchester illustration. (Large preview)

Once again, I used GSAP to select groups of circles that flicker like lights across the skyline:

function animateRandomDots() {
  const circles = gsap.utils.toArray("#banner-dots circle")
  const numberToAnimate = gsap.utils.random(3, 6, 1)
  const selected = gsap.utils.shuffle(circles).slice(0, numberToAnimate)
}

Then, at two-second intervals, the fill colour of those circles changes from the teal accent to the same off-white colour as the rest of my illustration:

gsap.to(selected, {
  fill: "color(display-p3 .439 .761 .733)",
  duration: 0.3,
  stagger: 0.05,
  onComplete: () => {
    gsap.to(selected, {
      fill: "color(display-p3 .949 .949 .949)",
      duration: 0.5,
      delay: 2
    })
  }
})

gsap.delayedCall(gsap.utils.random(1, 3), animateRandomDots) }
animateRandomDots()

The result is a skyline that gently flickers, as if the city itself is alive. Finally, I rotated the wheel. Here, there was no need to use GSAP as this is possible using CSS rotate alone:

<g id="banner-wheel">
  <path stroke="#F2F2F2" stroke-linecap="round" stroke-width="4" d="[…]"/>
  <path fill="#D8F76E" d="[…]"/>
</g>
Rotating wheel in Manchester illustration
Rotating wheel in my Manchester illustration.

#banner-wheel {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: rotateWheel 30s linear infinite;
}

@keyframes rotateWheel {
  to { transform: rotate(360deg); }
}

CSS animations are lightweight and ideal for simple, repetitive effects, like fades and rotations. They’re easy to implement and don’t require libraries. GSAP, on the other hand, offers far more control as it can handle path morphing and sequence timelines. The choice of which to use depends on whether I need the precision of GSAP or the simplicity of CSS.

By keeping the wheel turning and the circles glowing, the skyline animations stay in the background yet give the design a distinctive feel. They avoid stock photo clichés while reinforcing EPD’s brand identity and are proof that, even in a conservative sector like property investment, ambient animation can add atmosphere without detracting from the message.

Wrapping up

From Reuven’s musical textures to Mike’s narrative-driven Orango Jones and EPD’s glowing skylines, these projects show how ambient animation adapts to context. Sometimes it’s purely atmospheric, like drifting notes or rotating wheels; other times, it blends seamlessly with interaction, rewarding curiosity without getting in the way.

Whether it echoes a composer’s improvisation, serves as a playful narrative device, or adds subtle distinction to a conservative industry, the same principles hold true:

Keep motion slow, seamless, and purposeful so that it enhances, rather than distracts from, the design.

Smashing Editorial

Monday, October 13, 2025

Creating Elastic And Bounce Effects With Expressive Animator

 Elastic and bounce effects have long been among the most desirable but time-consuming techniques in motion design. Expressive Animator streamlines the process, making it possible to produce lively animations in seconds, bypassing the tedious work of manual keyframe editing.

In the world of modern web design, SVG images are used everywhere, from illustrations to icons to background effects, and are universally prized for their crispness and lightweight size. While static SVG images play an important role in web design, most of the time their true potential is unlocked only when they are combined with motion.

Few things add more life and personality to a website than a well-executed SVG animation. But not all animations have the same impact in terms of digital experience. For example, elastic and bounce effects have a unique appeal in motion design because they bring a sense of realism into movement, making animations more engaging and memorable.

Grumpy Egg
(Large preview)

However, anyone who has dived into animating SVGs knows the technical hurdles involved. Creating a convincing elastic or bounce effect traditionally requires handling complex CSS keyframes or wrestling with JavaScript animation libraries. Even when using an SVG animation editor, it will most likely require you to manually add the keyframes and adjust the easing functions between them, which can become a time-consuming process of trial and error, no matter the level of experience you have.

This is where Expressive Animator shines. It allows creators to apply elastic and bounce effects in seconds, bypassing the tedious work of manual keyframe editing. And the result is always exceptional: animations that feel alive, produced with a fraction of the effort.

Using Expressive Animator To Create An Elastic Effect

Creating an elastic effect in Expressive Animator is remarkably simple, fast, and intuitive, since the effect is built right into the software as an easing function. This means you only need two keyframes (start and end) to make the effect, and the software will automatically handle the springy motion in between. Even better, the elastic easing can be applied to any animatable property (e.g., position, scale, rotation, opacity, morph, etc.), giving you a consistent way to add it to your animations.

Before we dive into the tutorial, take a look at the video below to see what you will learn to create and the entire process from start to finish.

First things first, let’s set the scene. For this, we’ll create a new project by pressing Ctrl/Cmd + P and configuring it in the “Create New Project” dialog that pops up. For frame size, we’ll choose 1080×1080, for a duration of 00:01:30, and we’ll let the frame rate remain unchanged at 60 frames per second (fps).

“Create New Project” dialog
(Large preview)

Once you hit the “Create project” button, you can use the Pen and Ellipse tools to create the artwork that will be animated, or you can simply copy and paste the artwork below.

See the Pen Effects With Expressive Animator - Artwork for Animation.

Now that everything has been set up, let’s create the animation. Make sure that snapping and auto-record are enabled, then move the playhead to 01:00f. By enabling snapping, you will be able to perfectly align nodes and graphic objects on the canvas. On the other hand, as the name suggests, auto-record tracks every change you make to the artwork and adds the appropriate keyframes on the timeline.

Screenshot with snapping and auto-record are enabled and the playhead moved to 01:00f
(Large preview)

Press the A key on your keyboard to switch to the Node tool, then select the String object and move its handle to the center-right point of the artboard. Don’t worry about precision, as the snapping will do all the heavy lifting for you. This will bend the shape and add keyframes for the Morph animator.

Screenshot with the String object and its handle moved to the center-right point of the artboard
(Large preview)

Next, press the V key on your keyboard to switch to the Selection tool. With this tool enabled, select the Ball, move it to the right, and place it in the middle of the string. Once again, snapping will do all the hard work, allowing you to position the ball exactly where you want to, while auto-recording automatically adds the appropriate keyframes.

Screenshot with the Ball selected and moved to the middle of the string
(Large preview)

You can now replay the animation and disable auto-recording by clicking on the Auto-Record button again.

As you can see when replaying, the direction in which the String and Ball objects are moving is wrong. Fortunately, we can fix this extremely easily just by reversing the keyframes. To do this, select the keyframes in the timeline and right-click to open the context menu and choose Reverse. This will reverse the keyframes, and if you replay the animation, you will see that the direction is now correct.

Screenshot with the context menu where you can choose Reverse
(Large preview)

With this out of the way, we can finally add the elastic effect. Select all the keyframes in the timeline and click on the Custom easing button to open a dialog with easing options. From the dialog, choose Elastic and set the oscillations to 4 and the stiffness to 2.5.

That’s it! Click anywhere outside the easing dialog to close it and replay the animation to see the result.

Selected custom easing button that opened a dialog with easing options
(Large preview)

The animation can be exported as well. Press Cmd/Ctrl + E on your keyboard to open the export dialog and choose from various export options, ranging from vectorized formats, such as SVG and Lottie, to rasterized formats, such as GIF and video.

For this specific animation, we’re going to choose the SVG export format. Expressive Animator allows you to choose between three different types of SVG, depending on the technology used for animation: SMIL, CSS, or JavaScript.

Export settings in the Expressive Animator
(Large preview)

Each of these technologies has different strengths and weaknesses, but for this tutorial, we are going to choose SMIL. This is because SMIL-based animations are widely supported, even on Safari browsers, and can be used as background images or embedded in HTML pages using the <img> tag. In fact, Andy Clarke recently wrote all about SMIL animations here at Smashing Magazine if you want a full explanation of how it works.

You can visualize the exported SVG in the following CodePen demo:

See the Pen Expressive Animator - Exported SVG.

Expressive Animator For Bounce And Other Effects

Adding a bounce effect to an animation is very similar to the process we just covered for creating an elastic effect, since both are built into Expressive Animator as easing functions. Just like elastic, bounce easing can be applied to any animatable property, giving you quick ways to create realistic motion.

Beyond these two effects, Expressive Animator also offers other easing options that can shape the personality of your animation, like Back, Steps, Sinc, just to name a few.

Easing functions in the Expressive Animator

Conclusion 

Elastic and bounce effects have long been among the most desirable but time-consuming techniques in motion design. By integrating them directly into its easing functions, Expressive Animator removes the complexity of manual keyframe manipulation and transforms what used to be a technical challenge into a creative opportunity.

The best part is that getting started with Expressive Animator comes with zero risk. The software offers a full 7–day free trial without requiring an account, so you can download it instantly and begin experimenting with your own designs right away. After the trial ends, you can buy Expressive Animator with a one-time payment, no subscription required. This will give you a perpetual license covering both Windows and macOS.

To help you get started even faster, I’ve prepared some extra resources for you. You’ll find the source files for the animations created in this tutorial, along with a curated list of useful links that will guide you further in exploring Expressive Animator and SVG animation. These materials are meant to give you a solid starting point so you can learn, experiment, and build on your own with confidence.

Thursday, August 21, 2025

Optimising SVGs

 What’s the best way to make your SVGs faster, simpler, and more manageable? In this article, pioneering author and web designer explains the process he relies on to prepare, optimise, and structure SVGs for animation and beyond.

SVG animations take me back to the Hanna-Barbera cartoons I watched as a kid. Shows like Wacky Races, The Perils of Penelope Pitstop, and, of course, Yogi Bear. They inspired me to lovingly recreate some classic Toon Titles using CSS, SVG, and SMIL animations.

But getting animations to load quickly and work smoothly needs more than nostalgia. It takes clean design, lean code, and a process that makes complex SVGs easier to animate. Here’s how I do it.

Whether for personal projects or commercial work, preparing SVGs well ensures they’re accessible. Optimising them ensures they load quickly, especially on mobile, and thinking carefully about how they’re structured makes maintaining them easier. I’ve developed a process that balances visuals with accessibility and performance and makes complex SVGs easier to work with.

So, to explain my process, I’ve chosen an episode of The Yogi Bear Show called “Bewitched Bear,” first broadcast in January 1960. In this story, Yogi steals a witch’s broom to help him grab “pic-a-nic” baskets.

“Hey, hey, hey!”

Start Clean And Design With Optimisation In Mind

Keeping things simple is key to making SVGs that are optimised and ready to animate. Tools like Adobe Illustrator convert bitmap images to vectors, but the output often contains too many extraneous groups, layers, and masks. Instead, I start cleaning in Sketch, work from a reference image, and use the Pen tool to create paths.

Tip: Affinity Designer (UK) and Sketch (Netherlands) are alternatives to Adobe Illustrator and Figma. Both are independent and based in Europe. Sketch has been my default design app since Adobe killed Fireworks.

Beginning With Outlines

For these Toon Titles illustrations, I first use the Pen tool to draw black outlines with as few anchor points as possible. The more points a shape has, the bigger a file becomes, so simplifying paths and reducing the number of points makes an SVG much smaller, often with no discernible visual difference.

Two outlines with different anchor points
Left: 160 anchor points. Right: 80 points. (Large preview)

Bearing in mind that parts of this Yogi illustration will ultimately be animated, I keep outlines for this Bewitched Bear’s body, head, collar, and tie separate so that I can move them independently. The head might nod, the tie could flap, and, like in those classic cartoons, Yogi’s collar will hide the joins between them.

Separate outlines for body, head, collar and tie, and broom.
Separate outlines for body, head, collar and tie, and broom. (Large preview)

Drawing Simple Background Shapes #

With the outlines in place, I use the Pen tool again to draw new shapes, which fill the areas with colour. These colours sit behind the outlines, so they don’t need to match them exactly. The fewer anchor points, the smaller the file size.

Original vector artwork and a simplified version with Adobe Illustrator
Left: Original vector artwork, 8 Kb. Right: Simplified using Adobe Illustrator, 2 Kb. (Large preview)

Sadly, neither Affinity Designer nor Sketch has tools that can simplify paths, but if you have it, using Adobe Illustrator can shave a few extra kilobytes off these background shapes.

An illustration how to simplify paths with Adobe Illustrator
Adobe Illustrator: Object → Path → Simplify. (Large preview)

Optimising The Code #

It’s not just metadata that makes SVG bulkier. The way you export from your design app also affects file size.

Vector artwork ready for optimisation.
Vector artwork ready for optimisation. (Large preview)

Exporting just those simple background shapes from Adobe Illustrator includes unnecessary groups, masks, and bloated path data by default. Sketch’s code is barely any better, and there’s plenty of room for improvement, even in its SVGO Compressor code. I rely on Jake Archibald’s SVGOMG, which uses SVGO v3 and consistently delivers the best optimised SVGs.

Jake Archibald’s SVGOMG online optimisation tool.
Jake Archibald’s SVGOMG online optimisation tool. (Large preview)

Layering SVG Elements #

My process for preparing SVGs for animation goes well beyond drawing vectors and optimising paths — it also includes how I structure the code itself. When every visual element is crammed into a single SVG file, even optimised code can be a nightmare to navigate. Locating a specific path or group often feels like searching for a needle in a haystack.

Toon Titles recreation of the Yogi Bear title card
Yogi Bear title card design by Lawrence Goble (1958). Toon Titles recreation. (Large preview)

That’s why I develop my SVGs in layers, exporting and optimising one set of elements at a time — always in the order they’ll appear in the final file. This lets me build the master SVG gradually by pasting it in each cleaned-up section. For example, I start with backgrounds like this gradient and title graphic.

Gradient background and title graphic.
Gradient background and title graphic. (Large preview)

Instead of facing a wall of SVG code, I can now easily identify the background gradient’s path and its associated linearGradient, and see the group containing the title graphic. I take this opportunity to add a comment to the code, which will make editing and adding animations to it easier in the future:

<svg ...>
  <defs>
    <!-- ... -->
  </defs>
  <path fill="url(#grad)" d=""/>
  <!-- TITLE GRAPHIC -->
  <g>
    <path  />
    <!-- ... --> 
  </g>
</svg>
Trail with Gaussian Blur.
Trail with Gaussian Blur. (Large preview)

Next, I add the blurred trail from Yogi’s airborne broom. This includes defining a Gaussian Blur filter and placing its path between the background and title layers:

<svg ...>
  <defs>
    <linearGradient id="grad" ></linearGradient>
    <filter id="trail" ></filter>
  </defs>
  <!-- GRADIENT -->
  <!-- TRAIL -->
  <path filter="url(#trail)" />
  <!-- TITLE GRAPHIC -->
</svg>
Yogi Bear’s magical stars.
Yogi Bear’s magical stars. (Large preview)

Then come the magical stars, added in the same sequential fashion:

<svg ...>
  <!-- GRADIENT -->
  <!-- TRAIL -->
  <!-- STARS -->
  <!-- TITLE GRAPHIC -->
</svg>

To keep everything organised and animation-ready, I create an empty group that will hold all the parts of Yogi:

<g id="yogi">...</g>
Added Yogi Bear’s component parts
Sequentially adding Yogi Bear’s component parts. (Large preview)

Then I build Yogi from the ground up — starting with background props, like his broom:

<g id="broom">...</g>

Followed by grouped elements for his body, head, collar, and tie:

<g id="yogi">
  <g id="broom"></g>
  <g id="body"></g>
  <g id="head"></g>
  <g id="collar"></g>
  <g id="tie"></g>
</g>
Toon Titles recreation of the Yogi Bear title card
Yogi Bear title card design by Lawrence Goble (1958). Toon Titles recreation. (Large preview)

Since I export each layer from the same-sized artboard, I don’t need to worry about alignment or positioning issues later on — they’ll all slot into place automatically. I keep my code clean, readable, and ordered logically by layering elements this way. It also makes animating smoother, as each component is easier to identify.

Reusing Elements With <use>

When duplicate shapes get reused repeatedly, SVG files can get bulky fast. My recreation of the “Bewitched Bear” title card contains 80 stars in three sizes. Combining all those shapes into one optimised path would bring the file size down to 3KB. But I want to animate individual stars, which would almost double that to 5KB:

<g id="stars">
 <path class="star-small" fill="#eae3da" d="..."/>
 <path class="star-medium" fill="#eae3da" d="..."/>
 <path class="star-large" fill="#eae3da" d="..."/>
 <!-- ... -->
</g>

Moving the stars’ fill attribute values to their parent group reduces the overall weight a little:

<g id="stars" fill="#eae3da">
 <path class="star-small" d=""/>
 <path class="star-medium" d=""/>
 <path class="star-large" d=""/>
 <!-- ... -->
</g>
Yogi Bear’s sparkling stars.
Yogi Bear’s sparkling stars. (Large preview)

But a more efficient and manageable option is to define each star size as a reusable template:

<defs>
  <path id="star-large" fill="#eae3da" fill-rule="evenodd" d=""/>
  <path id="star-medium" fill="#eae3da" fill-rule="evenodd" d=""/>
  <path id="star-small" fill="#eae3da" fill-rule="evenodd" d=""/>
</defs>

With this setup, changing a star’s design only means updating its template once, and every instance updates automatically. Then, I reference each one using <use> and position them with x and y attributes:

<g id="stars">
  <!-- Large stars -->
  <use href="#star-large" x="1575" y="495"/>
  <!-- ... -->
  <!-- Medium stars -->
  <use href="#star-medium" x="1453" y="696"/>
  <!-- ... -->
  <!-- Small stars -->
  <use href="#star-small" x="1287" y="741"/>
  <!-- ... -->
</g>

This approach makes the SVG easier to manage, lighter to load, and faster to iterate on, especially when working with dozens of repeating elements. Best of all, it keeps the markup clean without compromising on flexibility or performance.

Adding Animations

The stars trailing behind Yogi’s stolen broom bring so much personality to the animation. I wanted them to sparkle in a seemingly random pattern against the dark blue background, so I started by defining a keyframe animation that cycles through different opacity levels:

@keyframes sparkle {
  0%, 100% { opacity: .1; }
  50% { opacity: 1; }
}

Next, I applied this looping animation to every use element inside my stars group:

#stars use {
  animation: sparkle 10s ease-in-out infinite;
}

The secret to creating a convincing twinkle lies in variation. I staggered animation delays and durations across the stars using nth-child selectors, starting with the quickest and most frequent sparkle effects:

/* Fast, frequent */
#stars use:nth-child(n + 1):nth-child(-n + 10) {
  animation-delay: .1s;
  animation-duration: 2s;
}

From there, I layered in additional timings to mix things up. Some stars sparkle slowly and dramatically, others more randomly, with a variety of rhythms and pauses:

/* Medium */
#stars use:nth-child(n + 11):nth-child(-n + 20) { ... }

/* Slow, dramatic */
#stars use:nth-child(n + 21):nth-child(-n + 30) { ... }

/* Random */
#stars use:nth-child(3n + 2) { ... }

/* Alternating */
#stars use:nth-child(4n + 1) { ... }

/* Scattered */
#stars use:nth-child(n + 31) { ... }

By thoughtfully structuring the SVG and reusing elements, I can build complex-looking animations without bloated code, making even a simple effect like changing opacity sparkle.

Yogi Bear
Subtle movements bring Yogi Bear to life. (Large preview)

Then, for added realism, I make Yogi’s head wobble:

@keyframes headWobble {
  0% { transform: rotate(-0.8deg) translateY(-0.5px); }
  100% { transform: rotate(0.9deg) translateY(0.3px); }
}

#head {
  animation: headWobble 0.8s cubic-bezier(0.5, 0.15, 0.5, 0.85) infinite alternate;
}

His tie waves:

@keyframes tieWave {
  0%, 100% { transform: rotateZ(-4deg) rotateY(15deg) scaleX(0.96); }
  33% { transform: rotateZ(5deg) rotateY(-10deg) scaleX(1.05); }
  66% { transform: rotateZ(-2deg) rotateY(5deg) scaleX(0.98); }
}

#tie {
  transform-style: preserve-3d;
  animation: tieWave 10s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite;
}

His broom swings:

@keyframes broomSwing {
  0%, 20% { transform: rotate(-5deg); }
  30% { transform: rotate(-4deg); }
  50%, 70% { transform: rotate(5deg); }
  80% { transform: rotate(4deg); }
  100% { transform: rotate(-5deg); }
}

#broom {
  animation: broomSwing 4s cubic-bezier(0.5, 0.05, 0.5, 0.95) infinite;
}

And, finally, Yogi himself gently rotates as he flies on his magical broom:

@keyframes yogiWobble {
  0% { transform: rotate(-2.8deg) translateY(-0.8px) scale(0.998); }
  30% { transform: rotate(1.5deg) translateY(0.3px); }
  100% { transform: rotate(3.2deg) translateY(1.2px) scale(1.002); }
}

#yogi {
  animation: yogiWobble 3.5s cubic-bezier(.37, .14, .3, .86) infinite alternate;
}

All these subtle movements bring Yogi to life. By developing structured SVGs, I can create animations that feel full of character without writing a single line of JavaScript.

Try this yourself:

See the Pen Bewitched Bear CSS/SVG animation [forked] by Andy Clarke.

Conclusion

Whether you’re recreating a classic title card or animating icons for an interface, the principles are the same:

  1. Start clean,
  2. Optimise early, and
  3. Structure everything with animation in mind.

SVGs offer incredible creative freedom, but only if kept lean and manageable. When you plan your process like a production cell — layer by layer, element by element — you’ll spend less time untangling code and more time bringing your work to life.