The Grainient component creates stunning animated gradient backgrounds using WebGL shaders, featuring customizable colors, warp effects, grain texture, and color adjustments.
Overview
Key features:
- WebGL 2.0 shader-based rendering
- Three-color gradient blending
- Animated warp/distortion effects
- Grain texture overlay
- Color correction (contrast, gamma, saturation)
- Center offset and zoom controls
- Responsive and performant
- Fully customizable via props
Props
Animation
Speed of the animation (higher = faster)
Colors
First gradient color (hex format)
Second gradient color (hex format)
Third gradient color (hex format)
Balance between colors (-1 to 1)
Warp Effects
Overall strength of warp effect
Amplitude of warp displacement
Blending
Angle of gradient blend in degrees
Softness of color transitions
Rotation & Noise
Amount of rotation effect
Grain
Amount of grain texture (0-1)
Whether grain moves over time
Color Correction
Contrast adjustment (1 = normal)
Gamma correction (1 = normal)
Color saturation (0 = grayscale, 1 = normal)
Horizontal center offset (-1 to 1)
Vertical center offset (-1 to 1)
Zoom level (lower = zoomed in)
Styling
Additional CSS classes for the container
Implementation
import Grainient from './components/Grainient/Grainient';
function App() {
return (
<div className="w-full h-screen">
<Grainient
color1="#FF9FFC"
color2="#5227FF"
color3="#B19EEF"
timeSpeed={0.5}
warpStrength={1.5}
grainAmount={0.15}
grainAnimated
/>
</div>
);
}
Example Configurations
Hero Background
Vibrant Purple
Sunset
Ocean
<Grainient
color1="#000000"
color2="#726e6e"
color3="#fafafa"
timeSpeed={0.9}
colorBalance={0}
warpStrength={1}
warpFrequency={2}
warpSpeed={2}
warpAmplitude={50}
blendAngle={-20}
blendSoftness={0.05}
rotationAmount={500}
noiseScale={2}
grainAmount={0.1}
grainScale={2}
grainAnimated
contrast={1.5}
gamma={1}
saturation={1}
centerX={0}
centerY={0}
zoom={0.9}
/>
Monochromatic gradient with subtle animation (used in Hero component)<Grainient
color1="#FF9FFC"
color2="#5227FF"
color3="#B19EEF"
timeSpeed={0.5}
warpStrength={2}
warpFrequency={8}
grainAmount={0.2}
contrast={2}
saturation={1.3}
/>
Bold, vibrant purple gradient with high contrast<Grainient
color1="#FF6B6B"
color2="#FFA500"
color3="#FFD700"
timeSpeed={0.3}
blendAngle={45}
warpStrength={0.5}
grainAmount={0.05}
gamma={1.2}
/>
Warm sunset colors with gentle animation<Grainient
color1="#0066CC"
color2="#00CCFF"
color3="#66FFFF"
timeSpeed={0.4}
warpFrequency={3}
warpSpeed={1.5}
grainAmount={0.08}
saturation={1.2}
/>
Cool ocean-inspired gradient
Shader Architecture
The component uses custom GLSL shaders:
Vertex Shader
#version 300 es
in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
Simple fullscreen triangle rendering.
Fragment Shader
The fragment shader implements:
- UV Transformation: Centers and zooms the texture coordinates
- Noise-based Rotation: Applies dynamic rotation based on noise
- Warp Effect: Distorts UVs with sine waves
- Color Blending: Mixes three colors with smoothstep
- Grain Overlay: Adds film grain texture
- Color Correction: Applies contrast, saturation, and gamma
Key shader code:
// Warp effect
tuv.x += sin(tuv.y * frequency + warpTime) / amplitude;
tuv.y += sin(tuv.x * (frequency * 1.5) + warpTime) / (amplitude * 0.5);
// Gradient blending
vec3 layer1 = mix(colDark, colOrg, smoothstep(edge0, edge1, blendX));
vec3 layer2 = mix(colOrg, colLav, smoothstep(edge0, edge1, blendX));
vec3 col = mix(layer1, layer2, smoothstep(v0, v1, tuv.y));
// Grain
float grain = fract(sin(dot(grainUv, vec2(12.9898, 78.233))) * 43758.5453);
col += (grain - 0.5) * uGrainAmount;
Color Conversion
The component converts hex colors to RGB:
const hexToRgb = hex => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
if (!result) return [1, 1, 1];
return [
parseInt(result[1], 16) / 255,
parseInt(result[2], 16) / 255,
parseInt(result[3], 16) / 255
];
};
WebGL Initialization
const renderer = new Renderer({
webgl: 2, // Use WebGL 2.0
alpha: true, // Transparent background
antialias: false, // Disabled for performance
dpr: Math.min(window.devicePixelRatio || 1, 2) // Cap at 2x
});
Responsive Behavior
The component uses ResizeObserver for responsive sizing:
const setSize = () => {
const rect = container.getBoundingClientRect();
const width = Math.max(1, Math.floor(rect.width));
const height = Math.max(1, Math.floor(rect.height));
renderer.setSize(width, height);
res[0] = gl.drawingBufferWidth;
res[1] = gl.drawingBufferHeight;
};
const ro = new ResizeObserver(setSize);
ro.observe(container);
Animation Loop
const t0 = performance.now();
const loop = t => {
program.uniforms.iTime.value = (t - t0) * 0.001;
renderer.render({ scene: mesh });
raf = requestAnimationFrame(loop);
};
Cleanup
useEffect(() => {
// ... initialization
return () => {
cancelAnimationFrame(raf);
ro.disconnect();
try {
container.removeChild(canvas);
} catch {
// Ignore
}
};
}, [/* all props */]);
The Grainient component re-initializes when any prop changes. For performance, avoid changing props frequently during runtime.
- Device pixel ratio capped at 2x to prevent excessive GPU load
- Antialias disabled for better performance
- WebGL 2.0 for better shader performance
- Efficient fullscreen triangle rendering (3 vertices vs 4 for quad)
- ResizeObserver for optimal resize handling
Browser Compatibility
Requires:
- WebGL 2.0 support
- ES6+ JavaScript features
- Modern browser (Chrome 56+, Firefox 51+, Safari 15+, Edge 79+)