Making Graphics Move: Frontend Animation from CSS to WebGL

Static charts convey information. Animated charts convey process.

Many people think of “showing off” when they hear animations. But 95% of blog animation needs can be solved with CSS. CSS @keyframes + animation is like a Swiss Army knife — simple, reliable, no extra libraries, and respects user accessibility preferences through prefers-reduced-motion.

But there’s always that 5% where CSS falls short. Multi-element choreography, designer-created complex interactions, 3D data visualization, massive particle rendering. That’s when you need to find another path.

This post isn’t about “how to write animations” but “which path to choose.” CSS, GSAP, Lottie, Three.js, Canvas — how far can each road go, and where should you turn back.

SVG Animation: SMIL Isn’t Dead

SVG animation has three implementation paths: SMIL declarative animation, CSS-driven animation, and JavaScript-driven animation.

SMIL is SVG’s native animation mechanism with core elements:

  • <animate>: tween attributes
  • <animateTransform>: animate transform properties (translate, rotate, scale, skew)
  • <animateMotion>: move elements along paths
xml
1
2
3
4
5
<svg width="200" height="100">
  <circle cx="50" cy="50" r="20" fill="steelblue">
    <animate attributeName="cx" from="50" to="150" dur="2s" repeatCount="indefinite"/>
  </circle>
</svg>

In 2015, the Chrome team publicly proposed deprecating SMIL, citing high maintenance costs and functional overlap with the Web Animations API. But developers strongly opposed this — path morphing and SVG animations inside img tags have no CSS or Web Animations API equivalent. So Chrome paused the deprecation plan.

Need to clarify: SMIL hasn’t been “disabled” by modern browsers. All modern browsers (Chrome, Firefox, Safari, Edge) still support SMIL, only Internet Explorer never did. SVG 2 specification retains all animation elements.

The community generally recommends prioritizing CSS/JS animations for new projects because of better performance and integration with prefers-reduced-motion.

CSS-driven SVG animation typically outperforms SMIL and offers stronger control:

css
1
2
3
4
5
6
7
8
9
/* SVG path drawing animation */
.path {
  stroke-dasharray: 1000;
  stroke-dashoffset: 1000;
  animation: draw 2s ease forwards;
}
@keyframes draw {
  to { stroke-dashoffset: 0; }
}

SVG is DOM-based vector graphics. When elements get numerous, DOM operations become the bottleneck. Suitable for medium-scale graphics animation requiring accessibility and stylability — icon micro-interactions, loading animations, path drawing, data visualization transitions.

Author’s note: For Animated SVG in blogs, CSS animation is the best starting point. No extra libraries needed, better performance than SMIL, and controllable via prefers-reduced-motion. If complex choreography is needed, consider GSAP.

GSAP: Timeline is the Killer Abstraction

GSAP is developed by GreenSock and is one of the industry’s most mature JavaScript animation platforms.

v1.0 in 2010 standardized the API, introducing core concepts like timeline and easing functions. v3.0 in 2019 was a major update — file size halved from old TweenMax, simplified API, backward compatibility, timeline default value inheritance.

GSAP’s core is Tween and Timeline:

  • Tween: Basic animation unit, defining start/end states via gsap.to(), gsap.from(), gsap.fromTo(). Can operate any readable/writable property — not limited to CSS styles, but also direct SVG properties (viewBox, d path data, stroke-dasharray), Canvas context properties, etc.
  • Timeline: Chain/parallel orchestrate multiple tweens, precise control of timing, nesting, and overall playback control. This is GSAP’s killer abstraction.
javascript
1
2
3
4
// GSAP timeline orchestration example
const tl = gsap.timeline({ repeat: -1, yoyo: true });
tl.to(".bar", { scaleY: 1.5, duration: 0.5, stagger: 0.1, ease: "power2.inOut" })
  .to(".bar", { scaleY: 1, duration: 0.5, stagger: 0.1, ease: "power2.inOut" });

