Vanta.js
When to Use
- Adding an animated WebGL/Three.js background to a hero section or full page.
- Choosing from pre-built 3D effects (birds, fog, waves, net, globe, etc.).
- Needing a quick, visually impressive background without writing custom shaders.
- Integrating animated backgrounds into React, Vue, or other SPA frameworks.
Key Concepts
What Is Vanta.js
Vanta.js provides ready-made animated 3D backgrounds powered by Three.js (or p5.js for some effects). Each effect is self-contained: pick one, configure colors and parameters, and attach it to a DOM element.
Dependencies
Vanta.js requires Three.js as a peer dependency for most effects:
npm install three vanta
Or via CDN:
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.birds.min.js"></script>
Available Effects
| Effect | Description |
|---|---|
BIRDS |
Flocking birds simulation. |
FOG |
Volumetric fog with color gradients. |
WAVES |
Animated wave surface. |
NET |
Connected particles forming a net. |
CELLS |
Organic cell-like blobs. |
TRUNK |
Growing tree trunk / branches. |
TOPOLOGY |
Topographic contour lines. |
DOTS |
Floating connected dots. |
RINGS |
Concentric animated rings. |
HALO |
Glowing radial halo effect. |
GLOBE |
Dotted rotating globe. |
CLOUDS |
Volumetric clouds (uses p5.js instead of Three.js). |
Basic Setup
import * as THREE from "three";
import BIRDS from "vanta/dist/vanta.birds.min";
const effect = BIRDS({
el: "#hero-background",
THREE: THREE,
backgroundColor: 0x0a0a1a,
color1: 0x6366f1,
color2: 0x06b6d4,
birdSize: 1.2,
wingSpan: 20,
quantity: 4,
mouseControls: true,
touchControls: true,
});
Common Configuration Options
These options apply to most effects:
| Option | Type | Description |
|---|---|---|
el |
string / HTMLElement | CSS selector or DOM element to attach the background to. |
THREE |
object | Reference to the Three.js library. |
backgroundColor |
hex number | Background color (e.g., 0x0a0a1a). |
color / color1 / color2 |
hex number | Primary and secondary accent colors. |
mouseControls |
boolean | Enable mouse-reactive movement. |
touchControls |
boolean | Enable touch-reactive movement on mobile. |
gyroControls |
boolean | Enable device gyroscope reactivity. |
minHeight |
number | Minimum canvas height in pixels. |
minWidth |
number | Minimum canvas width in pixels. |
scale |
number | Geometry scale factor. |
speed |
number | Animation speed multiplier. |
Effect-Specific Options
BIRDS:
{ birdSize: 1, wingSpan: 20, speedLimit: 5, separation: 20, alignment: 20, cohesion: 20, quantity: 3 }
WAVES:
{ waveHeight: 15, waveSpeed: 0.5, shininess: 50, zoom: 1 }
NET:
{ points: 10, maxDistance: 20, spacing: 16, showDots: true }
FOG:
{ highlightColor: 0xffc300, midtoneColor: 0xff1f00, lowlightColor: 0x2d00ff, baseColor: 0x242424, blurFactor: 0.6, speed: 1, zoom: 1 }
GLOBE:
{ size: 1, backgroundColor: 0x0, color: 0xff6600, color2: 0x001166 }
Quick Recipes
Animated Background on a Div
<div id="hero" style="width: 100%; height: 100vh; position: relative;">
<h1 style="position: relative; z-index: 1; color: white; padding: 2rem;">
Welcome
</h1>
</div>
<script type="module">
import * as THREE from "three";
import NET from "vanta/dist/vanta.net.min";
const effect = NET({
el: "#hero",
THREE: THREE,
color: 0x6366f1,
backgroundColor: 0x0f172a,
points: 12,
maxDistance: 22,
spacing: 18,
mouseControls: true,
touchControls: true,
});
// Cleanup when no longer needed
// effect.destroy();
</script>
React Integration
import { useEffect, useRef, useState } from "react";
import * as THREE from "three";
import FOG from "vanta/dist/vanta.fog.min";
export default function HeroBackground({ children }) {
const containerRef = useRef(null);
const [effect, setEffect] = useState(null);
useEffect(() => {
if (!containerRef.current) return;
const vantaEffect = FOG({
el: containerRef.current,
THREE: THREE,
highlightColor: 0x6366f1,
midtoneColor: 0x3b82f6,
lowlightColor: 0x1e1b4b,
baseColor: 0x0f172a,
blurFactor: 0.5,
speed: 1.5,
zoom: 0.8,
mouseControls: true,
touchControls: true,
});
setEffect(vantaEffect);
return () => {
if (vantaEffect) vantaEffect.destroy();
};
}, []);
return (
<div
ref={containerRef}
style={{ width: "100%", height: "100vh", position: "relative" }}
>
<div style={{ position: "relative", zIndex: 1 }}>{children}</div>
</div>
);
}
Switching Effects Dynamically
let currentEffect = null;
function setBackground(effectModule, options) {
if (currentEffect) currentEffect.destroy();
currentEffect = effectModule({
el: "#hero",
THREE: THREE,
...options,
});
}
// Usage
import WAVES from "vanta/dist/vanta.waves.min";
import BIRDS from "vanta/dist/vanta.birds.min";
setBackground(WAVES, { color: 0x0077be, waveHeight: 20 });
// Later...
setBackground(BIRDS, { color1: 0xff6600, backgroundColor: 0x111111 });
Responsive Resize
Vanta.js handles window resizing automatically. If you dynamically change the container size (e.g., toggling a sidebar), call:
effect.resize();
Cleanup
Always destroy the effect when removing the element or navigating away:
effect.destroy();
This removes the injected <canvas>, stops the animation loop, and releases the WebGL context.
In React, call destroy() in the useEffect cleanup function. In Vue, call it in onBeforeUnmount.
Common Pitfalls
| Pitfall | Fix |
|---|---|
| Missing Three.js reference | Always pass THREE: THREE (or p5 for cloud effects). Without it, the effect fails silently. |
| Effect not visible | Ensure the target element has a defined height (e.g., height: 100vh). A zero-height container produces an invisible canvas. |
| Content hidden behind canvas | Set position: relative; z-index: 1 on content inside the container. The Vanta canvas is absolutely positioned inside the element. |
| No cleanup on route change | Call effect.destroy() when the component unmounts. Leaked effects keep animating and consume GPU resources in the background. |
| Multiple effects on one element | Destroy the existing effect before creating a new one. Layering effects doubles GPU load and causes visual glitches. |
| Poor mobile performance | Reduce quantity, points, or speed. Disable gyroControls if not needed. Some effects (HALO, GLOBE) are heavier than others. |
Best Practices
- Choose the right effect for the vibe.
FOGandWAVESare subtle and professional.BIRDSandNETare more dynamic.GLOBEworks well for international / tech products. - Keep colors consistent with your brand. Pass your design system's hex values into
backgroundColor,color1, andcolor2. - Lazy-load the effect. Import Vanta and Three.js dynamically so they don't block initial page load:
const [THREE, WAVES] = await Promise.all([ import("three"), import("vanta/dist/vanta.waves.min"), ]); - Reduce intensity on mobile. Use
matchMediato detect small screens and pass lower values forpoints,speed, andquantity. - Always destroy on unmount. One leaked Vanta effect means a dangling WebGL context and continuous GPU usage.
- Test with DevTools Performance tab. Animated backgrounds should stay well under 16 ms per frame. If they exceed this, simplify parameters.
- Respect reduced motion:
const prefersReduced = matchMedia( "(prefers-reduced-motion: reduce)", ).matches; if (prefersReduced) { // Use a static gradient or skip the effect entirely } - Use
minHeightandminWidthto prevent the canvas from collapsing on very small viewports.