
Lightweight 3d Effects
- 1.5k installs
- 629 repo stars
- Updated November 20, 2025
- freshtechbro/claudedesignskills
lightweight-3d-effects is an agent skill that adds pseudo-3D illustrations, Vanta animated backgrounds, and Vanilla-Tilt parallax effects for lightweight landing pages.
About
The lightweight-3d-effects skill combines Zdog, Vanta.js, and Vanilla-Tilt.js for decorative 3D elements and micro-interactions without heavy frameworks. Zdog provides a designer-friendly pseudo-3D engine using Canvas or SVG with drag rotation and small bundle size for illustrative shapes like Ellipse and Rect primitives. Vanta.js supplies animated 3D backgrounds powered by Three.js or p5.js for hero sections. Vanilla-Tilt.js adds smooth parallax tilt on cards and images responding to mouse or gyroscope input. The skill targets decorative illustrations, hero animations, card tilt effects, and performance-focused landing page depth where full WebGL scenes are unnecessary. Setup snippets document CDN imports, illustration configuration, animation loops, and tilt option tuning such as max tilt, perspective, and glare settings. Developers invoke it when tasks mention Zdog pseudo-3D, Vanta backgrounds, Vanilla-Tilt parallax, card tilt, or lightweight landing page visuals.
- Combines Zdog pseudo-3D, Vanta.js animated backgrounds, and Vanilla-Tilt parallax tilt.
- Zdog Canvas or SVG illustrations with drag rotation and small footprint.
- Vanta.js hero backgrounds via Three.js or p5.js without custom WebGL setup.
- Vanilla-Tilt micro-interactions for cards and images with gyroscope support.
- Focused on performance-friendly decorative depth rather than full 3D frameworks.
Lightweight 3d Effects by the numbers
- 1,484 all-time installs (skills.sh)
- +99 installs in the week ending Jul 29, 2026 (Skillselion tracking)
- Ranked #289 of 2,244 Frontend Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 31, 2026 (Skillselion catalog sync)
lightweight-3d-effects capabilities & compatibility
- Capabilities
- zdog pseudo 3d illustration setup · vanta.js animated background integration · vanilla tilt parallax configuration · canvas and svg rendering patterns · performance focused decorative effects · hero section visual depth
- Use cases
- frontend · ui design · web design
What lightweight-3d-effects says it does
This skill combines three powerful libraries for decorative 3D elements and micro-interactions
Ideal for performance-focused designs.
npx skills add https://github.com/freshtechbro/claudedesignskills --skill lightweight-3d-effectsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.5k |
|---|---|
| repo stars | ★ 629 |
| Security audit | 2 / 3 scanners passed |
| Last updated | November 20, 2025 |
| Repository | freshtechbro/claudedesignskills ↗ |
How do I add decorative 3D depth and hero animations to a landing page without importing a heavy 3D framework?
Add lightweight pseudo-3D illustrations, animated backgrounds, and parallax tilt micro-interactions with Zdog, Vanta.js, and Vanilla-Tilt.js.
Who is it for?
Frontend developers adding hero visuals, card tilt, and pseudo-3D decorations on performance-focused marketing pages.
Skip if: Skip when you need full game-engine 3D scenes or complex physics-based WebGL applications.
When should I use this skill?
User asks for Zdog pseudo-3D, Vanta.js backgrounds, Vanilla-Tilt parallax, card tilt effects, or lightweight landing page visuals.
What you get
Working Zdog illustrations, Vanta.js background effects, and Vanilla-Tilt card interactions integrated with minimal performance overhead.
- Hero section 3D snippets
- Product card tilt components
By the numbers
- Covers 8 production example categories in the skill readme
- Combines 3 libraries: Vanta.js, Zdog, and Vanilla-Tilt
Files
Lightweight 3D Effects Skill
Overview
This skill combines three powerful libraries for decorative 3D elements and micro-interactions:
- Zdog: Pseudo-3D engine for designer-friendly vector illustrations
- Vanta.js: Animated 3D backgrounds powered by Three.js/p5.js
- Vanilla-Tilt.js: Smooth parallax tilt effects responding to mouse/gyroscope
When to Use This Skill
- Add decorative 3D illustrations without heavy frameworks
- Create animated backgrounds for hero sections
- Implement subtle parallax tilt effects on cards/images
- Build lightweight landing pages with visual depth
- Add micro-interactions that enhance UX without performance impact
Zdog - Pseudo-3D Illustrations
Core Concepts
Zdog is a pseudo-3D engine that renders flat, round designs in 3D space using Canvas or SVG.
Key Features:
- Designer-friendly declarative API
- Small file size (~28kb minified)
- Canvas or SVG rendering
- Drag rotation built-in
- Smooth animations
Basic Setup
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/zdog@1/dist/zdog.dist.min.js"></script>
<style>
.zdog-canvas {
display: block;
margin: 0 auto;
background: #FDB;
cursor: move;
}
</style>
</head>
<body>
<canvas class="zdog-canvas" width="240" height="240"></canvas>
<script>
let isSpinning = true;
let illo = new Zdog.Illustration({
element: '.zdog-canvas',
zoom: 4,
dragRotate: true,
onDragStart: function() {
isSpinning = false;
},
});
// Add shapes
new Zdog.Ellipse({
addTo: illo,
diameter: 20,
translate: { z: 10 },
stroke: 5,
color: '#636',
});
new Zdog.Rect({
addTo: illo,
width: 20,
height: 20,
translate: { z: -10 },
stroke: 3,
color: '#E62',
fill: true,
});
function animate() {
illo.rotate.y += isSpinning ? 0.03 : 0;
illo.updateRenderGraph();
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>Zdog Shapes
Basic Shapes:
// Circle
new Zdog.Ellipse({
addTo: illo,
diameter: 80,
stroke: 20,
color: '#636',
});
// Rectangle
new Zdog.Rect({
addTo: illo,
width: 80,
height: 60,
stroke: 10,
color: '#E62',
fill: true,
});
// Rounded Rectangle
new Zdog.RoundedRect({
addTo: illo,
width: 60,
height: 40,
cornerRadius: 10,
stroke: 4,
color: '#C25',
fill: true,
});
// Polygon
new Zdog.Polygon({
addTo: illo,
radius: 40,
sides: 5,
stroke: 8,
color: '#EA0',
fill: true,
});
// Line
new Zdog.Shape({
addTo: illo,
path: [
{ x: -40, y: 0 },
{ x: 40, y: 0 },
],
stroke: 6,
color: '#636',
});
// Bezier Curve
new Zdog.Shape({
addTo: illo,
path: [
{ x: -40, y: -20 },
{
bezier: [
{ x: -40, y: 20 },
{ x: 40, y: 20 },
{ x: 40, y: -20 },
],
},
],
stroke: 4,
color: '#C25',
closed: false,
});Zdog Groups
Organize shapes into groups for complex models:
// Create a group
let head = new Zdog.Group({
addTo: illo,
translate: { y: -40 },
});
// Add shapes to group
new Zdog.Ellipse({
addTo: head,
diameter: 60,
stroke: 30,
color: '#FED',
});
// Eyes
new Zdog.Ellipse({
addTo: head,
diameter: 8,
stroke: 4,
color: '#333',
translate: { x: -10, z: 15 },
});
new Zdog.Ellipse({
addTo: head,
diameter: 8,
stroke: 4,
color: '#333',
translate: { x: 10, z: 15 },
});
// Mouth
new Zdog.Shape({
addTo: head,
path: [
{ x: -10, y: 0 },
{
bezier: [
{ x: -5, y: 5 },
{ x: 5, y: 5 },
{ x: 10, y: 0 },
],
},
],
stroke: 2,
color: '#333',
translate: { y: 5, z: 15 },
closed: false,
});
// Rotate entire group
head.rotate.y = Math.PI / 4;Zdog Animation
// Continuous rotation
function animate() {
illo.rotate.y += 0.03;
illo.updateRenderGraph();
requestAnimationFrame(animate);
}
animate();
// Bounce animation
let t = 0;
function bounceAnimate() {
t += 0.05;
illo.translate.y = Math.sin(t) * 20;
illo.updateRenderGraph();
requestAnimationFrame(bounceAnimate);
}
bounceAnimate();
// Interactive rotation with easing
let targetRotateY = 0;
let currentRotateY = 0;
document.addEventListener('mousemove', (event) => {
targetRotateY = (event.clientX / window.innerWidth - 0.5) * Math.PI;
});
function smoothAnimate() {
// Ease towards target
currentRotateY += (targetRotateY - currentRotateY) * 0.1;
illo.rotate.y = currentRotateY;
illo.updateRenderGraph();
requestAnimationFrame(smoothAnimate);
}
smoothAnimate();---
Vanta.js - Animated 3D Backgrounds
Core Concepts
Vanta.js provides animated WebGL backgrounds with minimal setup, powered by Three.js or p5.js.
Key Features:
- 14+ animated effects (Waves, Birds, Net, Clouds, etc.)
- Mouse/touch interaction
- Customizable colors and settings
- ~120KB total (including Three.js)
- 60fps on most devices
Basic Setup
<!DOCTYPE html>
<html>
<head>
<style>
#vanta-bg {
width: 100%;
height: 100vh;
}
.content {
position: relative;
z-index: 1;
color: white;
text-align: center;
padding: 100px 20px;
}
</style>
</head>
<body>
<div id="vanta-bg">
<div class="content">
<h1>My Animated Background</h1>
<p>Content goes here</p>
</div>
</div>
<!-- Three.js (required) -->
<script src="https://cdn.jsdelivr.net/npm/three@0.134.0/build/three.min.js"></script>
<!-- Vanta.js effect -->
<script src="https://cdn.jsdelivr.net/npm/vanta@0.5.24/dist/vanta.waves.min.js"></script>
<script>
VANTA.WAVES({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
color: 0x23153c,
shininess: 30.00,
waveHeight: 15.00,
waveSpeed: 0.75,
zoom: 0.65
});
</script>
</body>
</html>Available Effects
1. WAVES (Three.js)
VANTA.WAVES({
el: "#vanta-bg",
color: 0x23153c,
shininess: 30,
waveHeight: 15,
waveSpeed: 0.75,
zoom: 0.65
});2. CLOUDS (Three.js)
VANTA.CLOUDS({
el: "#vanta-bg",
skyColor: 0x68b8d7,
cloudColor: 0xadc1de,
cloudShadowColor: 0x183550,
sunColor: 0xff9919,
sunGlareColor: 0xff6633,
sunlightColor: 0xff9933,
speed: 1.0
});3. BIRDS (p5.js required)
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@0.5.24/dist/vanta.birds.min.js"></script>
<script>
VANTA.BIRDS({
el: "#vanta-bg",
backgroundColor: 0x23153c,
color1: 0xff0000,
color2: 0x0000ff,
birdSize: 1.5,
wingSpan: 20,
speedLimit: 5,
separation: 40,
alignment: 40,
cohesion: 40,
quantity: 3
});
</script>4. NET (Three.js)
VANTA.NET({
el: "#vanta-bg",
color: 0x3fff00,
backgroundColor: 0x23153c,
points: 10,
maxDistance: 20,
spacing: 15,
showDots: true
});5. CELLS (p5.js required)
VANTA.CELLS({
el: "#vanta-bg",
color1: 0x00ff00,
color2: 0xff0000,
size: 1.5,
speed: 1.0,
scale: 1.0
});6. FOG (Three.js)
VANTA.FOG({
el: "#vanta-bg",
highlightColor: 0xff3f81,
midtoneColor: 0x1d004d,
lowlightColor: 0x2b1a5e,
baseColor: 0x000000,
blurFactor: 0.6,
speed: 1.0,
zoom: 1.0
});Other effects: GLOBE, TRUNK, TOPOLOGY, DOTS, HALO, RINGS
Configuration Options
// Common options for all effects
{
el: "#element-id", // Required: target element
mouseControls: true, // Enable mouse interaction
touchControls: true, // Enable touch interaction
gyroControls: false, // Device orientation
minHeight: 200.00, // Minimum height
minWidth: 200.00, // Minimum width
scale: 1.00, // Size scale
scaleMobile: 1.00, // Mobile scale
// Colors (hex numbers, not strings)
color: 0x23153c,
backgroundColor: 0x000000,
// Performance
forceAnimate: false, // Force animation even when hidden
// Effect-specific options vary by effect
}Vanta.js Methods
// Initialize and store reference
const vantaEffect = VANTA.WAVES({
el: "#vanta-bg",
// ... options
});
// Destroy when done (important for SPAs)
vantaEffect.destroy();
// Update options dynamically
vantaEffect.setOptions({
color: 0xff0000,
waveHeight: 20
});
// Resize (usually automatic)
vantaEffect.resize();React Integration
import { useEffect, useRef, useState } from 'react';
import VANTA from 'vanta/dist/vanta.waves.min';
import * as THREE from 'three';
function VantaBackground() {
const vantaRef = useRef(null);
const [vantaEffect, setVantaEffect] = useState(null);
useEffect(() => {
if (!vantaEffect) {
setVantaEffect(VANTA.WAVES({
el: vantaRef.current,
THREE: THREE,
mouseControls: true,
touchControls: true,
color: 0x23153c,
shininess: 30,
waveHeight: 15,
waveSpeed: 0.75
}));
}
return () => {
if (vantaEffect) vantaEffect.destroy();
};
}, [vantaEffect]);
return (
<div ref={vantaRef} style={{ width: '100%', height: '100vh' }}>
<div className="content">
<h1>React + Vanta.js</h1>
</div>
</div>
);
}---
Vanilla-Tilt.js - Parallax Tilt Effects
Core Concepts
Vanilla-Tilt.js adds smooth 3D tilt effects responding to mouse movement and device orientation.
Key Features:
- Lightweight (~8.5kb minified)
- No dependencies
- Gyroscope support
- Optional glare effect
- Smooth transitions
Basic Setup
<!DOCTYPE html>
<html>
<head>
<style>
.tilt-card {
width: 300px;
height: 400px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 15px;
margin: 50px auto;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
transform-style: preserve-3d;
}
.tilt-inner {
transform: translateZ(60px);
}
</style>
</head>
<body>
<div class="tilt-card" data-tilt>
<div class="tilt-inner">Hover Me!</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vanilla-tilt@1.8.1/dist/vanilla-tilt.min.js"></script>
</body>
</html>Configuration Options
VanillaTilt.init(document.querySelector(".tilt-card"), {
// Rotation
max: 25, // Max tilt angle (degrees)
reverse: false, // Reverse tilt direction
startX: 0, // Initial tilt X (degrees)
startY: 0, // Initial tilt Y (degrees)
// Appearance
perspective: 1000, // Transform perspective (lower = more intense)
scale: 1.1, // Scale on hover (1 = no scale)
// Animation
speed: 400, // Transition speed (ms)
transition: true, // Enable smooth transitions
easing: "cubic-bezier(.03,.98,.52,.99)",
// Behavior
axis: null, // Restrict to "x" or "y" axis
reset: true, // Reset on mouse leave
"reset-to-start": true, // Reset to start position vs [0,0]
// Glare effect
glare: true, // Enable glare
"max-glare": 0.5, // Glare opacity (0-1)
"glare-prerender": false, // Pre-render glare elements
// Advanced
full-page-listening: false, // Listen to entire page
gyroscope: true, // Enable device orientation
gyroscopeMinAngleX: -45, // Min X angle
gyroscopeMaxAngleX: 45, // Max X angle
gyroscopeMinAngleY: -45, // Min Y angle
gyroscopeMaxAngleY: 45, // Max Y angle
gyroscopeSamples: 10 // Calibration samples
});Advanced Examples
Card with Glare Effect:
<div class="tilt-card" data-tilt
data-tilt-glare
data-tilt-max-glare="0.5"
data-tilt-scale="1.1">
<div class="tilt-inner">
<h3>Premium Card</h3>
<p>With glare effect</p>
</div>
</div>Layered 3D Effect:
<style>
.tilt-card {
transform-style: preserve-3d;
}
.layer-1 {
transform: translateZ(20px);
}
.layer-2 {
transform: translateZ(40px);
}
.layer-3 {
transform: translateZ(60px);
}
</style>
<div class="tilt-card" data-tilt data-tilt-max="15">
<div class="layer-1">Background</div>
<div class="layer-2">Middle</div>
<div class="layer-3">Front</div>
</div>Programmatic Control:
const element = document.querySelector(".tilt-card");
VanillaTilt.init(element, {
max: 25,
speed: 400,
glare: true,
"max-glare": 0.5
});
// Get tilt values
element.addEventListener("tiltChange", (e) => {
console.log("Tilt:", e.detail);
});
// Reset programmatically
element.vanillaTilt.reset();
// Destroy instance
element.vanillaTilt.destroy();
// Get current values
const values = element.vanillaTilt.getValues();
console.log(values); // { tiltX, tiltY, percentageX, percentageY, angle }React Integration
import { useEffect, useRef } from 'react';
import VanillaTilt from 'vanilla-tilt';
function TiltCard({ children, options }) {
const tiltRef = useRef(null);
useEffect(() => {
const element = tiltRef.current;
VanillaTilt.init(element, {
max: 25,
speed: 400,
glare: true,
"max-glare": 0.5,
...options
});
return () => {
element.vanillaTilt.destroy();
};
}, [options]);
return (
<div ref={tiltRef} className="tilt-card">
{children}
</div>
);
}
// Usage
<TiltCard options={{ max: 30, scale: 1.1 }}>
<h3>My Card</h3>
</TiltCard>---
Common Patterns
Pattern 1: Hero Section with Vanta + Content
<section id="hero">
<div class="hero-content">
<h1>Welcome</h1>
<p>Animated background with content overlay</p>
<button>Get Started</button>
</div>
</section>
<style>
#hero {
position: relative;
width: 100%;
height: 100vh;
overflow: hidden;
}
.hero-content {
position: relative;
z-index: 1;
color: white;
text-align: center;
padding-top: 20vh;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/three@0.134.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@0.5.24/dist/vanta.waves.min.js"></script>
<script>
VANTA.WAVES({
el: "#hero",
mouseControls: true,
touchControls: true,
color: 0x23153c,
waveHeight: 20,
waveSpeed: 1.0
});
</script>Pattern 2: Zdog Icon Grid
<div class="icon-grid">
<canvas class="icon" width="120" height="120"></canvas>
<canvas class="icon" width="120" height="120"></canvas>
<canvas class="icon" width="120" height="120"></canvas>
</div>
<script src="https://unpkg.com/zdog@1/dist/zdog.dist.min.js"></script>
<script>
document.querySelectorAll('.icon').forEach((canvas, index) => {
let illo = new Zdog.Illustration({
element: canvas,
zoom: 3,
dragRotate: true
});
// Create different icon for each canvas
const icons = [
createHeartIcon,
createStarIcon,
createCheckIcon
];
icons[index](illo);
function animate() {
illo.rotate.y += 0.02;
illo.updateRenderGraph();
requestAnimationFrame(animate);
}
animate();
});
function createHeartIcon(illo) {
new Zdog.Shape({
addTo: illo,
path: [
{ x: 0, y: -10 },
{
bezier: [
{ x: -20, y: -20 },
{ x: -20, y: 0 },
{ x: 0, y: 10 }
]
},
{
bezier: [
{ x: 20, y: 0 },
{ x: 20, y: -20 },
{ x: 0, y: -10 }
]
}
],
stroke: 6,
color: '#E62',
fill: true,
closed: false
});
}
</script>Pattern 3: Tilt Card Gallery
<div class="card-gallery">
<div class="card" data-tilt data-tilt-glare data-tilt-max-glare="0.3">
<img src="product1.jpg" alt="Product 1">
<h3>Product 1</h3>
</div>
<div class="card" data-tilt data-tilt-glare data-tilt-max-glare="0.3">
<img src="product2.jpg" alt="Product 2">
<h3>Product 2</h3>
</div>
<div class="card" data-tilt data-tilt-glare data-tilt-max-glare="0.3">
<img src="product3.jpg" alt="Product 3">
<h3>Product 3</h3>
</div>
</div>
<style>
.card-gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
padding: 50px;
}
.card {
background: white;
border-radius: 15px;
padding: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
transform-style: preserve-3d;
}
.card img {
width: 100%;
border-radius: 10px;
transform: translateZ(40px);
}
.card h3 {
margin-top: 15px;
transform: translateZ(60px);
}
</style>
<script src="https://cdn.jsdelivr.net/npm/vanilla-tilt@1.8.1/dist/vanilla-tilt.min.js"></script>Pattern 4: Combined Effect - Vanta Background + Tilt Cards
<div id="vanta-section">
<div class="container">
<h1>Our Services</h1>
<div class="services-grid">
<div class="service-card" data-tilt data-tilt-scale="1.05">
<div class="icon">🚀</div>
<h3>Fast</h3>
<p>Lightning quick performance</p>
</div>
<div class="service-card" data-tilt data-tilt-scale="1.05">
<div class="icon">🎨</div>
<h3>Beautiful</h3>
<p>Stunning visual design</p>
</div>
<div class="service-card" data-tilt data-tilt-scale="1.05">
<div class="icon">💪</div>
<h3>Powerful</h3>
<p>Feature-rich platform</p>
</div>
</div>
</div>
</div>
<script>
// Vanta background
VANTA.NET({
el: "#vanta-section",
color: 0x3fff00,
backgroundColor: 0x23153c,
points: 10,
maxDistance: 20
});
// Tilt cards
VanillaTilt.init(document.querySelectorAll(".service-card"), {
max: 15,
speed: 400,
glare: true,
"max-glare": 0.3
});
</script>---
Performance Best Practices
Zdog Optimization
1. Limit Shape Count: Keep total shapes under 100 for smooth 60fps 2. Use Groups: Organize related shapes for easier management 3. Optimize Animation Loop: Only call updateRenderGraph() when needed 4. Canvas vs SVG: Canvas is faster for animations, SVG for static illustrations
Vanta.js Optimization
1. Single Instance: Use only 1-2 Vanta effects per page 2. Mobile Fallback: Disable on mobile or use static background 3. Destroy on Unmount: Always call .destroy() in SPAs 4. Reduce Particle Count: Lower points, quantity for better performance
// Mobile detection and fallback
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
if (!isMobile) {
VANTA.WAVES({
el: "#hero",
// ... options
});
} else {
document.getElementById('hero').style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
}Vanilla-Tilt Optimization
1. Limit Instances: Apply to visible elements only 2. Reduce `gyroscopeSamples`: Lower for better mobile performance 3. Disable on Low-End Devices: Check device capabilities 4. Use CSS `will-change`: Hint browser for transforms
.tilt-card {
will-change: transform;
}---
Common Pitfalls
Pitfall 1: Multiple Vanta Instances
Problem: Multiple Vanta effects cause performance issues
Solution: Use only one effect, or lazy-load effects per section
// Intersection Observer to load Vanta only when visible
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !entry.target.vantaEffect) {
entry.target.vantaEffect = VANTA.WAVES({
el: entry.target,
// ... options
});
}
});
});
observer.observe(document.getElementById('hero'));Pitfall 2: Memory Leaks in SPAs
Problem: Vanta/Tilt not destroyed on component unmount
Solution: Always clean up
// React useEffect cleanup
useEffect(() => {
const effect = VANTA.WAVES({ el: vantaRef.current });
return () => {
effect.destroy(); // Important!
};
}, []);Pitfall 3: Zdog Not Rendering
Problem: Canvas appears blank
Causes:
- Forgot to call
updateRenderGraph() - Canvas size is 0
- Shapes are outside view
Solution:
// Always call updateRenderGraph after shape changes
illo.updateRenderGraph();
// Ensure canvas has dimensions
<canvas width="240" height="240"></canvas>
// Check shape positions are visible
new Zdog.Ellipse({
addTo: illo,
diameter: 20,
translate: { z: 0 }, // Keep close to origin
});Pitfall 4: Tilt Not Working on Mobile
Problem: Tilt doesn't respond on mobile devices
Solution: Enable gyroscope controls
VanillaTilt.init(element, {
gyroscope: true,
gyroscopeMinAngleX: -45,
gyroscopeMaxAngleX: 45
});Pitfall 5: Color Format Confusion (Vanta.js)
Problem: Colors don't work
Cause: Vanta.js uses hex numbers, not strings
// ❌ Wrong
color: "#23153c"
// ✅ Correct
color: 0x23153c---
Resources
Zdog:
Vanta.js:
Vanilla-Tilt.js:
Related Skills
- threejs-webgl: For more complex 3D graphics beyond decorative effects
- gsap-scrolltrigger: For animating these effects on scroll
- motion-framer: For React component animations alongside these effects
- react-three-fiber: Advanced 3D when lightweight effects aren't enough
Lightweight 3D Effects - Production Examples
Real-world patterns and examples combining Vanta.js, Zdog, and Vanilla-Tilt for production applications.
---
Table of Contents
1. Hero Sections 2. Product Cards 3. Portfolio Layouts 4. Interactive Dashboards 5. Marketing Pages 6. E-Commerce 7. SaaS Landing Pages 8. Performance Patterns
---
1. Hero Sections
Example 1.1: Split Hero with Vanta + Zdog Logo
Use Case: SaaS landing page with animated background and brand icon
<div class="hero-split">
<div id="vanta-bg"></div>
<div class="hero-content">
<canvas class="logo-zdog" width="200" height="200"></canvas>
<h1>Welcome to Your Product</h1>
<p>Tagline describing your amazing service</p>
<button class="cta-button tilt-btn" data-tilt>Get Started</button>
</div>
</div>.hero-split {
position: relative;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
#vanta-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}
.hero-content {
text-align: center;
z-index: 1;
color: white;
}
.logo-zdog {
display: block;
margin: 0 auto 2rem;
filter: drop-shadow(0 10px 30px rgba(0,0,0,0.3));
}
.cta-button {
padding: 1rem 3rem;
font-size: 1.2rem;
background: rgba(255, 255, 255, 0.2);
color: white;
border: 2px solid white;
border-radius: 50px;
cursor: pointer;
backdrop-filter: blur(10px);
transform-style: preserve-3d;
}// Vanta background
VANTA.NET({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
color: 0x3fafff,
backgroundColor: 0x23153c,
points: 10,
maxDistance: 20
});
// Zdog logo
const logoIllo = new Zdog.Illustration({
element: '.logo-zdog',
zoom: 3,
dragRotate: false
});
// Create your brand icon here
const logo = new Zdog.Shape({
addTo: logoIllo,
path: [
{ x: -30, y: -30 },
{ x: 30, y: -30 },
{ x: 0, y: 30 }
],
closed: true,
stroke: 8,
color: '#fff',
fill: true
});
function animate() {
logo.rotate.y += 0.02;
logoIllo.updateRenderGraph();
requestAnimationFrame(animate);
}
animate();
// Tilt button
VanillaTilt.init(document.querySelector(".cta-button"), {
max: 15,
glare: true,
"max-glare": 0.5,
scale: 1.1
});---
Example 1.2: Full-Screen Hero with Scroll Indicator
Use Case: Portfolio site with immersive background and scroll prompt
<section class="hero-fullscreen">
<div id="vanta-bg"></div>
<div class="hero-content">
<h1 class="hero-title">Creative Developer</h1>
<p class="hero-subtitle">Building beautiful experiences</p>
<div class="scroll-indicator">
<canvas class="scroll-zdog" width="60" height="80"></canvas>
<span>Scroll to explore</span>
</div>
</div>
</section>// Vanta clouds
VANTA.CLOUDS({
el: "#vanta-bg",
skyColor: 0x68b8d7,
cloudColor: 0xadc1de,
speed: 0.5
});
// Animated scroll indicator
const scrollIllo = new Zdog.Illustration({
element: '.scroll-zdog',
zoom: 2
});
// Mouse with scrolling animation
const mouse = new Zdog.RoundedRect({
addTo: scrollIllo,
width: 20,
height: 30,
cornerRadius: 10,
stroke: 2,
color: '#fff'
});
const wheel = new Zdog.Ellipse({
addTo: scrollIllo,
diameter: 4,
translate: { y: -8 },
stroke: 2,
color: '#fff',
fill: true
});
let wheelY = -8;
function animate() {
wheelY += 0.5;
if (wheelY > 8) wheelY = -8;
wheel.translate.y = wheelY;
scrollIllo.updateRenderGraph();
requestAnimationFrame(animate);
}
animate();---
2. Product Cards
Example 2.1: Pricing Cards with Tilt + Zdog Icons
Use Case: SaaS pricing page with interactive plan cards
<div class="pricing-grid">
<div class="pricing-card tilt-card" data-tilt>
<canvas class="plan-icon" width="100" height="100"></canvas>
<h3>Starter</h3>
<p class="price">$9<span>/mo</span></p>
<ul>
<li>Feature 1</li>
<li>Feature 2</li>
<li>Feature 3</li>
</ul>
<button class="select-btn">Select Plan</button>
</div>
<!-- Repeat for Pro and Enterprise -->
</div>// Initialize tilt for all cards
VanillaTilt.init(document.querySelectorAll(".pricing-card"), {
max: 10,
speed: 400,
glare: true,
"max-glare": 0.2,
scale: 1.02
});
// Create unique Zdog icon for each plan
const starterIcon = new Zdog.Illustration({
element: document.querySelectorAll('.plan-icon')[0],
zoom: 2
});
new Zdog.Ellipse({
addTo: starterIcon,
diameter: 30,
stroke: 5,
color: '#4CAF50',
fill: true
});
// Animate
function animate() {
starterIcon.rotate.y += 0.02;
starterIcon.updateRenderGraph();
requestAnimationFrame(animate);
}
animate();.pricing-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
padding: 4rem 2rem;
}
.pricing-card {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 2.5rem;
border: 1px solid rgba(255, 255, 255, 0.2);
text-align: center;
transform-style: preserve-3d;
}
.plan-icon {
display: block;
margin: 0 auto 1.5rem;
transform: translateZ(30px);
}
.price {
font-size: 3rem;
font-weight: 700;
transform: translateZ(20px);
}
.select-btn {
transform: translateZ(40px);
/* Additional button styles */
}---
Example 2.2: Product Showcase Gallery
Use Case: E-commerce product grid with hover effects
<div class="product-gallery">
<div class="product-card tilt-card" data-tilt>
<div class="product-badge">NEW</div>
<img src="product1.jpg" alt="Product" class="product-image">
<canvas class="product-icon" width="80" height="80"></canvas>
<h3>Product Name</h3>
<p class="product-price">$99.99</p>
<button class="add-to-cart">Add to Cart</button>
</div>
</div>VanillaTilt.init(document.querySelectorAll(".product-card"), {
max: 12,
speed: 400,
glare: true,
"max-glare": 0.3,
scale: 1.05
});
// Add floating Zdog indicator on each card
document.querySelectorAll('.product-icon').forEach((canvas, i) => {
const illo = new Zdog.Illustration({
element: canvas,
zoom: 1.5
});
// Star rating indicator
for (let j = 0; j < 5; j++) {
new Zdog.Polygon({
addTo: illo,
sides: 5,
radius: 6,
translate: { x: (j - 2) * 14 },
stroke: 2,
color: '#FFD700',
fill: true
});
}
function animate() {
illo.rotate.z = Math.sin(Date.now() * 0.001 + i) * 0.1;
illo.updateRenderGraph();
requestAnimationFrame(animate);
}
animate();
});---
3. Portfolio Layouts
Example 3.1: Project Grid with Vanta Backgrounds
Use Case: Portfolio with per-project animated backgrounds
<div class="portfolio-grid">
<div class="project-card">
<div class="project-vanta" data-effect="waves"></div>
<div class="project-content tilt-content" data-tilt>
<canvas class="project-icon" width="80" height="80"></canvas>
<h3>Project Title</h3>
<p>Project description</p>
<a href="#" class="view-project">View Project →</a>
</div>
</div>
</div>// Initialize Vanta for each project card
document.querySelectorAll('.project-card').forEach((card, index) => {
const vantaEl = card.querySelector('.project-vanta');
const effect = vantaEl.dataset.effect;
const effects = {
waves: () => VANTA.WAVES({
el: vantaEl,
color: 0x23153c,
waveHeight: 10,
zoom: 0.75
}),
cells: () => VANTA.CELLS({
el: vantaEl,
color1: 0x18b0c6,
size: 1.5
}),
net: () => VANTA.NET({
el: vantaEl,
color: 0x3fafff,
points: 8
})
};
effects[effect]();
// Tilt only the content overlay
VanillaTilt.init(card.querySelector('.project-content'), {
max: 8,
scale: 1.02,
glare: true,
"max-glare": 0.2
});
});.project-card {
position: relative;
height: 400px;
border-radius: 20px;
overflow: hidden;
}
.project-vanta {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
.project-content {
position: relative;
z-index: 2;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
padding: 2rem;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
transform-style: preserve-3d;
}
.project-icon {
transform: translateZ(30px);
}
.project-content h3 {
transform: translateZ(20px);
}---
4. Interactive Dashboards
Example 4.1: Stats Dashboard with Zdog Charts
Use Case: Analytics dashboard with 3D data visualization
<div class="dashboard">
<div id="vanta-bg"></div>
<div class="stats-grid">
<div class="stat-card tilt-card" data-tilt>
<canvas class="stat-chart" width="200" height="200"></canvas>
<h3>Users</h3>
<p class="stat-value">12,345</p>
<span class="stat-change">+12.5%</span>
</div>
</div>
</div>// Subtle background
VANTA.TOPOLOGY({
el: "#vanta-bg",
color: 0x667eea,
backgroundColor: 0xf8f9fa
});
// Zdog bar chart
const chartIllo = new Zdog.Illustration({
element: '.stat-chart',
zoom: 2,
dragRotate: true
});
const data = [10, 25, 15, 30, 20, 35, 28];
const barWidth = 8;
const spacing = 12;
data.forEach((value, i) => {
new Zdog.Box({
addTo: chartIllo,
width: barWidth,
height: value,
depth: barWidth,
translate: {
x: (i - 3) * spacing,
y: value / 2
},
stroke: 1,
color: `hsl(${220 + i * 10}, 70%, 60%)`,
fill: true,
topFace: '#667eea',
bottomFace: '#764ba2'
});
});
function animate() {
chartIllo.rotate.y += 0.01;
chartIllo.updateRenderGraph();
requestAnimationFrame(animate);
}
animate();
// Tilt cards
VanillaTilt.init(document.querySelectorAll(".stat-card"), {
max: 5,
glare: false,
scale: 1.01
});---
5. Marketing Pages
Example 5.1: Feature Showcase with Staggered Animations
Use Case: Product marketing page with animated feature cards
<section class="features-section">
<div id="vanta-bg"></div>
<div class="features-container">
<div class="feature-item tilt-card" data-tilt>
<canvas class="feature-icon" width="120" height="120"></canvas>
<h3>Lightning Fast</h3>
<p>Optimized for performance</p>
</div>
<div class="feature-item tilt-card" data-tilt data-delay="100">
<canvas class="feature-icon" width="120" height="120"></canvas>
<h3>Secure</h3>
<p>Enterprise-grade security</p>
</div>
<div class="feature-item tilt-card" data-tilt data-delay="200">
<canvas class="feature-icon" width="120" height="120"></canvas>
<h3>Scalable</h3>
<p>Grows with your business</p>
</div>
</div>
</section>// Vanta background
VANTA.DOTS({
el: "#vanta-bg",
color: 0xff3f81,
backgroundColor: 0xffffff,
size: 2,
spacing: 30
});
// Staggered tilt initialization
document.querySelectorAll('.feature-item').forEach((item, index) => {
const delay = parseInt(item.dataset.delay) || 0;
setTimeout(() => {
VanillaTilt.init(item, {
max: 12,
speed: 400,
glare: true,
"max-glare": 0.2
});
// Fade in animation
item.style.opacity = '1';
item.style.transform = 'translateY(0)';
}, delay);
// Initial state
item.style.opacity = '0';
item.style.transform = 'translateY(20px)';
item.style.transition = 'opacity 0.6s, transform 0.6s';
});
// Create unique Zdog icons
const icons = document.querySelectorAll('.feature-icon');
// Lightning icon
const lightning = new Zdog.Illustration({
element: icons[0],
zoom: 2
});
new Zdog.Shape({
addTo: lightning,
path: [
{ x: 0, y: -30 },
{ x: 10, y: 0 },
{ x: -5, y: 0 },
{ x: 0, y: 30 }
],
closed: true,
stroke: 3,
color: '#FFD700',
fill: true
});
function animateLightning() {
lightning.rotate.z = Math.sin(Date.now() * 0.002) * 0.2;
lightning.updateRenderGraph();
requestAnimationFrame(animateLightning);
}
animateLightning();
// Similar patterns for other icons...---
6. E-Commerce
Example 6.1: Product Detail Page with 360° View
Use Case: Product page with interactive 3D model
<div class="product-detail">
<div class="product-view">
<canvas class="product-3d" width="600" height="600"></canvas>
<div class="view-controls">
<button id="rotate-left">←</button>
<button id="rotate-right">→</button>
<button id="zoom-in">+</button>
<button id="zoom-out">−</button>
</div>
</div>
<div class="product-info tilt-card" data-tilt>
<h1>Product Name</h1>
<p class="price">$299.99</p>
<button class="buy-now">Buy Now</button>
</div>
</div>// Zdog 3D product model
const productIllo = new Zdog.Illustration({
element: '.product-3d',
zoom: 3,
dragRotate: true
});
// Example: Watch model
const watch = new Zdog.Anchor({
addTo: productIllo
});
// Watch body
new Zdog.Cylinder({
addTo: watch,
diameter: 30,
length: 10,
stroke: 2,
color: '#333',
fill: true
});
// Watch face
new Zdog.Ellipse({
addTo: watch,
diameter: 28,
translate: { z: 6 },
stroke: 1,
color: '#fff',
fill: true
});
// Watch hands
new Zdog.Shape({
addTo: watch,
path: [{ y: 0 }, { y: -10 }],
translate: { z: 7 },
stroke: 2,
color: '#333'
});
let rotationSpeed = 0;
function animate() {
watch.rotate.y += rotationSpeed;
productIllo.updateRenderGraph();
requestAnimationFrame(animate);
}
animate();
// Control buttons
document.getElementById('rotate-left').addEventListener('click', () => {
rotationSpeed = -0.05;
setTimeout(() => rotationSpeed = 0, 500);
});
document.getElementById('rotate-right').addEventListener('click', () => {
rotationSpeed = 0.05;
setTimeout(() => rotationSpeed = 0, 500);
});
let currentZoom = 3;
document.getElementById('zoom-in').addEventListener('click', () => {
currentZoom += 0.5;
productIllo.zoom = currentZoom;
});
document.getElementById('zoom-out').addEventListener('click', () => {
currentZoom = Math.max(1, currentZoom - 0.5);
productIllo.zoom = currentZoom;
});
// Tilt info card
VanillaTilt.init(document.querySelector('.product-info'), {
max: 8,
glare: true,
"max-glare": 0.2
});---
7. SaaS Landing Pages
Example 7.1: Feature Comparison Table
Use Case: SaaS pricing page with interactive comparison
<section class="comparison-section">
<div id="vanta-bg"></div>
<table class="comparison-table">
<thead>
<tr>
<th>Features</th>
<th class="plan-column tilt-column" data-tilt>
<canvas class="plan-icon" width="60" height="60"></canvas>
<span>Starter</span>
</th>
<th class="plan-column tilt-column" data-tilt>
<canvas class="plan-icon" width="60" height="60"></canvas>
<span>Pro</span>
</th>
<th class="plan-column tilt-column" data-tilt>
<canvas class="plan-icon" width="60" height="60"></canvas>
<span>Enterprise</span>
</th>
</tr>
</thead>
<tbody>
<!-- Feature rows -->
</tbody>
</table>
</section>// Subtle background
VANTA.RINGS({
el: "#vanta-bg",
backgroundColor: 0xf8f9fa,
color: 0x667eea
});
// Tilt plan columns
VanillaTilt.init(document.querySelectorAll('.plan-column'), {
max: 5,
glare: true,
"max-glare": 0.1,
scale: 1.02,
axis: 'y' // Only tilt vertically
});
// Zdog icons for each plan
const plans = document.querySelectorAll('.plan-icon');
// Starter: Single star
const starter = new Zdog.Illustration({
element: plans[0],
zoom: 1.5
});
new Zdog.Polygon({
addTo: starter,
sides: 5,
radius: 15,
stroke: 2,
color: '#4CAF50',
fill: true
});
// Pro: Two stars
const pro = new Zdog.Illustration({
element: plans[1],
zoom: 1.5
});
[-8, 8].forEach(x => {
new Zdog.Polygon({
addTo: pro,
sides: 5,
radius: 12,
translate: { x },
stroke: 2,
color: '#2196F3',
fill: true
});
});
// Enterprise: Three stars
const enterprise = new Zdog.Illustration({
element: plans[2],
zoom: 1.5
});
[-12, 0, 12].forEach(x => {
new Zdog.Polygon({
addTo: enterprise,
sides: 5,
radius: 10,
translate: { x },
stroke: 2,
color: '#9C27B0',
fill: true
});
});
// Animate all
function animate() {
starter.rotate.z += 0.02;
pro.rotate.z += 0.02;
enterprise.rotate.z += 0.02;
starter.updateRenderGraph();
pro.updateRenderGraph();
enterprise.updateRenderGraph();
requestAnimationFrame(animate);
}
animate();---
8. Performance Patterns
Example 8.1: Lazy Loading Vanta Backgrounds
Use Case: Improve initial page load by deferring Vanta initialization
// Lazy load Vanta when section is visible
const vantaSections = document.querySelectorAll('[data-vanta]');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const section = entry.target;
const effect = section.dataset.vanta;
// Load effect
if (effect === 'waves') {
VANTA.WAVES({
el: section,
color: 0x23153c,
waveHeight: 15
});
}
// Stop observing
observer.unobserve(section);
}
});
}, { threshold: 0.1 });
vantaSections.forEach(section => observer.observe(section));---
Example 8.2: Conditional Effect Loading Based on Device
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
const isLowEnd = navigator.hardwareConcurrency <= 4;
if (!isMobile && !isLowEnd) {
// Full effects
VANTA.WAVES({
el: "#vanta-bg",
color: 0x23153c,
waveHeight: 20,
waveSpeed: 1.0
});
VanillaTilt.init(document.querySelectorAll(".tilt-card"), {
max: 25,
glare: true,
"max-glare": 0.5
});
} else if (isMobile && !isLowEnd) {
// Mobile-optimized
VANTA.DOTS({ // Lighter effect
el: "#vanta-bg",
size: 2,
spacing: 40
});
// No tilt on mobile, use gyro instead
VanillaTilt.init(document.querySelectorAll(".tilt-card"), {
max: 10,
gyroscope: true,
glare: false
});
} else {
// Low-end devices: static gradient
document.getElementById('vanta-bg').style.background =
'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
// No tilt effects
}---
Example 8.3: Request Idle Callback for Zdog Animations
// Use requestIdleCallback for non-critical animations
let zdogQueue = [];
function animateZdog() {
if ('requestIdleCallback' in window) {
requestIdleCallback(() => {
zdogQueue.forEach(illo => illo.updateRenderGraph());
requestAnimationFrame(animateZdog);
});
} else {
// Fallback
zdogQueue.forEach(illo => illo.updateRenderGraph());
requestAnimationFrame(animateZdog);
}
}
// Add illustrations to queue
zdogQueue.push(illo1, illo2, illo3);
animateZdog();---
Example 8.4: Debounced Resize Handling
let resizeTimer;
let vantaEffect;
function initVanta() {
vantaEffect = VANTA.WAVES({ el: "#vanta-bg" });
}
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
// Destroy and recreate on significant resize
if (vantaEffect) {
vantaEffect.destroy();
}
initVanta();
}, 500);
});
initVanta();---
Complete Integration Example
Full-Stack Landing Page
Combining all three libraries for a production landing page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Production Landing Page</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<!-- Hero with Vanta -->
<section class="hero" id="hero">
<div id="vanta-bg"></div>
<div class="hero-content">
<canvas class="logo-zdog" width="200" height="200"></canvas>
<h1>Your Amazing Product</h1>
<p>The tagline that converts</p>
<button class="cta-btn tilt-btn" data-tilt>Get Started Free</button>
</div>
</section>
<!-- Features with Tilt Cards -->
<section class="features">
<h2>Features</h2>
<div class="feature-grid">
<div class="feature-card tilt-card" data-tilt>
<canvas class="feature-icon" width="100" height="100"></canvas>
<h3>Fast</h3>
<p>Lightning-fast performance</p>
</div>
<div class="feature-card tilt-card" data-tilt>
<canvas class="feature-icon" width="100" height="100"></canvas>
<h3>Secure</h3>
<p>Bank-level security</p>
</div>
<div class="feature-card tilt-card" data-tilt>
<canvas class="feature-icon" width="100" height="100"></canvas>
<h3>Scalable</h3>
<p>Grows with you</p>
</div>
</div>
</section>
<!-- Pricing with Zdog + Tilt -->
<section class="pricing" data-vanta="topology">
<h2>Pricing</h2>
<div class="pricing-grid">
<!-- Pricing cards here -->
</div>
</section>
<!-- Scripts -->
<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.waves.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.topology.min.js"></script>
<script src="https://unpkg.com/zdog@1/dist/zdog.dist.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vanilla-tilt/1.8.1/vanilla-tilt.min.js"></script>
<script src="app.js"></script>
</body>
</html>// app.js
// Performance check
const performanceLevel = (() => {
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
const cores = navigator.hardwareConcurrency || 2;
if (isMobile && cores <= 4) return 'low';
if (isMobile || cores <= 4) return 'medium';
return 'high';
})();
console.log('Performance level:', performanceLevel);
// Conditional initialization
if (performanceLevel === 'high') {
// Full effects
initFullEffects();
} else if (performanceLevel === 'medium') {
initMediumEffects();
} else {
initLowEffects();
}
function initFullEffects() {
// Hero Vanta
VANTA.WAVES({
el: "#vanta-bg",
color: 0x23153c,
waveHeight: 20,
waveSpeed: 1.0
});
// Pricing Vanta (lazy load)
const pricingObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
VANTA.TOPOLOGY({
el: entry.target,
color: 0x667eea
});
pricingObserver.unobserve(entry.target);
}
});
});
pricingObserver.observe(document.querySelector('[data-vanta="topology"]'));
// Zdog icons
initZdogIcons();
// Vanilla Tilt
VanillaTilt.init(document.querySelectorAll(".tilt-card"), {
max: 15,
glare: true,
"max-glare": 0.3,
scale: 1.05
});
VanillaTilt.init(document.querySelector(".cta-btn"), {
max: 20,
glare: true,
"max-glare": 0.5,
scale: 1.1
});
}
function initMediumEffects() {
// Simpler Vanta
VANTA.DOTS({
el: "#vanta-bg",
size: 2,
spacing: 40
});
// Zdog without heavy animations
initZdogIcons();
// Lighter tilt
VanillaTilt.init(document.querySelectorAll(".tilt-card"), {
max: 10,
glare: false,
scale: 1.02
});
}
function initLowEffects() {
// Static gradient
document.getElementById('vanta-bg').style.background =
'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
// Static Zdog icons
initZdogIcons(false); // No animation
// No tilt effects
}
function initZdogIcons(animate = true) {
// Logo
const logoIllo = new Zdog.Illustration({
element: '.logo-zdog',
zoom: 3
});
const logo = new Zdog.Box({
addTo: logoIllo,
width: 30,
height: 30,
depth: 30,
stroke: 2,
color: '#fff',
fill: true
});
if (animate) {
function animateLogo() {
logo.rotate.y += 0.02;
logoIllo.updateRenderGraph();
requestAnimationFrame(animateLogo);
}
animateLogo();
} else {
logoIllo.updateRenderGraph();
}
// Feature icons
const featureIcons = document.querySelectorAll('.feature-icon');
featureIcons.forEach((canvas, i) => {
const illo = new Zdog.Illustration({
element: canvas,
zoom: 2
});
// Different icon for each feature
if (i === 0) {
// Lightning
new Zdog.Shape({
addTo: illo,
path: [
{ x: 0, y: -20 },
{ x: 8, y: 0 },
{ x: -4, y: 0 },
{ x: 0, y: 20 }
],
closed: true,
stroke: 3,
color: '#FFD700',
fill: true
});
} else if (i === 1) {
// Shield
new Zdog.Shape({
addTo: illo,
path: [
{ x: 0, y: -20 },
{ x: 15, y: -10 },
{ x: 15, y: 10 },
{ x: 0, y: 20 },
{ x: -15, y: 10 },
{ x: -15, y: -10 }
],
closed: true,
stroke: 3,
color: '#4CAF50',
fill: true
});
} else {
// Graph
[5, 15, 10, 20].forEach((h, j) => {
new Zdog.Rect({
addTo: illo,
width: 6,
height: h,
translate: { x: (j - 1.5) * 8, y: h / 2 - 10 },
stroke: 2,
color: '#2196F3',
fill: true
});
});
}
if (animate) {
function animateFeature() {
illo.rotate.y += 0.01;
illo.updateRenderGraph();
requestAnimationFrame(animateFeature);
}
animateFeature();
} else {
illo.updateRenderGraph();
}
});
}
// Cleanup
window.addEventListener('beforeunload', () => {
document.querySelectorAll('.tilt-card').forEach(card => {
if (card.vanillaTilt) card.vanillaTilt.destroy();
});
});---
Best Practices Summary
1. Performance First
- Test on low-end devices
- Implement conditional loading
- Use lazy loading for off-screen effects
2. Accessibility
- Provide fallbacks for disabled animations
- Maintain sufficient color contrast
- Ensure interactive elements are keyboard-accessible
3. Mobile Optimization
- Disable heavy effects on mobile
- Use simpler Vanta effects (DOTS, RINGS)
- Consider using gyroscope for tilt on mobile
4. Memory Management
- Always destroy effects on unmount
- Clean up event listeners
- Use Intersection Observer for visibility detection
5. User Experience
- Keep animations subtle (max tilt: 10-15°)
- Provide pause/play controls for accessibility
- Test cross-browser compatibility
---
These examples demonstrate production-ready patterns for building stunning, performant web experiences with lightweight 3D effects.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lightweight 3D Effects - Starter Template</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- Vanta.js Background -->
<div id="vanta-bg"></div>
<!-- Main Content -->
<div class="container">
<!-- Hero Section -->
<section class="hero">
<canvas class="hero-zdog" width="200" height="200"></canvas>
<h1>Lightweight 3D Effects</h1>
<p class="subtitle">Combining Vanta.js, Zdog, and Vanilla-Tilt</p>
</section>
<!-- Feature Cards -->
<section class="cards">
<div class="card tilt-card" data-tilt>
<canvas class="card-icon zdog-icon-1" width="120" height="120"></canvas>
<h2>Vanta.js</h2>
<p>Animated WebGL backgrounds powered by Three.js for immersive visual experiences.</p>
<button class="card-btn">Learn More</button>
</div>
<div class="card tilt-card" data-tilt>
<canvas class="card-icon zdog-icon-2" width="120" height="120"></canvas>
<h2>Zdog</h2>
<p>Pseudo-3D engine for canvas and SVG. Create flat designs with dimensional depth.</p>
<button class="card-btn">Learn More</button>
</div>
<div class="card tilt-card" data-tilt>
<canvas class="card-icon zdog-icon-3" width="120" height="120"></canvas>
<h2>Vanilla-Tilt</h2>
<p>Smooth 3D tilt effect for any DOM element. Perfect for interactive cards.</p>
<button class="card-btn">Learn More</button>
</div>
</section>
<!-- Interactive Demo -->
<section class="demo-section">
<div class="demo-card tilt-card" data-tilt>
<canvas class="demo-zdog" width="300" height="300"></canvas>
<div class="demo-controls">
<h3>Interactive Demo</h3>
<p>Hover and drag to interact</p>
<div class="control-buttons">
<button id="toggleSpin">Pause</button>
<button id="changeColor">Change Color</button>
<button id="resetView">Reset</button>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer>
<p>Built with ❤️ using Vanta.js, Zdog, and Vanilla-Tilt</p>
<div class="footer-links">
<a href="https://www.vantajs.com" target="_blank">Vanta.js</a>
<a href="https://zzz.dog" target="_blank">Zdog</a>
<a href="https://github.com/micku7zu/vanilla-tilt.js" target="_blank">Vanilla-Tilt</a>
</div>
</footer>
</div>
<!-- Scripts -->
<!-- Three.js (required for Vanta) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<!-- Vanta.js WAVES effect -->
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.waves.min.js"></script>
<!-- Zdog -->
<script src="https://unpkg.com/zdog@1/dist/zdog.dist.min.js"></script>
<!-- Vanilla Tilt -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/vanilla-tilt/1.8.1/vanilla-tilt.min.js"></script>
<!-- Main JavaScript -->
<script src="main.js"></script>
</body>
</html>
// Lightweight 3D Effects - Starter Template
// Combining Vanta.js, Zdog, and Vanilla-Tilt
console.log('Lightweight 3D Effects initialized');
// ============================================================================
// VANTA.JS BACKGROUND
// ============================================================================
let vantaEffect = VANTA.WAVES({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
color: 0x23153c,
shininess: 30.00,
waveHeight: 15.00,
waveSpeed: 0.75,
zoom: 0.65
});
console.log('Vanta.js background initialized');
// ============================================================================
// ZDOG ILLUSTRATIONS
// ============================================================================
// Hero Zdog Icon (Rotating Cube)
const heroIllo = new Zdog.Illustration({
element: '.hero-zdog',
zoom: 2,
dragRotate: false
});
const heroCube = new Zdog.Box({
addTo: heroIllo,
width: 40,
height: 40,
depth: 40,
stroke: 2,
color: '#fff',
fill: true,
frontFace: '#667eea',
rearFace: '#764ba2',
leftFace: '#5568d3',
rightFace: '#8568df',
topFace: '#98d5e8',
bottomFace: '#f093fb'
});
// Animate hero cube
function animateHeroCube() {
heroCube.rotate.x += 0.01;
heroCube.rotate.y += 0.02;
heroIllo.updateRenderGraph();
requestAnimationFrame(animateHeroCube);
}
animateHeroCube();
// ============================================================================
// Card Icon 1 - Vanta (Globe)
const icon1 = new Zdog.Illustration({
element: '.zdog-icon-1',
zoom: 2,
dragRotate: false
});
// Globe with particles
const globe1 = new Zdog.Anchor({
addTo: icon1
});
new Zdog.Ellipse({
addTo: globe1,
diameter: 40,
stroke: 2,
color: '#667eea',
});
new Zdog.Ellipse({
addTo: globe1,
diameter: 40,
rotate: { y: Math.PI/2 },
stroke: 2,
color: '#764ba2',
});
// Particles around globe
for (let i = 0; i < 8; i++) {
const angle = (i / 8) * Math.PI * 2;
new Zdog.Ellipse({
addTo: globe1,
diameter: 4,
translate: {
x: Math.cos(angle) * 30,
y: Math.sin(angle) * 30
},
stroke: 2,
color: '#fff',
fill: true
});
}
function animateIcon1() {
globe1.rotate.y += 0.03;
icon1.updateRenderGraph();
requestAnimationFrame(animateIcon1);
}
animateIcon1();
// ============================================================================
// Card Icon 2 - Zdog (3D Z)
const icon2 = new Zdog.Illustration({
element: '.zdog-icon-2',
zoom: 3,
dragRotate: false
});
const zShape = new Zdog.Anchor({
addTo: icon2
});
// Draw "Z" with 3D depth
new Zdog.Shape({
addTo: zShape,
path: [
{ x: -15, y: -15 },
{ x: 15, y: -15 },
{ x: -15, y: 15 },
{ x: 15, y: 15 }
],
stroke: 8,
color: '#667eea'
});
// Add depth layers
for (let i = 0; i < 5; i++) {
new Zdog.Shape({
addTo: zShape,
path: [
{ x: -15, y: -15, z: -i*2 },
{ x: 15, y: -15, z: -i*2 },
{ x: -15, y: 15, z: -i*2 },
{ x: 15, y: 15, z: -i*2 }
],
stroke: 8,
color: `hsl(${240 + i*10}, 70%, ${60 - i*5}%)`
});
}
function animateIcon2() {
zShape.rotate.y += 0.02;
zShape.rotate.x += 0.01;
icon2.updateRenderGraph();
requestAnimationFrame(animateIcon2);
}
animateIcon2();
// ============================================================================
// Card Icon 3 - Vanilla Tilt (Tilted Square)
const icon3 = new Zdog.Illustration({
element: '.zdog-icon-3',
zoom: 2,
dragRotate: false
});
const tiltSquare = new Zdog.Anchor({
addTo: icon3,
rotate: { x: Math.PI/6, y: Math.PI/6 }
});
// Layered squares
for (let i = 0; i < 3; i++) {
new Zdog.Rect({
addTo: tiltSquare,
width: 35 - i*5,
height: 35 - i*5,
translate: { z: i*10 },
stroke: 3,
color: `hsl(${240 + i*30}, 70%, 60%)`,
fill: true
});
}
// Subtle rotation
function animateIcon3() {
tiltSquare.rotate.y += 0.01;
icon3.updateRenderGraph();
requestAnimationFrame(animateIcon3);
}
animateIcon3();
// ============================================================================
// Demo Zdog (Interactive)
const demoIllo = new Zdog.Illustration({
element: '.demo-zdog',
zoom: 2,
dragRotate: true,
onDragStart: function() {
isSpinning = false;
}
});
let isSpinning = true;
let currentColor = '#667eea';
// Create complex 3D model
const demoModel = new Zdog.Anchor({
addTo: demoIllo
});
// Center sphere
const centerSphere = new Zdog.Hemisphere({
addTo: demoModel,
diameter: 40,
stroke: 5,
color: currentColor,
fill: true
});
// Orbiting elements
const orbitRadius = 60;
const orbitCount = 6;
const orbitingShapes = [];
for (let i = 0; i < orbitCount; i++) {
const angle = (i / orbitCount) * Math.PI * 2;
const orbitAnchor = new Zdog.Anchor({
addTo: demoModel,
rotate: { y: angle }
});
const shape = new Zdog.Box({
addTo: orbitAnchor,
width: 15,
height: 15,
depth: 15,
translate: { z: orbitRadius },
stroke: 2,
color: `hsl(${(i / orbitCount) * 360}, 70%, 60%)`,
fill: true
});
orbitingShapes.push({ anchor: orbitAnchor, shape: shape, angle: angle });
}
// Animation
let time = 0;
function animateDemo() {
time += 0.02;
if (isSpinning) {
demoModel.rotate.y += 0.02;
}
// Animate orbiting shapes
orbitingShapes.forEach((item, i) => {
const wave = Math.sin(time + item.angle) * 10;
item.shape.translate.z = orbitRadius + wave;
item.shape.rotate.x += 0.03;
item.shape.rotate.y += 0.02;
});
demoIllo.updateRenderGraph();
requestAnimationFrame(animateDemo);
}
animateDemo();
// ============================================================================
// VANILLA TILT
// ============================================================================
VanillaTilt.init(document.querySelectorAll(".tilt-card"), {
max: 15,
speed: 400,
glare: true,
"max-glare": 0.3,
scale: 1.03,
perspective: 1000
});
console.log('Vanilla Tilt initialized');
// ============================================================================
// DEMO CONTROLS
// ============================================================================
document.getElementById('toggleSpin').addEventListener('click', function() {
isSpinning = !isSpinning;
this.textContent = isSpinning ? 'Pause' : 'Play';
});
document.getElementById('changeColor').addEventListener('click', function() {
// Generate random color
const hue = Math.floor(Math.random() * 360);
currentColor = `hsl(${hue}, 70%, 60%)`;
centerSphere.color = currentColor;
// Randomize orbiting shapes
orbitingShapes.forEach(item => {
const randomHue = Math.floor(Math.random() * 360);
item.shape.color = `hsl(${randomHue}, 70%, 60%)`;
});
});
document.getElementById('resetView').addEventListener('click', function() {
demoModel.rotate.x = 0;
demoModel.rotate.y = 0;
demoModel.rotate.z = 0;
isSpinning = true;
document.getElementById('toggleSpin').textContent = 'Pause';
});
// ============================================================================
// CARD BUTTON INTERACTIONS
// ============================================================================
document.querySelectorAll('.card-btn').forEach(btn => {
btn.addEventListener('click', function(e) {
e.stopPropagation(); // Prevent tilt effect from interfering
const card = this.closest('.card');
const cardTitle = card.querySelector('h2').textContent;
console.log(`Button clicked: ${cardTitle}`);
// Add visual feedback
this.style.transform = 'translateZ(40px) scale(0.95)';
setTimeout(() => {
this.style.transform = 'translateZ(40px) scale(1)';
}, 100);
// You can add navigation or modal logic here
alert(`Learn more about ${cardTitle}!`);
});
});
// ============================================================================
// RESPONSIVE HANDLING
// ============================================================================
// Disable tilt on mobile for better performance
if (window.innerWidth < 768) {
document.querySelectorAll('.tilt-card').forEach(card => {
if (card.vanillaTilt) {
card.vanillaTilt.destroy();
}
});
console.log('Tilt disabled on mobile');
}
// Resize handler for Vanta
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
if (vantaEffect && vantaEffect.resize) {
vantaEffect.resize();
}
}, 250);
});
// ============================================================================
// CLEANUP ON PAGE UNLOAD
// ============================================================================
window.addEventListener('beforeunload', () => {
if (vantaEffect && vantaEffect.destroy) {
vantaEffect.destroy();
}
document.querySelectorAll('.tilt-card').forEach(card => {
if (card.vanillaTilt) {
card.vanillaTilt.destroy();
}
});
console.log('Effects cleaned up');
});
// ============================================================================
// PERFORMANCE MONITORING
// ============================================================================
if (typeof performance !== 'undefined' && performance.now) {
const startTime = performance.now();
window.addEventListener('load', () => {
const loadTime = performance.now() - startTime;
console.log(`Page loaded in ${loadTime.toFixed(2)}ms`);
});
}
console.log('All effects initialized successfully');
Lightweight 3D Effects - Starter Template
Production-ready template combining Vanta.js, Zdog, and Vanilla-Tilt for stunning 3D effects with minimal overhead.
---
Features
- 🌊 Vanta.js Background - Animated WebGL waves effect
- 🎨 Zdog Illustrations - Multiple pseudo-3D graphics
- 🎯 Vanilla Tilt Cards - Interactive 3D tilt effects with glare
- 📱 Responsive Design - Mobile-optimized with performance considerations
- ♿ Accessible - Semantic HTML and proper contrast
- 🚀 Fast Loading - CDN-based, optimized assets
---
Quick Start
1. View Locally
Simply open index.html in a modern web browser. All dependencies are loaded via CDN.
# Option 1: Direct open
open index.html
# Option 2: Local server (recommended)
python -m http.server 8000
# Visit http://localhost:80002. Customize Colors
Edit main.js to change effect colors:
// Vanta.js waves color
let vantaEffect = VANTA.WAVES({
el: "#vanta-bg",
color: 0x23153c, // Change this hex number
waveHeight: 15.00,
waveSpeed: 0.75
});
// Zdog colors
const heroCube = new Zdog.Box({
frontFace: '#667eea', // Change face colors
rearFace: '#764ba2'
});3. Change Vanta Effect
Replace WAVES with any other Vanta effect:
// Change from WAVES to BIRDS
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.birds.min.js"></script>
let vantaEffect = VANTA.BIRDS({
el: "#vanta-bg",
backgroundColor: 0x23153c,
color1: 0xff0090,
quantity: 5
});Available effects: WAVES, CLOUDS, BIRDS, NET, CELLS, FOG, GLOBE, RINGS, DOTS, TOPOLOGY, TRUNK
---
Project Structure
starter_lightweight/
├── index.html # Main HTML structure
├── style.css # Styling and responsive design
├── main.js # All effects initialization
└── README.md # This file---
What's Included
Vanta.js Background
Type: WAVES effect Purpose: Immersive animated background Performance: ~15-20ms per frame on desktop
// Located in main.js
let vantaEffect = VANTA.WAVES({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
color: 0x23153c,
waveHeight: 15.00,
zoom: 0.65
});Customization Options:
color: Wave color (hex number)waveHeight: Wave amplitude (0-30)waveSpeed: Animation speed (0-2)shininess: Surface reflectivity (0-100)
---
Zdog Illustrations
5 Different Illustrations:
1. Hero Cube (.hero-zdog)
- Rotating 3D cube with colored faces
- Auto-animation on X and Y axes
2. Card Icon 1 - Globe (.zdog-icon-1)
- Orbiting globe with particles
- Represents Vanta.js
3. Card Icon 2 - 3D Z (.zdog-icon-2)
- Layered "Z" letter with depth
- Represents Zdog
4. Card Icon 3 - Tilted Square (.zdog-icon-3)
- Stacked squares with parallax
- Represents Vanilla-Tilt
5. Demo Model (.demo-zdog)
- Interactive sphere with orbiting boxes
- Drag to rotate
- Control buttons for color/spin
How to Add More:
const newIllo = new Zdog.Illustration({
element: '.your-canvas-class',
zoom: 2
});
new Zdog.Ellipse({
addTo: newIllo,
diameter: 40,
stroke: 5,
color: '#667eea'
});
function animate() {
newIllo.rotate.y += 0.03;
newIllo.updateRenderGraph();
requestAnimationFrame(animate);
}
animate();---
Vanilla Tilt Cards
Applied to: .tilt-card elements Features:
- 15° max tilt
- Glare effect at 30% opacity
- 3% scale on hover
- 1000px perspective
Configuration (in main.js):
VanillaTilt.init(document.querySelectorAll(".tilt-card"), {
max: 15, // Max tilt angle
speed: 400, // Transition speed (ms)
glare: true, // Enable glare
"max-glare": 0.3, // Max glare opacity
scale: 1.03, // Scale multiplier
perspective: 1000 // CSS perspective
});3D Transform Layers:
.card-icon {
transform: translateZ(30px);
}
.card h2 {
transform: translateZ(20px);
}
.card-btn {
transform: translateZ(40px);
}This creates depth when tilting.
---
Customization Guide
Change Hero Title
Edit index.html:
<h1>Lightweight 3D Effects</h1>
<!-- Change to: -->
<h1>Your Custom Title</h1>Modify Card Content
Edit index.html card sections:
<div class="card tilt-card" data-tilt>
<canvas class="card-icon zdog-icon-1" width="120" height="120"></canvas>
<h2>Your Title</h2>
<p>Your description text here.</p>
<button class="card-btn">Learn More</button>
</div>Add New Zdog Icon
1. Add canvas to HTML:
<canvas class="zdog-icon-4" width="120" height="120"></canvas>2. Create illustration in main.js:
const icon4 = new Zdog.Illustration({
element: '.zdog-icon-4',
zoom: 2
});
new Zdog.Polygon({
addTo: icon4,
sides: 6,
radius: 20,
stroke: 3,
color: '#667eea',
fill: true
});
function animateIcon4() {
icon4.rotate.z += 0.02;
icon4.updateRenderGraph();
requestAnimationFrame(animateIcon4);
}
animateIcon4();Change Tilt Settings per Card
Instead of global init, initialize individually:
VanillaTilt.init(document.querySelector(".card-1"), {
max: 25,
glare: true
});
VanillaTilt.init(document.querySelector(".card-2"), {
max: 10,
glare: false
});---
Performance Optimization
Mobile Optimization
Automatic optimizations included:
1. Tilt disabled on mobile (< 768px width)
if (window.innerWidth < 768) {
document.querySelectorAll('.tilt-card').forEach(card => {
if (card.vanillaTilt) {
card.vanillaTilt.destroy();
}
});
}2. Vanta mobile scale
scaleMobile: 1.00 // Adjust to 0.5 for better performance3. Responsive CSS
@media (max-width: 768px) {
.hero h1 {
font-size: 2.5rem;
}
}Further Optimizations
1. Reduce Vanta complexity
VANTA.WAVES({
el: "#vanta-bg",
points: 5, // Lower = faster (default: 10)
maxDistance: 15, // Lower = faster (default: 20)
waveHeight: 10 // Lower = faster (default: 15)
});2. Limit Zdog animations
// Only animate when visible
let isVisible = true;
let observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
isVisible = entry.isIntersecting;
});
});
observer.observe(document.querySelector('.hero-zdog'));
function animate() {
if (isVisible) {
heroCube.rotate.x += 0.01;
heroIllo.updateRenderGraph();
}
requestAnimationFrame(animate);
}3. Pause Vanta when not visible
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
if (vantaEffect) vantaEffect.destroy();
} else {
vantaEffect = VANTA.WAVES({ el: "#vanta-bg" });
}
});---
Browser Support
| Browser | Vanta.js | Zdog | Vanilla-Tilt |
|---|---|---|---|
| Chrome | ✅ | ✅ | ✅ |
| Firefox | ✅ | ✅ | ✅ |
| Safari | ✅ | ✅ | ✅ |
| Edge | ✅ | ✅ | ✅ |
| IE11 | ❌ | ✅ | ✅ |
Notes:
- Vanta.js requires WebGL support
- All effects gracefully degrade if unavailable
- Mobile gyroscope requires HTTPS
---
Common Issues
Vanta Not Loading
Problem: Background stays gradient, no animation
Solutions: 1. Check browser console for errors 2. Ensure Three.js loads before Vanta 3. Verify WebGL is enabled:
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
console.log('WebGL:', gl ? 'Enabled' : 'Disabled');Tilt Not Working
Problem: Cards don't tilt on hover
Solutions: 1. Ensure data-tilt attribute exists 2. Check if Vanilla-Tilt script loaded 3. Verify card has dimensions:
.card {
width: 300px;
height: 400px;
}Zdog Canvas Blank
Problem: Canvas shows nothing
Solutions: 1. Verify canvas dimensions are set 2. Check if illustration is updating:
console.log('Illustration:', illo);
illo.updateRenderGraph(); // Force render3. Ensure shapes have stroke or fill
---
Deployment
CDN Dependencies
All dependencies load from CDN (no build step required):
- Three.js:
https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js - Vanta.js:
https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.waves.min.js - Zdog:
https://unpkg.com/zdog@1/dist/zdog.dist.min.js - Vanilla-Tilt:
https://cdnjs.cloudflare.com/ajax/libs/vanilla-tilt/1.8.1/vanilla-tilt.min.js
Hosting
Static Hosting (recommended):
- Netlify: Drag & drop folder
- Vercel:
vercel deploy - GitHub Pages: Push to
gh-pagesbranch - Cloudflare Pages: Connect repository
Example Netlify Deploy:
# Install Netlify CLI
npm install -g netlify-cli
# Deploy
netlify deploy --prod --dir=.Build for Production (Optional)
For bundling with your app:
npm install three vanta zdog vanilla-tiltimport * as THREE from 'three';
import WAVES from 'vanta/dist/vanta.waves.min.js';
import Zdog from 'zdog';
import VanillaTilt from 'vanilla-tilt';
// Use as before---
Advanced Examples
Dynamic Effect Switching
let effects = ['WAVES', 'BIRDS', 'NET'];
let currentEffect = 0;
document.getElementById('switchEffect').addEventListener('click', () => {
vantaEffect.destroy();
currentEffect = (currentEffect + 1) % effects.length;
const effect = effects[currentEffect];
if (effect === 'WAVES') {
vantaEffect = VANTA.WAVES({ el: "#vanta-bg" });
} else if (effect === 'BIRDS') {
vantaEffect = VANTA.BIRDS({ el: "#vanta-bg" });
} else {
vantaEffect = VANTA.NET({ el: "#vanta-bg" });
}
});Synchronized Animations
let masterTime = 0;
function animateAll() {
masterTime += 0.02;
// Sync hero cube
heroCube.rotate.y = Math.sin(masterTime) * Math.PI;
// Sync demo model
demoModel.rotate.y = masterTime;
// Update all
heroIllo.updateRenderGraph();
demoIllo.updateRenderGraph();
requestAnimationFrame(animateAll);
}
animateAll();Scroll-Based Effects
window.addEventListener('scroll', () => {
const scrollPercent = window.scrollY / (document.body.scrollHeight - window.innerHeight);
// Rotate demo model based on scroll
demoModel.rotate.y = scrollPercent * Math.PI * 2;
demoIllo.updateRenderGraph();
// Change Vanta color
const hue = Math.floor(scrollPercent * 360);
const color = parseInt(`0x${hue.toString(16).padStart(6, '0')}`);
vantaEffect.setOptions({ color });
});---
Resources
Documentation
- Vanta.js: https://www.vantajs.com
- Zdog: https://zzz.dog
- Vanilla-Tilt: https://github.com/micku7zu/vanilla-tilt.js
Inspiration
- Vanta Gallery: https://www.vantajs.com/?effect=waves
- Zdog Examples: https://codepen.io/desandro/
- Tilt Patterns: https://micku7zu.github.io/vanilla-tilt.js/
---
License
This template is MIT licensed - free for personal and commercial use.
Dependencies:
- Three.js: MIT
- Vanta.js: MIT
- Zdog: MIT
- Vanilla-Tilt: MIT
---
Support
For issues or questions: 1. Check browser console for errors 2. Review the Common Issues section 3. Consult official library documentation 4. Test in a different browser
---
Built with ❤️ using lightweight 3D effects libraries
/* Reset and Base Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
overflow-x: hidden;
color: white;
}
/* Vanta Background */
#vanta-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
z-index: -1;
}
/* Container */
.container {
position: relative;
z-index: 1;
max-width: 1400px;
margin: 0 auto;
padding: 2rem;
}
/* Hero Section */
.hero {
text-align: center;
padding: 4rem 2rem;
min-height: 60vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.hero-zdog {
display: block;
margin: 0 auto 2rem;
filter: drop-shadow(0 10px 30px rgba(0,0,0,0.3));
}
.hero h1 {
font-size: 4rem;
font-weight: 700;
margin-bottom: 1rem;
text-shadow: 0 4px 20px rgba(0,0,0,0.3);
background: linear-gradient(135deg, #fff 0%, #e0e0ff 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.subtitle {
font-size: 1.5rem;
opacity: 0.9;
text-shadow: 0 2px 10px rgba(0,0,0,0.3);
margin-bottom: 2rem;
}
/* Feature Cards */
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin: 4rem 0;
padding: 0 1rem;
}
.card {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 2.5rem;
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
transition: all 0.3s ease;
transform-style: preserve-3d;
text-align: center;
}
.card:hover {
box-shadow: 0 30px 80px rgba(0,0,0,0.4);
}
.card-icon {
display: block;
margin: 0 auto 1.5rem;
transform: translateZ(30px);
}
.card h2 {
font-size: 2rem;
margin-bottom: 1rem;
transform: translateZ(20px);
}
.card p {
font-size: 1.1rem;
line-height: 1.6;
opacity: 0.9;
margin-bottom: 1.5rem;
transform: translateZ(15px);
}
.card-btn {
padding: 0.75rem 2rem;
font-size: 1rem;
background: rgba(255, 255, 255, 0.2);
color: white;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 50px;
cursor: pointer;
transition: all 0.3s;
font-weight: 600;
transform: translateZ(40px);
}
.card-btn:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateZ(40px) translateY(-2px);
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
}
/* Demo Section */
.demo-section {
margin: 6rem 0;
display: flex;
justify-content: center;
}
.demo-card {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 30px;
padding: 3rem;
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 30px 80px rgba(0,0,0,0.3);
max-width: 600px;
width: 100%;
transform-style: preserve-3d;
}
.demo-zdog {
display: block;
margin: 0 auto 2rem;
cursor: grab;
transform: translateZ(40px);
}
.demo-zdog:active {
cursor: grabbing;
}
.demo-controls {
text-align: center;
transform: translateZ(30px);
}
.demo-controls h3 {
font-size: 2rem;
margin-bottom: 0.5rem;
}
.demo-controls p {
opacity: 0.8;
margin-bottom: 1.5rem;
}
.control-buttons {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
}
.control-buttons button {
padding: 0.75rem 1.5rem;
font-size: 1rem;
background: rgba(255, 255, 255, 0.2);
color: white;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 50px;
cursor: pointer;
transition: all 0.3s;
font-weight: 600;
}
.control-buttons button:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.control-buttons button:active {
transform: translateY(0);
}
/* Footer */
footer {
text-align: center;
padding: 3rem 2rem;
margin-top: 4rem;
border-top: 1px solid rgba(255, 255, 255, 0.2);
}
footer p {
font-size: 1.2rem;
margin-bottom: 1rem;
opacity: 0.9;
}
.footer-links {
display: flex;
gap: 2rem;
justify-content: center;
flex-wrap: wrap;
}
.footer-links a {
color: white;
text-decoration: none;
opacity: 0.8;
transition: opacity 0.3s;
font-size: 1rem;
}
.footer-links a:hover {
opacity: 1;
}
/* Responsive Design */
@media (max-width: 768px) {
.hero h1 {
font-size: 2.5rem;
}
.subtitle {
font-size: 1.2rem;
}
.cards {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.demo-card {
padding: 2rem;
}
.demo-zdog {
width: 250px;
height: 250px;
}
.footer-links {
flex-direction: column;
gap: 1rem;
}
}
/* Glare Effect (from Vanilla Tilt) */
.js-tilt-glare {
border-radius: 20px;
}
/* Loading State */
body.loading #vanta-bg {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
Vanilla-Tilt.js Patterns Reference
Complete reference for Vanilla-Tilt.js - smooth 3D tilt effect for any DOM element.
Version: 1.8.1 License: MIT GitHub: https://github.com/micku7zu/vanilla-tilt.js
---
Table of Contents
1. Getting Started 2. Configuration Options 3. Methods 4. Events 5. Common Patterns 6. Advanced Techniques 7. Framework Integration 8. Performance
---
Getting Started
CDN Installation
<script src="https://cdnjs.cloudflare.com/ajax/libs/vanilla-tilt/1.8.1/vanilla-tilt.min.js"></script>
<div class="tilt-card" data-tilt></div>
<script>
VanillaTilt.init(document.querySelector(".tilt-card"));
</script>---
NPM Installation
npm install vanilla-tiltimport VanillaTilt from 'vanilla-tilt';
VanillaTilt.init(document.querySelector(".tilt-card"));---
Basic Usage
HTML Attribute (No JavaScript):
<div class="tilt-card" data-tilt></div>JavaScript Initialization:
<div class="tilt-card"></div>
<script>
VanillaTilt.init(document.querySelector(".tilt-card"), {
max: 25,
speed: 400,
glare: true,
"max-glare": 0.5
});
</script>Multiple Elements:
VanillaTilt.init(document.querySelectorAll(".tilt-card"));---
Configuration Options
Complete Options Reference
VanillaTilt.init(element, {
// Rotation
reverse: false, // Reverse tilt direction
max: 15, // Max tilt angle (degrees)
startX: 0, // Starting tilt on X axis (degrees)
startY: 0, // Starting tilt on Y axis (degrees)
perspective: 1000, // Transform perspective (px)
// Scale
scale: 1.0, // Scale on hover (2 = 200%)
// Speed & Easing
speed: 300, // Transition speed (ms)
transition: true, // Enable/disable transition
easing: "cubic-bezier(.03,.98,.52,.99)", // CSS easing
// Axis Control
axis: null, // Restrict axis ("x" or "y")
reset: true, // Reset on mouseout
"reset-to-start": true, // Reset to startX/startY values
// Glare Effect
glare: false, // Enable glare effect
"max-glare": 1, // Max glare opacity (0-1)
"glare-prerender": false, // Pre-render glare element
// Mouse/Touch
"mouse-event-element": null, // Element for mouse detection
"full-page-listening": false, // Listen to mouse on entire page
gyroscope: true, // Enable gyroscope (mobile)
gyroscopeMinAngleX: -45, // Min gyro angle X
gyroscopeMaxAngleX: 45, // Max gyro angle X
gyroscopeMinAngleY: -45, // Min gyro angle Y
gyroscopeMaxAngleY: 45 // Max gyro angle Y
});---
Configuration Options Details
Rotation Options
max
Maximum tilt angle in degrees.
// Subtle tilt
VanillaTilt.init(element, { max: 10 });
// Dramatic tilt
VanillaTilt.init(element, { max: 35 });
// Extreme tilt
VanillaTilt.init(element, { max: 50 });Recommended: 15-25 for most use cases
---
reverse
Reverse the tilt direction.
VanillaTilt.init(element, {
reverse: true // Tilts opposite to mouse movement
});Use case: Parallax layers moving in opposite directions
---
startX / startY
Initial tilt angle (degrees).
VanillaTilt.init(element, {
startX: 10, // Start tilted 10° on X axis
startY: -5 // Start tilted -5° on Y axis
});---
perspective
CSS perspective value (pixels).
// Subtle 3D
VanillaTilt.init(element, { perspective: 2000 });
// Strong 3D
VanillaTilt.init(element, { perspective: 500 });Lower values = stronger 3D effect
---
Scale Options
scale
Element scale on hover (1.0 = 100%).
// Subtle zoom
VanillaTilt.init(element, { scale: 1.05 });
// Noticeable zoom
VanillaTilt.init(element, { scale: 1.2 });
// Dramatic zoom
VanillaTilt.init(element, { scale: 1.5 });---
Speed & Easing
speed
Transition duration in milliseconds.
// Fast (snappy)
VanillaTilt.init(element, { speed: 200 });
// Smooth (recommended)
VanillaTilt.init(element, { speed: 400 });
// Slow (fluid)
VanillaTilt.init(element, { speed: 800 });---
easing
CSS easing function for transitions.
// Linear
VanillaTilt.init(element, {
easing: "linear"
});
// Ease out (recommended)
VanillaTilt.init(element, {
easing: "cubic-bezier(.03,.98,.52,.99)"
});
// Bounce
VanillaTilt.init(element, {
easing: "cubic-bezier(.68,-0.55,.265,1.55)"
});
// Elastic
VanillaTilt.init(element, {
easing: "cubic-bezier(.6,.04,.98,.335)"
});---
transition
Enable/disable CSS transitions.
// Disable for instant response
VanillaTilt.init(element, { transition: false });---
Axis Control
axis
Restrict tilt to one axis.
// Horizontal tilt only
VanillaTilt.init(element, { axis: "x" });
// Vertical tilt only
VanillaTilt.init(element, { axis: "y" });
// Both axes (default)
VanillaTilt.init(element, { axis: null });---
reset
Reset tilt when mouse leaves element.
// Keep tilt on mouseout
VanillaTilt.init(element, { reset: false });
// Reset on mouseout (default)
VanillaTilt.init(element, { reset: true });---
reset-to-start
Reset to startX/startY values instead of 0.
VanillaTilt.init(element, {
startX: 10,
startY: -5,
"reset-to-start": true // Resets to (10, -5) not (0, 0)
});---
Glare Effect
glare
Enable glossy glare overlay.
VanillaTilt.init(element, {
glare: true,
"max-glare": 0.5 // 50% max opacity
});Note: Automatically adds a .js-tilt-glare element inside the tilt element.
---
max-glare
Maximum glare opacity (0 to 1).
// Subtle glare
VanillaTilt.init(element, {
glare: true,
"max-glare": 0.2
});
// Strong glare
VanillaTilt.init(element, {
glare: true,
"max-glare": 0.8
});---
glare-prerender
Pre-render glare element in HTML (performance optimization).
<div class="tilt-card" data-tilt>
<div class="js-tilt-glare">
<div class="js-tilt-glare-inner"></div>
</div>
<!-- Your content -->
</div>VanillaTilt.init(element, {
glare: true,
"glare-prerender": true
});---
Mouse/Touch Options
mouse-event-element
Use different element for mouse tracking.
VanillaTilt.init(element, {
"mouse-event-element": document.querySelector(".parent")
});Use case: Tilt element based on mouse position over larger container.
---
full-page-listening
Track mouse position across entire page.
VanillaTilt.init(element, {
"full-page-listening": true
});Use case: Parallax effects that respond to page-wide mouse movement.
---
Gyroscope Options
gyroscope
Enable device orientation (mobile).
VanillaTilt.init(element, {
gyroscope: true,
gyroscopeMinAngleX: -45,
gyroscopeMaxAngleX: 45,
gyroscopeMinAngleY: -45,
gyroscopeMaxAngleY: 45
});Note: Requires HTTPS and user permission on iOS.
---
Methods
destroy()
Remove tilt effect and event listeners.
const element = document.querySelector(".tilt-card");
VanillaTilt.init(element);
// Later...
element.vanillaTilt.destroy();Important: Always call destroy() when removing elements to prevent memory leaks.
---
reset()
Reset tilt to default position.
element.vanillaTilt.reset();---
getValues()
Get current tilt values.
const values = element.vanillaTilt.getValues();
console.log(values);
// {
// tiltX: 10.5,
// tiltY: -5.2,
// percentageX: 52.5,
// percentageY: 47.8,
// angle: 11.7
// }Return values:
tiltX: Current X-axis tilt (degrees)tiltY: Current Y-axis tilt (degrees)percentageX: Mouse X position as percentage (0-100)percentageY: Mouse Y position as percentage (0-100)angle: Total tilt angle (degrees)
---
setOptions(options)
Update options after initialization.
element.vanillaTilt.setOptions({
max: 35,
speed: 600
});---
Events
tiltChange
Fired when tilt changes.
element.addEventListener("tiltChange", (e) => {
console.log("Tilt changed:", e.detail);
// {
// tiltX: 10.5,
// tiltY: -5.2,
// percentageX: 52.5,
// percentageY: 47.8,
// angle: 11.7
// }
});---
mouseLeave
Fired when mouse leaves element.
element.addEventListener("mouseLeave", () => {
console.log("Mouse left tilt element");
});---
mouseEnter
Fired when mouse enters element.
element.addEventListener("mouseEnter", () => {
console.log("Mouse entered tilt element");
});---
Common Patterns
Pattern 1: Card Gallery
Tilting card grid with glare.
<div class="card-grid">
<div class="tilt-card" data-tilt>
<img src="image1.jpg" alt="Card 1">
<h3>Card Title</h3>
</div>
<div class="tilt-card" data-tilt>
<img src="image2.jpg" alt="Card 2">
<h3>Card Title</h3>
</div>
<!-- More cards... -->
</div>.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
padding: 2rem;
}
.tilt-card {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
transform-style: preserve-3d;
}
.tilt-card img {
width: 100%;
display: block;
transform: translateZ(20px);
}
.tilt-card h3 {
padding: 1rem;
transform: translateZ(40px);
}VanillaTilt.init(document.querySelectorAll(".tilt-card"), {
max: 15,
speed: 400,
glare: true,
"max-glare": 0.3,
scale: 1.05
});---
Pattern 2: Parallax Layers
Multiple layered elements with different tilt depths.
<div class="parallax-container" data-tilt>
<div class="layer layer-1">Background</div>
<div class="layer layer-2">Midground</div>
<div class="layer layer-3">Foreground</div>
</div>.parallax-container {
position: relative;
width: 400px;
height: 400px;
transform-style: preserve-3d;
}
.layer {
position: absolute;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
}
.layer-1 {
background: #667eea;
transform: translateZ(-50px);
}
.layer-2 {
background: #764ba2;
opacity: 0.8;
transform: translateZ(0px);
}
.layer-3 {
background: #f093fb;
opacity: 0.6;
transform: translateZ(50px);
}VanillaTilt.init(document.querySelector(".parallax-container"), {
max: 25,
speed: 400,
perspective: 1000
});---
Pattern 3: Hover Reveal
Reveal content on tilt hover.
<div class="reveal-card" data-tilt>
<div class="card-front">
<h2>Hover Me</h2>
</div>
<div class="card-back">
<p>Hidden content revealed!</p>
</div>
</div>.reveal-card {
width: 300px;
height: 400px;
position: relative;
transform-style: preserve-3d;
}
.card-front,
.card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
}
.card-front {
background: #667eea;
transform: translateZ(20px);
}
.card-back {
background: #764ba2;
transform: translateZ(-20px) rotateY(180deg);
opacity: 0;
transition: opacity 0.3s;
}
.reveal-card:hover .card-back {
opacity: 1;
}VanillaTilt.init(document.querySelector(".reveal-card"), {
max: 25,
speed: 400,
glare: true
});---
Pattern 4: 3D Button
Elevated button with shadow that follows tilt.
<button class="tilt-button" data-tilt>
<span>Click Me</span>
</button>.tilt-button {
padding: 1.5rem 3rem;
font-size: 1.2rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 50px;
cursor: pointer;
transform-style: preserve-3d;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
transition: box-shadow 0.3s;
}
.tilt-button span {
display: block;
transform: translateZ(20px);
}
.tilt-button:hover {
box-shadow: 0 15px 40px rgba(0,0,0,0.4);
}VanillaTilt.init(document.querySelector(".tilt-button"), {
max: 20,
speed: 300,
scale: 1.1,
glare: true,
"max-glare": 0.5
});---
Pattern 5: Product Showcase
E-commerce product card with layered depth.
<div class="product-card" data-tilt>
<div class="product-badge">NEW</div>
<img src="product.jpg" alt="Product" class="product-image">
<div class="product-info">
<h3 class="product-title">Product Name</h3>
<p class="product-price">$99.99</p>
<button class="product-button">Add to Cart</button>
</div>
</div>.product-card {
width: 320px;
background: white;
border-radius: 15px;
overflow: hidden;
transform-style: preserve-3d;
box-shadow: 0 20px 60px rgba(0,0,0,0.15);
}
.product-badge {
position: absolute;
top: 10px;
right: 10px;
background: #ff6b6b;
color: white;
padding: 0.5rem 1rem;
border-radius: 20px;
font-weight: bold;
transform: translateZ(60px);
z-index: 10;
}
.product-image {
width: 100%;
display: block;
transform: translateZ(30px);
}
.product-info {
padding: 1.5rem;
transform: translateZ(40px);
}
.product-button {
width: 100%;
padding: 0.75rem;
background: #667eea;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
transform: translateZ(50px);
}VanillaTilt.init(document.querySelector(".product-card"), {
max: 15,
speed: 400,
glare: true,
"max-glare": 0.2,
scale: 1.03
});---
Advanced Techniques
Dynamic Tilt Values
Update tilt based on external data.
const element = document.querySelector(".tilt-card");
VanillaTilt.init(element);
element.addEventListener("tiltChange", (e) => {
const { tiltX, tiltY, angle } = e.detail;
// Update background color based on tilt
const hue = Math.abs(tiltX * 10);
element.style.background = `hsl(${hue}, 70%, 50%)`;
// Update text based on angle
element.querySelector(".angle-display").textContent =
`Angle: ${angle.toFixed(1)}°`;
});---
Conditional Tilt
Enable tilt based on conditions.
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
if (!isMobile) {
VanillaTilt.init(document.querySelectorAll(".tilt-card"), {
max: 25,
speed: 400
});
} else {
// Enable gyroscope on mobile
VanillaTilt.init(document.querySelectorAll(".tilt-card"), {
max: 15,
speed: 600,
gyroscope: true
});
}---
Sync Multiple Elements
Tilt multiple elements together.
const cards = document.querySelectorAll(".sync-card");
// Initialize all cards
VanillaTilt.init(cards, {
max: 20,
speed: 400
});
// Sync tilt values
cards[0].addEventListener("tiltChange", (e) => {
const { tiltX, tiltY } = e.detail;
// Apply same tilt to other cards
cards.forEach((card, index) => {
if (index > 0) {
card.style.transform =
`perspective(1000px) rotateY(${tiltX}deg) rotateX(${-tiltY}deg)`;
}
});
});---
Tilt with Scroll
Combine tilt with scroll position.
const element = document.querySelector(".tilt-card");
VanillaTilt.init(element);
window.addEventListener("scroll", () => {
const scrollPercent = window.scrollY / (document.body.scrollHeight - window.innerHeight);
const rotation = scrollPercent * 360;
element.style.transform += ` rotate(${rotation}deg)`;
});---
Custom Glare Colors
Change glare color dynamically.
VanillaTilt.init(element, {
glare: true,
"max-glare": 0.5
});
element.addEventListener("tiltChange", (e) => {
const glareEl = element.querySelector(".js-tilt-glare-inner");
const { percentageX } = e.detail;
// Color shifts from blue to purple
const hue = 200 + (percentageX * 0.8);
glareEl.style.background =
`linear-gradient(0deg, transparent, hsl(${hue}, 70%, 50%))`;
});---
Framework Integration
React
import React, { useEffect, useRef } from 'react';
import VanillaTilt from 'vanilla-tilt';
function TiltCard({ children, options }) {
const tiltRef = useRef(null);
useEffect(() => {
VanillaTilt.init(tiltRef.current, options);
return () => {
tiltRef.current.vanillaTilt.destroy();
};
}, [options]);
return (
<div ref={tiltRef} className="tilt-card">
{children}
</div>
);
}
// Usage
<TiltCard options={{ max: 25, speed: 400, glare: true }}>
<h2>Card Content</h2>
</TiltCard>---
Vue 3
<template>
<div ref="tiltRef" class="tilt-card">
<slot></slot>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import VanillaTilt from 'vanilla-tilt';
const props = defineProps({
options: {
type: Object,
default: () => ({ max: 25, speed: 400 })
}
});
const tiltRef = ref(null);
onMounted(() => {
VanillaTilt.init(tiltRef.value, props.options);
});
onUnmounted(() => {
tiltRef.value.vanillaTilt.destroy();
});
</script>---
Angular
import { Component, ElementRef, Input, OnInit, OnDestroy } from '@angular/core';
import VanillaTilt from 'vanilla-tilt';
@Component({
selector: 'app-tilt-card',
template: `
<div class="tilt-card">
<ng-content></ng-content>
</div>
`
})
export class TiltCardComponent implements OnInit, OnDestroy {
@Input() options: any = { max: 25, speed: 400 };
constructor(private el: ElementRef) {}
ngOnInit() {
VanillaTilt.init(this.el.nativeElement.querySelector('.tilt-card'), this.options);
}
ngOnDestroy() {
this.el.nativeElement.querySelector('.tilt-card').vanillaTilt.destroy();
}
}---
Svelte
<script>
import { onMount, onDestroy } from 'svelte';
import VanillaTilt from 'vanilla-tilt';
export let options = { max: 25, speed: 400 };
let tiltRef;
onMount(() => {
VanillaTilt.init(tiltRef, options);
});
onDestroy(() => {
if (tiltRef && tiltRef.vanillaTilt) {
tiltRef.vanillaTilt.destroy();
}
});
</script>
<div bind:this={tiltRef} class="tilt-card">
<slot></slot>
</div>---
Performance
Optimization Tips
1. Disable on mobile for better performance
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
if (!isMobile) {
VanillaTilt.init(document.querySelectorAll(".tilt-card"));
}2. Use transform: translateZ for GPU acceleration
.tilt-card {
transform: translateZ(0); /* Force GPU layer */
}3. Limit tilt to visible elements
let observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
VanillaTilt.init(entry.target);
} else {
if (entry.target.vanillaTilt) {
entry.target.vanillaTilt.destroy();
}
}
});
});
document.querySelectorAll('.tilt-card').forEach(el => {
observer.observe(el);
});4. Reduce max angle for subtle effects
// Less intensive
VanillaTilt.init(element, { max: 10 });
// More intensive
VanillaTilt.init(element, { max: 50 });5. Disable glare on low-end devices
const hasGoodGPU = navigator.hardwareConcurrency > 4;
VanillaTilt.init(element, {
max: 25,
glare: hasGoodGPU,
"max-glare": hasGoodGPU ? 0.5 : 0
});---
Troubleshooting
Tilt Not Working
Check: 1. Element exists when initializing 2. Element has dimensions (width/height > 0) 3. No CSS conflicts with transform
// Wait for DOM
document.addEventListener('DOMContentLoaded', () => {
VanillaTilt.init(document.querySelector(".tilt-card"));
});---
Glare Not Showing
Solution: Ensure element has overflow: hidden or border-radius.
.tilt-card {
overflow: hidden; /* Required for glare */
border-radius: 10px;
}---
Memory Leaks in SPAs
Solution: Always destroy on unmount.
// Store reference
const element = document.querySelector(".tilt-card");
VanillaTilt.init(element);
// Before removing element
element.vanillaTilt.destroy();---
Resources
- GitHub: https://github.com/micku7zu/vanilla-tilt.js
- NPM:
npm install vanilla-tilt - Examples: https://micku7zu.github.io/vanilla-tilt.js/
---
License
MIT License - Free for commercial and personal use.
Vanta.js Effects Reference
Complete reference for Vanta.js - animated WebGL backgrounds powered by Three.js.
Version: 0.5.24 License: MIT Website: https://www.vantajs.com
---
Table of Contents
1. Getting Started 2. Available Effects 3. Common Options 4. Effect-Specific Options 5. Methods 6. Framework Integration 7. Performance
---
Getting Started
Basic Setup
<!-- Three.js (required for all effects) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<!-- Vanta.js effect (choose one) -->
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.waves.min.js"></script>
<div id="vanta-bg"></div>
<script>
VANTA.WAVES({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00
});
</script>NPM Installation
npm install vantaimport * as THREE from 'three';
import WAVES from 'vanta/dist/vanta.waves.min.js';
const vantaEffect = WAVES({
el: "#vanta-bg",
THREE: THREE
});---
Available Effects
1. WAVES
Animated wave surface effect.
CDN: vanta.waves.min.js
VANTA.WAVES({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
color: 0x23153c, // Hex number
shininess: 30.00,
waveHeight: 15.00,
waveSpeed: 0.75,
zoom: 0.65
});Options:
| Option | Type | Default | Description |
|---|---|---|---|
color | Number | 0x23153c | Wave color (hex number) |
shininess | Number | 30 | Surface reflectivity (0-100) |
waveHeight | Number | 15 | Wave amplitude |
waveSpeed | Number | 0.75 | Animation speed (0-2) |
zoom | Number | 0.65 | Camera zoom level |
---
2. CLOUDS
Volumetric cloud-like effect.
CDN: vanta.clouds.min.js
VANTA.CLOUDS({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
skyColor: 0x68b8d7,
cloudColor: 0xadc1de,
cloudShadowColor: 0x183550,
sunColor: 0xff9919,
sunGlareColor: 0xff6633,
sunlightColor: 0xff9933,
speed: 1.00
});Options:
| Option | Type | Default | Description |
|---|---|---|---|
skyColor | Number | 0x68b8d7 | Sky background color |
cloudColor | Number | 0xadc1de | Cloud base color |
cloudShadowColor | Number | 0x183550 | Cloud shadow color |
sunColor | Number | 0xff9919 | Sun body color |
sunGlareColor | Number | 0xff6633 | Sun glare color |
sunlightColor | Number | 0xff9933 | Sunlight color |
speed | Number | 1.00 | Cloud movement speed |
---
3. BIRDS
Flocking birds simulation.
CDN: vanta.birds.min.js
VANTA.BIRDS({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
backgroundColor: 0x23153c,
color1: 0xff0090,
color2: 0xff6633,
colorMode: "lerp", // "lerp" or "lerpGradient"
birdSize: 1.00,
wingSpan: 20.00,
speedLimit: 5.00,
separation: 20.00,
alignment: 20.00,
cohesion: 20.00,
quantity: 3.00
});Options:
| Option | Type | Default | Description |
|---|---|---|---|
backgroundColor | Number | 0x23153c | Scene background |
color1 | Number | 0xff0090 | Primary bird color |
color2 | Number | 0xff6633 | Secondary bird color |
colorMode | String | "lerp" | Color blending mode |
birdSize | Number | 1.00 | Bird scale multiplier |
wingSpan | Number | 20 | Wing spread distance |
speedLimit | Number | 5 | Maximum flight speed |
separation | Number | 20 | Avoid crowding force |
alignment | Number | 20 | Align with neighbors |
cohesion | Number | 20 | Move toward center |
quantity | Number | 3 | Number of birds |
---
4. NET
Particle network with connecting lines.
CDN: vanta.net.min.js
VANTA.NET({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
color: 0x3fafff,
backgroundColor: 0x23153c,
points: 10.00,
maxDistance: 20.00,
spacing: 15.00,
showDots: true
});Options:
| Option | Type | Default | Description |
|---|---|---|---|
color | Number | 0x3fafff | Line/point color |
backgroundColor | Number | 0x23153c | Background color |
points | Number | 10 | Number of points |
maxDistance | Number | 20 | Max connection distance |
spacing | Number | 15 | Point spacing |
showDots | Boolean | true | Display dots |
---
5. CELLS
Organic cellular growth pattern.
CDN: vanta.cells.min.js
VANTA.CELLS({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
color1: 0x18b0c6,
color2: 0xff6633,
size: 1.50,
speed: 1.00
});Options:
| Option | Type | Default | Description |
|---|---|---|---|
color1 | Number | 0x18b0c6 | Primary cell color |
color2 | Number | 0xff6633 | Secondary cell color |
size | Number | 1.50 | Cell size multiplier |
speed | Number | 1.00 | Growth animation speed |
---
6. FOG
Misty fog effect with depth.
CDN: vanta.fog.min.js
VANTA.FOG({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
highlightColor: 0xff3f81,
midtoneColor: 0xff1f51,
lowlightColor: 0x2d1b46,
baseColor: 0xffebff,
blurFactor: 0.60,
speed: 1.00,
zoom: 1.00
});Options:
| Option | Type | Default | Description |
|---|---|---|---|
highlightColor | Number | 0xff3f81 | Bright fog color |
midtoneColor | Number | 0xff1f51 | Mid fog color |
lowlightColor | Number | 0x2d1b46 | Dark fog color |
baseColor | Number | 0xffebff | Base background |
blurFactor | Number | 0.60 | Blur intensity (0-1) |
speed | Number | 1.00 | Movement speed |
zoom | Number | 1.00 | Camera zoom |
---
7. GLOBE
Rotating globe with points.
CDN: vanta.globe.min.js
VANTA.GLOBE({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
color: 0x3fafff,
color2: 0xff6633,
size: 1.50,
backgroundColor: 0x23153c
});Options:
| Option | Type | Default | Description |
|---|---|---|---|
color | Number | 0x3fafff | Primary globe color |
color2 | Number | 0xff6633 | Secondary color |
size | Number | 1.50 | Globe size |
backgroundColor | Number | 0x23153c | Background color |
---
8. RINGS
Concentric animated rings.
CDN: vanta.rings.min.js
VANTA.RINGS({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
backgroundColor: 0x23153c,
color: 0xff3f81
});Options:
| Option | Type | Default | Description |
|---|---|---|---|
backgroundColor | Number | 0x23153c | Background color |
color | Number | 0xff3f81 | Ring color |
---
9. HALO
Glowing halo particle effect.
CDN: vanta.halo.min.js
VANTA.HALO({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
backgroundColor: 0x111122,
size: 1.50,
xOffset: 0.20,
yOffset: 0.10
});Options:
| Option | Type | Default | Description |
|---|---|---|---|
backgroundColor | Number | 0x111122 | Background color |
size | Number | 1.50 | Halo size |
xOffset | Number | 0.20 | Horizontal position |
yOffset | Number | 0.10 | Vertical position |
---
10. TRUNK
Abstract trunk/tree structure.
CDN: vanta.trunk.min.js
VANTA.TRUNK({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
backgroundColor: 0x23153c,
color: 0xff3f81,
spacing: 2.00,
chaos: 4.00
});Options:
| Option | Type | Default | Description |
|---|---|---|---|
backgroundColor | Number | 0x23153c | Background color |
color | Number | 0xff3f81 | Trunk color |
spacing | Number | 2.00 | Branch spacing |
chaos | Number | 4.00 | Randomness factor |
---
11. TOPOLOGY
Topographic mesh surface.
CDN: vanta.topology.min.js
VANTA.TOPOLOGY({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
color: 0xff3f81,
backgroundColor: 0x23153c
});Options:
| Option | Type | Default | Description |
|---|---|---|---|
color | Number | 0xff3f81 | Mesh line color |
backgroundColor | Number | 0x23153c | Background color |
---
12. DOTS
Particle dot field effect.
CDN: vanta.dots.min.js
VANTA.DOTS({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
color: 0xff3f81,
color2: 0xffffff,
backgroundColor: 0x23153c,
size: 3.00,
spacing: 35.00
});Options:
| Option | Type | Default | Description |
|---|---|---|---|
color | Number | 0xff3f81 | Primary dot color |
color2 | Number | 0xffffff | Secondary dot color |
backgroundColor | Number | 0x23153c | Background color |
size | Number | 3.00 | Dot size |
spacing | Number | 35.00 | Dot spacing |
---
13. CLOUDS2
Alternative cloud implementation.
CDN: vanta.clouds2.min.js
VANTA.CLOUDS2({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
backgroundColor: 0x23153c,
skyColor: 0x3f7fb7,
cloudColor: 0x28496e,
lightColor: 0xff8800,
speed: 1.00
});---
14. RIPPLE
Water ripple effect.
Dependencies: vanta.ripple.min.js + p5.js
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.ripple.min.js"></script>VANTA.RIPPLE({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
color: 0x3f7fb7
});---
Common Options
These options work with most/all Vanta effects.
Control Options
{
el: "#vanta-bg", // Required: DOM element or selector
mouseControls: true, // Mouse movement interaction
touchControls: true, // Touch interaction
gyroControls: false, // Gyroscope interaction (mobile)
minHeight: 200.00, // Minimum height before effect pauses
minWidth: 200.00, // Minimum width before effect pauses
scale: 1.00, // Desktop scale
scaleMobile: 1.00 // Mobile scale multiplier
}Color Format
IMPORTANT: Colors must be hex numbers, NOT strings.
// ✅ Correct
color: 0xff3f81
// ❌ Incorrect
color: "#ff3f81"
color: "0xff3f81"Conversion:
// String to number
let colorString = "#ff3f81";
let colorNumber = parseInt(colorString.replace("#", "0x"));
// Number to string
let colorNumber = 0xff3f81;
let colorString = "#" + colorNumber.toString(16).padStart(6, '0');---
Methods
All Vanta effects return an instance with these methods:
destroy()
Remove the effect and clean up resources.
let vantaEffect = VANTA.WAVES({ el: "#vanta-bg" });
// Later...
vantaEffect.destroy();Important: Always call destroy() when removing the effect to prevent memory leaks.
---
setOptions(options)
Update effect options dynamically.
let vantaEffect = VANTA.WAVES({
el: "#vanta-bg",
color: 0x23153c
});
// Update color
vantaEffect.setOptions({
color: 0xff3f81,
waveHeight: 20
});---
resize()
Manually trigger a resize recalculation.
window.addEventListener('resize', () => {
vantaEffect.resize();
});Note: Vanta usually auto-detects resize, but manual calls help in edge cases.
---
Framework Integration
React
import React, { useState, useEffect, useRef } from 'react';
import * as THREE from 'three';
import WAVES from 'vanta/dist/vanta.waves.min.js';
function VantaBackground() {
const [vantaEffect, setVantaEffect] = useState(null);
const vantaRef = useRef(null);
useEffect(() => {
if (!vantaEffect) {
setVantaEffect(
WAVES({
el: vantaRef.current,
THREE: THREE,
mouseControls: true,
touchControls: true,
gyroControls: false,
color: 0x23153c,
shininess: 30,
waveHeight: 15,
zoom: 0.65
})
);
}
return () => {
if (vantaEffect) vantaEffect.destroy();
};
}, [vantaEffect]);
return (
<div ref={vantaRef} style={{ width: '100%', height: '100vh' }}>
<h1>Content over Vanta</h1>
</div>
);
}
export default VantaBackground;---
Vue 3
<template>
<div ref="vantaRef" class="vanta-container">
<slot></slot>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import * as THREE from 'three';
import WAVES from 'vanta/dist/vanta.waves.min.js';
const vantaRef = ref(null);
let vantaEffect = null;
onMounted(() => {
vantaEffect = WAVES({
el: vantaRef.value,
THREE: THREE,
mouseControls: true,
touchControls: true,
color: 0x23153c
});
});
onUnmounted(() => {
if (vantaEffect) vantaEffect.destroy();
});
</script>
<style scoped>
.vanta-container {
width: 100%;
height: 100vh;
}
</style>---
Angular
import { Component, OnInit, OnDestroy, ElementRef, ViewChild } from '@angular/core';
import * as THREE from 'three';
import WAVES from 'vanta/dist/vanta.waves.min.js';
@Component({
selector: 'app-vanta-bg',
template: `
<div #vantaRef class="vanta-container">
<ng-content></ng-content>
</div>
`,
styles: [`
.vanta-container {
width: 100%;
height: 100vh;
}
`]
})
export class VantaBgComponent implements OnInit, OnDestroy {
@ViewChild('vantaRef') vantaRef!: ElementRef;
vantaEffect: any;
ngOnInit() {
this.vantaEffect = WAVES({
el: this.vantaRef.nativeElement,
THREE: THREE,
mouseControls: true,
touchControls: true,
color: 0x23153c
});
}
ngOnDestroy() {
if (this.vantaEffect) this.vantaEffect.destroy();
}
}---
Svelte
<script>
import { onMount, onDestroy } from 'svelte';
import * as THREE from 'three';
import WAVES from 'vanta/dist/vanta.waves.min.js';
let vantaRef;
let vantaEffect;
onMount(() => {
vantaEffect = WAVES({
el: vantaRef,
THREE: THREE,
mouseControls: true,
touchControls: true,
color: 0x23153c
});
});
onDestroy(() => {
if (vantaEffect) vantaEffect.destroy();
});
</script>
<div bind:this={vantaRef} class="vanta-container">
<slot></slot>
</div>
<style>
.vanta-container {
width: 100%;
height: 100vh;
}
</style>---
Performance
Optimization Tips
1. Disable unused controls
VANTA.WAVES({
el: "#vanta-bg",
mouseControls: false, // Disable if not needed
touchControls: false,
gyroControls: false
});2. Reduce complexity on mobile
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
VANTA.WAVES({
el: "#vanta-bg",
scaleMobile: 0.5, // Smaller scale
waveHeight: isMobile ? 10 : 15,
points: isMobile ? 5 : 10
});3. Pause when not visible
let vantaEffect = VANTA.WAVES({ el: "#vanta-bg" });
let observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
vantaEffect.restart();
} else {
vantaEffect.destroy();
}
});
observer.observe(document.querySelector("#vanta-bg"));4. Set minHeight/minWidth
VANTA.WAVES({
el: "#vanta-bg",
minHeight: 200,
minWidth: 200
// Effect pauses below these dimensions
});5. Use simpler effects on low-end devices
const isLowEnd = navigator.hardwareConcurrency <= 4;
const effect = isLowEnd
? VANTA.DOTS // Simpler effect
: VANTA.WAVES; // Complex effect
effect({ el: "#vanta-bg" });---
Effect Performance Ranking
Most performant (lightest): 1. DOTS 2. NET 3. RINGS 4. TOPOLOGY
Moderate: 5. WAVES 6. CELLS 7. FOG 8. HALO
Intensive (heaviest): 9. BIRDS 10. CLOUDS 11. GLOBE 12. TRUNK
---
Mobile Fallbacks
Option 1: Disable on mobile
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
if (!isMobile) {
VANTA.WAVES({ el: "#vanta-bg" });
} else {
document.querySelector("#vanta-bg").style.background = '#23153c';
}Option 2: Static image fallback
.vanta-container {
background: url('fallback-image.jpg') center/cover;
}
.vanta-container.has-vanta {
background: none;
}VANTA.WAVES({ el: "#vanta-bg" });
document.querySelector("#vanta-bg").classList.add('has-vanta');---
Troubleshooting
Multiple Vanta Instances
Problem: Only one effect renders.
Solution: Use unique elements.
// ❌ Wrong - same element
VANTA.WAVES({ el: ".vanta-bg" });
VANTA.BIRDS({ el: ".vanta-bg" });
// ✅ Correct - different elements
VANTA.WAVES({ el: "#vanta-bg-1" });
VANTA.BIRDS({ el: "#vanta-bg-2" });---
Memory Leaks in SPAs
Problem: Effect persists after navigation.
Solution: Always destroy on unmount.
// Store reference
let vantaEffect = VANTA.WAVES({ el: "#vanta-bg" });
// Cleanup (React example)
useEffect(() => {
return () => {
if (vantaEffect) vantaEffect.destroy();
};
}, []);---
Color Not Changing
Problem: Color updates don't work.
Solution: Use hex numbers, not strings.
// ❌ Wrong
vantaEffect.setOptions({ color: "#ff3f81" });
// ✅ Correct
vantaEffect.setOptions({ color: 0xff3f81 });---
Effect Not Responsive
Problem: Effect doesn't resize with window.
Solution: Call resize() or enable auto-resize.
window.addEventListener('resize', () => {
vantaEffect.resize();
});---
Resources
- Official Site: https://www.vantajs.com
- GitHub: https://github.com/tengbao/vanta
- Interactive Gallery: https://www.vantajs.com/?effect=waves
- NPM:
npm install vanta
---
License
MIT License - Free for commercial and personal use.
Related skills
How it compares
Choose lightweight-3d-effects for CSS-friendly marketing 3D; reach for full WebGL skills when scenes require custom shaders or game-level rendering.
FAQ
Which libraries does lightweight-3d-effects use?
Zdog for pseudo-3D illustrations, Vanta.js for animated backgrounds, and Vanilla-Tilt.js for parallax tilt effects.
When should I use this skill?
For decorative 3D elements, hero section animations, and subtle card tilt micro-interactions without heavy frameworks.
What rendering options does Zdog support?
Canvas or SVG rendering with a declarative API, drag rotation, and smooth animation loops.
Is Lightweight 3d Effects safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.