From rive and lottie to AI videos: picking the best web animation stack without CPU spikes
Back to blog

From rive and lottie to AI videos: picking the best web animation stack without CPU spikes

9/13/2026 · 4 min · Development

Anyone in frontend development knows that adding fluid character animations to a web interface is a double-edged sword. Recently, while browsing Duolingo, I was impressed by the buttery smoothness of their mascot animations running effortlessly across devices. That sparked a deeper investigation into modern web animation stacks, comparing what I was using, and establishing a lightweight, cost-effective standard.

In this guide, I share the complete breakdown: examining Duolingo's Rive architecture, resolving Lottie CPU spikes, evaluating alternative open-source tools (Glaxnimate, Friction, Spline, SVG/GSAP), avoiding the trap of converting video into JSON frames, and deploying hardware-accelerated transparent WebM assets generated by AI.


1. The starting point: duolingo's stack and lottie's drawbacks#

Investigating Duolingo's web characters revealed they migrated to Rive (formerly Flare), running on a React and TypeScript architecture.

Rive's architectural advantages are significant:

The traditional lottie bottleneck#

Previously, I relied on Lottie for web animations. However, Lottie's runtime execution model presents fundamental performance drawbacks:

  1. It parses large JSON files containing every vector coordinate and Bézier curve point.
  2. Rendering is computed primarily by the CPU. When animations contain complex paths, expressions, and track mattes, client browsers struggle to recalculate geometries on every frame.
  3. This frequently triggers frame drops, Main Thread jank, and high battery consumption on mobile devices.

2. Evaluating alternatives: rive, svgs, and open-source tooling#

While Rive is technically impressive, two practical hurdles emerged:

I tested several other industry alternatives:


3. Mitigating dotlottie performance and the JSON video trap#

If your project requires dotLottie, three configurations help ease CPU load:

import { DotLottie } from '@lottiefiles/dotlottie-web';

const dotLottie = new DotLottie({
  canvas: document.getElementById('my-canvas'),
  src: 'mascot.lottie',
  autoplay: true,
  loop: true,
  renderer: 'webgl',
});

The trap of converting video into lottie JSON#

Without dedicated 2D vector rigging skills, I initially generated animated video clips and converted those .webm files into .json Lottie files through online converters.

The result was disastrous: converters cannot reconstruct mathematical vectors from video frames. Instead, they slice footage into hundreds of raw PNG images and store them as a massive spritesheet inside the JSON. A 150 kB video ballooned into a 5 MB JSON file that bloated browser RAM.


4. The optimal workflow: generative AI + transparent WebM (VP9)#

The breakthrough came from recognizing that for continuous looped animations that do not require runtime skeletal tracking, the native video container was already optimal.

I generated the mascot animations using Google Flow. The AI delivered fluid motion with accurate lighting, exported as transparent video with an alpha channel (VP9 codec with Alpha).

The final WebM file weighed between 100 kB and 200 kB.

File size and decoding comparison:#

FormatTypical SizeDecoding Method
WebM (VP9 + Alpha)100 kB to 200 kBHardware / GPU native
High-res static PNG image300 kB to 500 kBSoftware
Equivalent animated GIF6 MB to 10 MBSoftware (uncompressed frames)
Frame-converted Lottie JSON3 MB to 5 MBJavaScript parser / CPU

Because modern browsers support hardware decoding for VP9 and AV1, the HTML5 <video> element offloads playback directly to the GPU with minimal CPU overhead.


5. Practical HTML5 markup and core web vitals optimization#

To deploy mascot animations without layout shifts or autoplay blocks:

1. Optimization with ffmpeg:#

ffmpeg -i mascot.webm -c:v libvpx-vp9 -b:v 0 -crf 38 -pix_fmt yuva420p mascot-optimized.webm

2. HTML5 markup with CLS prevention:#

<video 
  autoplay 
  loop 
  muted 
  playsinline 
  preload="auto"
  width="300" 
  height="300" 
  aria-label="Interactive animated mascot"
>
  <!-- Fallback for Safari (iOS / macOS) using transparent HEVC -->
  <source src="/assets/img/mascot.mp4" type="video/mp4; codecs=hvc1">
  
  <!-- Ultra-lightweight format for Chrome, Firefox, Edge, and Android -->
  <source src="/assets/img/mascot.webm" type="video/webm">
</video>

Essential attributes:#

3. Server delivery rules (Nginx / Apache / CDN):#

  1. Disable dynamic compression (Gzip/Brotli) for .webm: Video streams are pre-compressed by codecs; re-compressing them wastes server CPU cycles.
  2. Aggressive HTTP caching headers:
Cache-Control: public, max-age=31536000, immutable

Conclusion: quick decision matrix#

Was this article helpful?

Leave a quick reaction to help prioritize future technical guides:

CC BY-NC

This post is licensed under CC BY-NC.

Comments

Join the discussion below.

0 comments