GSAP extends capabilities through plugins: MotionPathPlugin (path motion), ScrollTrigger (scroll-driven animation), MorphSVG (SVG shape morphing), DrawSVG (stroke drawing), Draggable, etc. Core library ~30KB, load on demand.

DimensionGSAPCSS Animation
StrengthsNot limited to CSS-animatable properties, timeline orchestration far exceeds CSS, cross-browser consistency, rich easing functionsZero dependency, declarative, browser native optimization
WeaknessesExtra library size, requires JS executionComplex choreography difficult, no fine-grained playback state control
Fit forComplex choreography, scroll-driven, SVG morphing, data visualization transition animationsSimple state switching, hover effects, basic transitions

Lottie: JavaScript Version of Designer Work

Lottie was open-sourced by Airbnb in 2017. It’s a cross-platform animation library (Android, iOS, Web, React Native, Windows) that parses JSON animations exported from Adobe After Effects via the Bodymovin plugin and renders natively on each platform.

Core workflow: Designer creates animation in After Effects → export to JSON via Bodymovin (or LottieFiles official plugin) → frontend uses lottie-web to parse and render.

Supports vector graphics, layers, masks, expressions, and other AE features. Provides Canvas / SVG / HTML three rendering modes.

Lottie is perfect for loading animations, empty state illustrations, and article decorative animations in blogs. You can find many free animation resources on the LottieFiles platform, import lottie-web, and play with a few lines of code.

DimensionDescription
Use casesUI animations, illustration animations, loading states, onboarding animations, empty state illustrations, micro-interactions
StrengthsDesign-development decoupling, cross-platform consistency, vector scalability, small files, programmatic control
WeaknessesLimited support for complex AE features, unsuitable for large-scale dynamic visualization, parsing overhead exists for complex JSON

Lottie is essentially “designer-driven” fixed animation, not good at data-driven dynamic binding. In data visualization, it’s mainly used for decorative/state animations outside charts (like loading, success prompts), not the geometric mapping of data itself.

Three.js: 3D Visualization and Massive Data

Three.js is led by developer Ricardo Cabello (@mrdoob), an open-source 3D JavaScript library aiming to simplify WebGL’s complex low-level APIs.

Core abstractions: Scene (scene graph), Camera (camera), Renderer (renderer), Mesh (mesh = Geometry + Material), Light (lighting).

3D data visualization scenarios include: 3D scatter plots, point cloud visualization, earth visualization, molecular structure visualization, industrial equipment simulation, particle systems.

Uber’s open-source deck.gl is a large-scale data visualization framework based on WebGL, specifically designed for geospatial big data, providing high-performance layered rendering (points, arcs, grids, heatmaps, etc.).

kepler.gl is a web-based large-scale geospatial visualization application built on deck.gl that can render millions of points representing thousands of trips and perform real-time spatial aggregation.

DimensionDescription
Use casesTrue 3D space, massive data points (million-level), geospatial big data, immersive visualization
StrengthsGPU acceleration, strong expressiveness, can handle big data
WeaknessesSteep learning curve, mobile/low-end device GPU compatibility needs attention, SEO and accessibility weaker than DOM/SVG

Canvas Particle Systems: When Scale Trumps Everything

requestAnimationFrame is the browser’s native animation API, syncing draw calls with display refresh (typically 60Hz or 120Hz) and automatically pausing when tab is invisible to save CPU and battery.

The core idea of particle systems: maintain a list of tiny objects, each with position, size, color, speed, lifetime properties; each frame generates new particles, updates surviving particles, recycles dead particles, pushes data to GPU/Canvas.

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Canvas particle animation core loop
function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  particles.forEach(p => {
    p.update();
    p.draw(ctx);
  });
  requestAnimationFrame(animate);
}
animate();

