Aurora Canvas
Installation
Copy and paste the following code into your project.
components/ui/aurora-canvas.tsx
import React, { useEffect, useRef } from "react";
import { cn } from "@/lib/utils";
export interface AuroraCanvasProps
extends React.HTMLAttributes<HTMLCanvasElement> {
colors?: string[];
speed?: number;
layers?: number;
interactive?: boolean;
}
interface Particle {
x: number;
y: number;
vx: number;
vy: number;
size: number;
color: string;
layer: number;
}
export function AuroraCanvas({
colors = ["#00ff87", "#60efff", "#0061ff", "#ff0099"],
speed = 0.2,
layers = 3,
interactive = true,
className,
...props
}: AuroraCanvasProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const particlesRef = useRef<Particle[]>([]);
const mousePosRef = useRef({ x: 0, y: 0 });
const frameRef = useRef<number | null>(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
// Set canvas size
const setCanvasSize = () => {
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * window.devicePixelRatio;
canvas.height = rect.height * window.devicePixelRatio;
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
};
setCanvasSize();
// Initialize particles
const particleCount = Math.floor(25 * layers);
particlesRef.current = Array.from({ length: particleCount }, () => ({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
vx: (Math.random() - 0.5) * speed,
vy: (Math.random() - 0.5) * speed,
size: Math.random() * 80 + 40,
color: colors[Math.floor(Math.random() * colors.length)],
layer: Math.floor(Math.random() * layers),
}));
// Animation loop
const animate = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const width = canvas.width / window.devicePixelRatio;
const height = canvas.height / window.devicePixelRatio;
// Update and draw particles
particlesRef.current.forEach((particle) => {
// Update position
particle.x += particle.vx;
particle.y += particle.vy;
// Wrap around edges
if (particle.x < -particle.size) particle.x = width + particle.size;
if (particle.x > width + particle.size) particle.x = -particle.size;
if (particle.y < -particle.size) particle.y = height + particle.size;
if (particle.y > height + particle.size) particle.y = -particle.size;
// Interactive movement
if (interactive && mousePosRef.current.x && mousePosRef.current.y) {
const dx = mousePosRef.current.x - particle.x;
const dy = mousePosRef.current.y - particle.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const maxDistance = 200;
if (distance < maxDistance) {
const force = (1 - distance / maxDistance) * 0.01;
particle.vx += dx * force;
particle.vy += dy * force;
}
}
// Apply velocity limits
const maxVelocity = speed * 2;
particle.vx = Math.max(
Math.min(particle.vx, maxVelocity),
-maxVelocity,
);
particle.vy = Math.max(
Math.min(particle.vy, maxVelocity),
-maxVelocity,
);
// Draw particle
ctx.beginPath();
const gradient = ctx.createRadialGradient(
particle.x,
particle.y,
0,
particle.x,
particle.y,
particle.size,
);
gradient.addColorStop(0, `${particle.color}30`);
gradient.addColorStop(1, `${particle.color}00`);
ctx.fillStyle = gradient;
ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
ctx.fill();
});
frameRef.current = requestAnimationFrame(animate);
};
animate();
// Mouse move handler with throttling
let lastMove = 0;
const handleMouseMove = (e: MouseEvent) => {
const now = Date.now();
if (now - lastMove < 16) return;
lastMove = now;
const rect = canvas.getBoundingClientRect();
mousePosRef.current = {
x: e.clientX - rect.left,
y: e.clientY - rect.top,
};
};
const handleResize = () => {
setCanvasSize();
// Reset particle positions on resize
particlesRef.current.forEach((particle) => {
particle.x = Math.random() * canvas.width;
particle.y = Math.random() * canvas.height;
});
};
if (interactive) {
canvas.addEventListener("mousemove", handleMouseMove);
}
window.addEventListener("resize", handleResize);
return () => {
if (frameRef.current) {
cancelAnimationFrame(frameRef.current);
}
window.removeEventListener("resize", handleResize);
if (interactive) {
canvas.removeEventListener("mousemove", handleMouseMove);
}
};
}, [colors, speed, layers, interactive]);
return (
<canvas
ref={canvasRef}
className={cn("block h-full w-full", className)}
{...props}
/>
);
}
Update the import paths to match your project setup.
import AuroraCanvas from "@/components/ui/aurora-canvas";
Usage
import AuroraCanvas from "@/components/ui/aurora-canvas";
export default function HeroSection() {
return (
<div className="relative h-[500px] w-full">
<AuroraCanvas />
</div>
);
}
Props
Name | Type | Default | Description |
---|---|---|---|
colors | string[] | ["#4f46e5", "#0ea5e9", "#6366f1"] | Array of colors used in the aurora effect |
speed | number | 0.2 | Speed of particle movement |
layers | number | 3 | Number of depth layers for the 3D effect |
interactive | boolean | true | Enable cursor interaction with particles |
className | string | undefined | Additional CSS classes |
Examples
Basic Usage
<AuroraCanvas />
Interactive Aurora
<AuroraCanvas interactive />
Custom Color Schemes
<AuroraCanvas
colors={[
"#4f46e5", // Deep indigo
"#0ea5e9", // Bright sky blue
"#6366f1", // Soft violet
"#8b5cf6", // Rich purple
"#ec4899", // Vibrant pink
"#f43f5e", // Bold rose
]}
/>
Hero Section Example
export default function Hero() {
return (
<div className="relative min-h-[600px] w-full overflow-hidden bg-gray-950">
<AuroraCanvas
className="absolute inset-0"
colors={["#4f46e5", "#0ea5e9", "#6366f1", "#8b5cf6"]}
speed={0.15}
/>
<div className="pointer-events-none relative z-10 flex min-h-[600px] flex-col items-center justify-center px-4 text-center">
<h1 className="bg-linear-to-b from-white to-gray-300 bg-clip-text text-5xl font-bold text-transparent">
Your Heading
</h1>
<p className="mt-6 max-w-2xl text-lg text-gray-300">
Your description text here
</p>
<button className="pointer-events-auto mt-8 rounded-full bg-white/10 px-6 py-2.5 text-white hover:bg-white/20">
Get Started
</button>
</div>
</div>
);
}
Slower, More Subtle Effect
<AuroraCanvas speed={0.1} layers={2} />
Important Notes
Interactivity with Content
When adding content over the Aurora Canvas, the interactivity might stop working because the content blocks mouse events. To fix this:
- Add pointer-events-none to the content container:
<div className="pointer-events-none">{/* Your content */}</div>
- Add pointer-events-auto to any interactive elements (buttons, links):
<button className="pointer-events-auto">Click Me</button>
This setup allows mouse events to pass through to the canvas while keeping interactive elements functional.