Noise Background
An animated noise background that injects just the right amount of dynamic grain
CUT THROUGH THE NOISE
Noise Intensity8%
Installation
Install required dependencies
npm i motion clsx tailwind-merge lucide-react
Download the noise background image below and place it in the public/images
folder of your project.
Copy the following source code
"use client";
import { useEffect, useRef } from "react";
import { motion, useAnimationControls } from "motion/react";
interface NoiseBackgroundProps {
animate?: boolean;
noiseIntensity?: number;
className?: string;
children?: React.ReactNode;
}
function NoiseBackground({
animate = true,
noiseIntensity = 0.08,
className = "",
children
}: NoiseBackgroundProps) {
const controls = useAnimationControls();
const isMounted = useRef(false);
const animationFrameId = useRef<number | null>(null);
useEffect(() => {
isMounted.current = true;
return () => {
isMounted.current = false;
if (animationFrameId.current) {
cancelAnimationFrame(animationFrameId.current);
}
};
}, []);
useEffect(() => {
const animateRandomly = async () => {
if (!isMounted.current || !animate) {
return;
}
const randomX1 = -15 + Math.random() * 30;
const randomX2 = -15 + Math.random() * 30;
const randomX3 = -15 + Math.random() * 30;
const randomY1 = -21 + Math.random() * 42;
const randomY2 = -21 + Math.random() * 42;
const randomY3 = -21 + Math.random() * 42;
try {
await controls.start({
transform: [
"translateX(0%) translateY(0%) translateZ(0px)",
`translateX(${randomX1}%) translateY(${randomY1}%) translateZ(0px)`,
`translateX(${randomX2}%) translateY(${randomY2}%) translateZ(0px)`,
`translateX(${randomX3}%) translateY(${randomY3}%) translateZ(0px)`,
"translateX(0%) translateY(0%) translateZ(0px)"
],
transition: {
duration: 0.2,
ease: "easeInOut",
times: [0, 0.25, 0.5, 0.75, 1]
}
});
if (isMounted.current && animate) {
animationFrameId.current = requestAnimationFrame(animateRandomly);
}
} catch (error) {
console.error("Animation failed to start or was interrupted:", error);
if (isMounted.current && animate) {
animationFrameId.current = requestAnimationFrame(animateRandomly);
}
}
};
if (animate) {
const timeoutId = setTimeout(() => {
if (isMounted.current) {
animateRandomly();
}
}, 0);
return () => {
clearTimeout(timeoutId);
if (animationFrameId.current) {
cancelAnimationFrame(animationFrameId.current);
}
controls.stop();
};
} else {
if (animationFrameId.current) {
cancelAnimationFrame(animationFrameId.current);
}
controls.stop();
}
}, [animate, controls]);
return (
<div className={`relative overflow-hidden ${className}`}>
<motion.div
className="absolute inset-[-200%] w-[400%] h-[400%] pointer-events-none"
animate={controls} // Link controls to this motion component
style={{
opacity: noiseIntensity,
background: "url('/images/noise.png')",
backgroundRepeat: "repeat",
}}
/>
<div className="relative z-[1] w-full h-full">
{children}
</div>
</div>
);
}
export default NoiseBackground;
Now paste it into your project and that's it!