Performance optimization techniques include:

  • Object pooling: reuse particle objects, avoid frequent GC
  • Compute-draw separation: update computation first, then redraw uniformly
  • Local clear instead of full-screen wipe: sparse particles only clear their surrounding area
  • Dual-layer Canvas separation: one layer draws static background (once), one layer draws dynamic particles (clear and redraw each frame)
  • ImageData pixel-level rendering: when particles are single pixels, use ImageData to write pixels directly, reaching extremely large scale
  • OffscreenCanvas + Web Worker: move entire simulation+rendering off main thread, keeping main thread responsive

Particle animations are commonly used for: flow field/wind field visualization, network/relationship force-directed graphs, heat and density maps, background ambient effects, large-scale point clouds.

Canvas is suitable for scenarios with large numbers of elements where individual interaction with each element isn’t needed (complementary to SVG’s DOM bottleneck).

Houdini is an umbrella of APIs that let developers intervene in the browser rendering engine:

  • @property: next-generation CSS variables, now fully supported by all modern browsers, can declare types and initial values for custom properties, unlocking stronger animation and computation capabilities
  • Animation Worklet: Houdini’s most powerful API, allows running animation logic in compositor thread, not blocked by main thread; but lowest browser support, currently only Chrome fully supports

Web Animations API (WAAPI) unifies CSS and SVG animation capabilities, allowing hardware-accelerated DOM element animation via JavaScript (like Element.animate(keyframes, options)). It combines CSS animation’s declarative power with JS’s imperative control, the modernization direction for SMIL/CSS animation.

javascript
1
2
3
4
5
// WAAPI example
element.animate(
  [{ transform: 'scale(1)' }, { transform: 'scale(1.5)' }],
  { duration: 1000, iterations: Infinity, direction: 'alternate' }
);

Variable fonts compress multiple axes of font weight (weight, width, slant, etc.) into a single font file, and font-variation-settings is an animatable CSS property, opening typography effects impossible with static fonts. Browser support: Chrome 66+, Firefox 62+, Safari 11+, Edge 17+, global coverage over 97% in 2024.

Animation Design Principles

Animation isn’t the goal, it’s the means.

  • Perceived performance: use transitions to mask loading and computation delays, making users “feel faster”
  • prefers-reduced-motion: respect user accessibility preferences, degrade or disable animations
  • Meaningful motion: animation should convey state changes, data mapping, or causal relationships, not decoration
  • Performance priority: prioritize compositor-friendly properties (transform, opacity), avoid triggering layout/repaint; prioritize Canvas/WebGL for large data

Technology Selection Decision Tree

mermaid
flowchart TD
    A[Animation needs] --> B[CSS Animation<br/>95% blog needs]
    A --> C[Lottie<br/>Designer effects]
    A --> D[GSAP<br/>Multi-element timing]
    A --> E[D3.js<br/>Data-driven transitions]
    A --> F[Three.js<br/>3D massive data]

    style A fill:#FF9800,color:#fff
    style B fill:#4CAF50,color:#fff
    style C fill:#9C27B0,color:#fff
    style D fill:#2196F3,color:#fff
    style E fill:#FF9800,color:#fff
    style F fill:#f44336,color:#fff

Simple state toggle → CSS Animation. 95% of blog animation needs are here.

Designer-provided interactive animations → Lottie. Design-development decoupling, cross-platform consistency.

Multi-element choreography → GSAP. Timeline abstraction, scroll-driven, SVG morphing.

Data-driven visualization → D3.js + SVG/Canvas. D3 handles data mapping, SVG/Canvas handles rendering.

3D space or massive data → Three.js/deck.gl. GPU acceleration, million-level point clouds.

Particle effects → Canvas. Object pooling, dual-layer Canvas, OffscreenCanvas + Web Worker.

Conclusion

The animation technology stack looks complex, but the core is understanding the boundaries of each path. If CSS can solve it, never introduce GSAP. If designer provides it, use Lottie. If data-driven, use D3/GSAP/Canvas. If 3D, use Three.js.

In blog scenarios, the vast majority of animation needs stay at the CSS level. If unsure, start with CSS, then look up when it doesn’t work.

After all, the best technology decision is knowing when to stop.