Grid Beams

A component for displaying a grid of beams.

Default
Default grid beams

Modern Solutions

Innovative technology for tomorrow's challenges

1"use client";
2
3import React, { useEffect, useRef } from "react";
4
5interface Beam {
6 color: string; // Color of the beam
7 position: number; // Position of the beam
8 progress: number; // Progress of the beam
9 speed: number; // Speed of the beam
10 width: number; // Width of the beam
11 isHorizontal: boolean; // Whether the beam is horizontal
12 opacity: number; // Opacity of the beam
13 delay: number; // Time before beam starts moving
14 pattern: number; // Pattern type (0: straight, 1: pulse, 2: accelerate-decelerate)
15 length: number; // Length of the beam (0-1)
16}
17
18// Pattern types as a type for better TypeScript support
19type BeamPattern = "steady" | "pulse" | "accelerate-decelerate";
20
21interface GridBeamsProps {
22 title?: string; // Title of the grid beams
23 subtitle?: string; // Subtitle of the grid beams
24 // Animation customization props
25 baseSpeed?: number; // Base speed multiplier
26 maxBeams?: number; // Maximum number of beams
27 initialBeams?: number; // Initial number of beams
28 beamDelay?: {
29 // Delay configuration
30 min: number; // Minimum delay frames
31 max: number; // Maximum delay frames
32 };
33 defaultPattern?: BeamPattern; // Default pattern for random beams
34 patternDistribution?: {
35 // Distribution of patterns (must sum to 1)
36 steady: number; // Proportion of steady beams
37 pulse: number; // Proportion of pulsing beams
38 "accelerate-decelerate": number; // Proportion of accelerate-decelerate beams
39 };
40 colors?: string[]; // Custom colors for beams
41 gridSize?: number; // Size of grid cells in pixels
42 randomStarts?: boolean; // Whether to randomize starting positions
43 beamLength?: { min: number; max: number }; // Length of beams
44}
45
46const GridBeams: React.FC<GridBeamsProps> = ({
47 title,
48 subtitle,
49 baseSpeed = 1.0,
50 maxBeams = 14,
51 initialBeams = 10,
52 beamDelay = { min: 0, max: 200 },
53 defaultPattern,
54 patternDistribution = {
55 steady: 0.3,
56 pulse: 0.4,
57 "accelerate-decelerate": 0.3,
58 },
59 colors = ["#3B82F6", "#8B5CF6", "#EC4899", "#10B981"],
60 gridSize = 40,
61 randomStarts = true,
62 beamLength = { min: 0.5, max: 0.9 },
63}) => {
64 const canvasRef = useRef<HTMLCanvasElement>(null);
65
66 // Convert pattern name to number
67 const getPatternNumber = (pattern: BeamPattern): number => {
68 switch (pattern) {
69 case "steady":
70 return 0;
71 case "pulse":
72 return 1;
73 case "accelerate-decelerate":
74 return 2;
75 default:
76 return 0;
77 }
78 };
79
80 // Get a random pattern based on distribution
81 const getRandomPattern = (): number => {
82 if (defaultPattern) {
83 return getPatternNumber(defaultPattern);
84 }
85
86 const rand = Math.random();
87 let cumulativeProbability = 0;
88
89 if (rand < (cumulativeProbability += patternDistribution.steady)) {
90 return 0; // Steady
91 } else if (rand < (cumulativeProbability += patternDistribution.pulse)) {
92 return 1; // Pulse
93 } else {
94 return 2; // Accelerate-decelerate
95 }
96 };
97
98 useEffect(() => {
99 const canvas = canvasRef.current;
100 if (!canvas) return;
101
102 const ctx = canvas.getContext("2d");
103 if (!ctx) return;
104
105 let animationFrameId: number;
106 let frame = 0;
107
108 // Set canvas dimensions to match parent container
109 const resizeCanvas = () => {
110 if (!canvas) return;
111 canvas.width = canvas.offsetWidth;
112 canvas.height = canvas.offsetHeight;
113 };
114
115 resizeCanvas();
116 window.addEventListener("resize", resizeCanvas);
117
118 const beams: Beam[] = [];
119
120 // Create initial beams with patterns
121 const createBeam = (patternOverride?: number): Beam => {
122 const isHorizontal = Math.random() > 0.5;
123 const pattern =
124 patternOverride !== undefined ? patternOverride : getRandomPattern();
125
126 // Calculate beam length based on the beamLength prop
127 const length =
128 beamLength.min + Math.random() * (beamLength.max - beamLength.min);
129
130 // Determine starting progress based on randomStarts
131 const startProgress = randomStarts ? Math.random() * (1 - length) : 0;
132
133 return {
134 color: colors[Math.floor(Math.random() * colors.length)],
135 position:
136 Math.floor(
137 (Math.random() * (isHorizontal ? canvas.height : canvas.width)) /
138 gridSize
139 ) * gridSize,
140 progress: startProgress,
141 speed: (0.2 + Math.random() * 0.6) * baseSpeed, // Apply base speed multiplier
142 width: 2 + Math.random() * 2,
143 isHorizontal,
144 opacity: 0.5 + Math.random() * 0.5,
145 delay:
146 beamDelay.min +
147 Math.floor(Math.random() * (beamDelay.max - beamDelay.min)),
148 pattern,
149 length: length, // Store the beam length
150 };
151 };
152
153 // Create beams with specific patterns - one of each type for each direction
154 const initializePatternedBeams = () => {
155 // Horizontal beams with different patterns
156 for (let i = 0; i < 3; i++) {
157 const beam = createBeam(i);
158 beam.isHorizontal = true;
159 beam.position = gridSize * (1 + i * 2);
160 beam.delay = i * 40; // Staggered delays
161 beams.push(beam);
162 }
163
164 // Vertical beams with different patterns
165 for (let i = 0; i < 3; i++) {
166 const beam = createBeam(i);
167 beam.isHorizontal = false;
168 beam.position = gridSize * (2 + i * 3);
169 beam.delay = 30 + i * 40; // Different staggered delays
170 beams.push(beam);
171 }
172
173 // Add additional random beams up to initialBeams count
174 const additionalBeams = Math.max(0, initialBeams - 6); // We already added 6 beams
175 for (let i = 0; i < additionalBeams; i++) {
176 beams.push(createBeam());
177 }
178 };
179
180 initializePatternedBeams();
181
182 // Update beam based on its pattern
183 const updateBeamProgress = (beam: Beam) => {
184 // Don't move if still in delay period
185 if (beam.delay > 0) {
186 beam.delay--;
187 return;
188 }
189
190 let speedFactor = 1;
191
192 switch (beam.pattern) {
193 case 0: // Steady movement
194 speedFactor = 1;
195 break;
196 case 1: // Pulsing - speed varies with sine wave
197 speedFactor = 0.5 + Math.sin(frame / 50) * 0.5;
198 break;
199 case 2: // Accelerate-decelerate
200 // Slower at beginning and end, faster in the middle
201 speedFactor =
202 beam.progress < 0.5 ? beam.progress * 2 : (1 - beam.progress) * 2;
203 speedFactor = 0.3 + speedFactor * 0.7; // Keep minimum speed
204 break;
205 }
206
207 beam.progress += (beam.speed / 400) * speedFactor; // Base speed div by 400 (slower)
208 };
209
210 // Animation loop
211 const animate = () => {
212 if (!canvas || !ctx) return;
213 frame++;
214
215 ctx.clearRect(0, 0, canvas.width, canvas.height);
216
217 // Draw grid
218 ctx.strokeStyle = "rgba(229, 231, 235, 0.1)"; // Light gray with transparency
219 ctx.lineWidth = 1;
220
221 // Draw vertical grid lines
222 for (let x = 0; x <= canvas.width; x += gridSize) {
223 ctx.beginPath();
224 ctx.moveTo(x, 0);
225 ctx.lineTo(x, canvas.height);
226 ctx.stroke();
227 }
228
229 // Draw horizontal grid lines
230 for (let y = 0; y <= canvas.height; y += gridSize) {
231 ctx.beginPath();
232 ctx.moveTo(0, y);
233 ctx.lineTo(canvas.width, y);
234 ctx.stroke();
235 }
236
237 // Draw and update beams
238 beams.forEach((beam, index) => {
239 ctx.beginPath();
240 ctx.globalAlpha = beam.opacity;
241
242 // For pulsing beams, also pulse opacity
243 if (beam.pattern === 1 && beam.delay === 0) {
244 ctx.globalAlpha = beam.opacity * (0.7 + Math.sin(frame / 50) * 0.3);
245 }
246
247 ctx.strokeStyle = beam.color;
248 ctx.lineWidth = beam.width;
249
250 if (beam.isHorizontal) {
251 const startX = canvas.width * beam.progress;
252 const endX = startX + canvas.width * beam.length;
253 ctx.moveTo(startX, beam.position);
254 ctx.lineTo(Math.min(endX, canvas.width), beam.position);
255 } else {
256 const startY = canvas.height * beam.progress;
257 const endY = startY + canvas.height * beam.length;
258 ctx.moveTo(beam.position, startY);
259 ctx.lineTo(beam.position, Math.min(endY, canvas.height));
260 }
261
262 ctx.stroke();
263 ctx.globalAlpha = 1;
264
265 // Update beam progress based on pattern
266 updateBeamProgress(beam);
267
268 // Reset beam when it reaches the end (fully off screen)
269 if (beam.progress > 1) {
270 // For patterned beams, maintain their pattern
271 if (index < 6) {
272 // The first 6 are our patterned beams
273 beam.progress = randomStarts
274 ? Math.random() * (1 - beam.length)
275 : 0;
276 beam.delay =
277 beamDelay.min +
278 Math.floor(Math.random() * (beamDelay.max - beamDelay.min));
279
280 // Maybe change color
281 if (Math.random() > 0.7) {
282 beam.color = colors[Math.floor(Math.random() * colors.length)];
283 }
284 } else {
285 // Random beams can change completely
286 if (Math.random() > 0.3) {
287 beam.progress = randomStarts
288 ? Math.random() * (1 - beam.length)
289 : 0;
290 beam.delay =
291 beamDelay.min +
292 Math.floor(Math.random() * (beamDelay.max - beamDelay.min));
293 } else {
294 beams[index] = createBeam();
295 }
296 }
297 }
298 });
299
300 // Add a new random beam occasionally but keep total reasonable
301 if (beams.length < maxBeams && frame % 300 === 0) {
302 beams.push(createBeam());
303 }
304
305 animationFrameId = requestAnimationFrame(animate);
306 };
307
308 animate();
309
310 // Clean up
311 return () => {
312 window.removeEventListener("resize", resizeCanvas);
313 cancelAnimationFrame(animationFrameId);
314 };
315 }, [
316 baseSpeed,
317 maxBeams,
318 initialBeams,
319 beamDelay,
320 defaultPattern,
321 patternDistribution,
322 colors,
323 gridSize,
324 randomStarts,
325 beamLength,
326 ]);
327
328 return (
329 <div className="relative w-full h-[40vh] bg-gray-900 overflow-hidden">
330 {/* Canvas for grid and beams */}
331 <canvas ref={canvasRef} className="absolute inset-0 w-full h-[40vh]" />
332
333 {/* Overlay gradient for better text visibility */}
334 <div className="absolute inset-0 bg-gradient-to-b from-gray-900/70 to-gray-900/90"></div>
335
336 {/* Content */}
337 <div className="relative z-10 flex flex-col items-center justify-center h-full px-4 text-center">
338 <h1 className="text-4xl md:text-6xl font-bold text-white mb-4">
339 {title || "Building The Future"}
340 </h1>
341 <p className="text-xl md:text-2xl text-gray-300 max-w-2xl">
342 {subtitle ||
343 "Create something amazing with our next-generation technology platform"}
344 </p>
345 <div className="mt-8">
346 <button className="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-lg mr-4 transition-colors">
347 Get Started
348 </button>
349 <button className="bg-transparent border border-white text-white font-bold py-3 px-6 rounded-lg hover:bg-white/10 transition-colors">
350 Learn More
351 </button>
352 </div>
353 </div>
354 </div>
355 );
356};
357
358export default GridBeams;
E2E Beams
No random starting positions

Edge-to-Edge Beams

Classic grid beam effect starting from edges

1"use client";
2
3import React, { useEffect, useRef } from "react";
4
5interface Beam {
6 color: string; // Color of the beam
7 position: number; // Position of the beam
8 progress: number; // Progress of the beam
9 speed: number; // Speed of the beam
10 width: number; // Width of the beam
11 isHorizontal: boolean; // Whether the beam is horizontal
12 opacity: number; // Opacity of the beam
13 delay: number; // Time before beam starts moving
14 pattern: number; // Pattern type (0: straight, 1: pulse, 2: accelerate-decelerate)
15 length: number; // Length of the beam (0-1)
16}
17
18// Pattern types as a type for better TypeScript support
19type BeamPattern = "steady" | "pulse" | "accelerate-decelerate";
20
21interface GridBeamsProps {
22 title?: string; // Title of the grid beams
23 subtitle?: string; // Subtitle of the grid beams
24 // Animation customization props
25 baseSpeed?: number; // Base speed multiplier
26 maxBeams?: number; // Maximum number of beams
27 initialBeams?: number; // Initial number of beams
28 beamDelay?: {
29 // Delay configuration
30 min: number; // Minimum delay frames
31 max: number; // Maximum delay frames
32 };
33 defaultPattern?: BeamPattern; // Default pattern for random beams
34 patternDistribution?: {
35 // Distribution of patterns (must sum to 1)
36 steady: number; // Proportion of steady beams
37 pulse: number; // Proportion of pulsing beams
38 "accelerate-decelerate": number; // Proportion of accelerate-decelerate beams
39 };
40 colors?: string[]; // Custom colors for beams
41 gridSize?: number; // Size of grid cells in pixels
42 randomStarts?: boolean; // Whether to randomize starting positions
43 beamLength?: { min: number; max: number }; // Length of beams
44}
45
46const GridBeams: React.FC<GridBeamsProps> = ({
47 title,
48 subtitle,
49 baseSpeed = 1.0,
50 maxBeams = 14,
51 initialBeams = 10,
52 beamDelay = { min: 0, max: 200 },
53 defaultPattern,
54 patternDistribution = {
55 steady: 0.3,
56 pulse: 0.4,
57 "accelerate-decelerate": 0.3,
58 },
59 colors = ["#3B82F6", "#8B5CF6", "#EC4899", "#10B981"],
60 gridSize = 40,
61 randomStarts = true,
62 beamLength = { min: 0.5, max: 0.9 },
63}) => {
64 const canvasRef = useRef<HTMLCanvasElement>(null);
65
66 // Convert pattern name to number
67 const getPatternNumber = (pattern: BeamPattern): number => {
68 switch (pattern) {
69 case "steady":
70 return 0;
71 case "pulse":
72 return 1;
73 case "accelerate-decelerate":
74 return 2;
75 default:
76 return 0;
77 }
78 };
79
80 // Get a random pattern based on distribution
81 const getRandomPattern = (): number => {
82 if (defaultPattern) {
83 return getPatternNumber(defaultPattern);
84 }
85
86 const rand = Math.random();
87 let cumulativeProbability = 0;
88
89 if (rand < (cumulativeProbability += patternDistribution.steady)) {
90 return 0; // Steady
91 } else if (rand < (cumulativeProbability += patternDistribution.pulse)) {
92 return 1; // Pulse
93 } else {
94 return 2; // Accelerate-decelerate
95 }
96 };
97
98 useEffect(() => {
99 const canvas = canvasRef.current;
100 if (!canvas) return;
101
102 const ctx = canvas.getContext("2d");
103 if (!ctx) return;
104
105 let animationFrameId: number;
106 let frame = 0;
107
108 // Set canvas dimensions to match parent container
109 const resizeCanvas = () => {
110 if (!canvas) return;
111 canvas.width = canvas.offsetWidth;
112 canvas.height = canvas.offsetHeight;
113 };
114
115 resizeCanvas();
116 window.addEventListener("resize", resizeCanvas);
117
118 const beams: Beam[] = [];
119
120 // Create initial beams with patterns
121 const createBeam = (patternOverride?: number): Beam => {
122 const isHorizontal = Math.random() > 0.5;
123 const pattern =
124 patternOverride !== undefined ? patternOverride : getRandomPattern();
125
126 // Calculate beam length based on the beamLength prop
127 const length =
128 beamLength.min + Math.random() * (beamLength.max - beamLength.min);
129
130 // Determine starting progress based on randomStarts
131 const startProgress = randomStarts ? Math.random() * (1 - length) : 0;
132
133 return {
134 color: colors[Math.floor(Math.random() * colors.length)],
135 position:
136 Math.floor(
137 (Math.random() * (isHorizontal ? canvas.height : canvas.width)) /
138 gridSize
139 ) * gridSize,
140 progress: startProgress,
141 speed: (0.2 + Math.random() * 0.6) * baseSpeed, // Apply base speed multiplier
142 width: 2 + Math.random() * 2,
143 isHorizontal,
144 opacity: 0.5 + Math.random() * 0.5,
145 delay:
146 beamDelay.min +
147 Math.floor(Math.random() * (beamDelay.max - beamDelay.min)),
148 pattern,
149 length: length, // Store the beam length
150 };
151 };
152
153 // Create beams with specific patterns - one of each type for each direction
154 const initializePatternedBeams = () => {
155 // Horizontal beams with different patterns
156 for (let i = 0; i < 3; i++) {
157 const beam = createBeam(i);
158 beam.isHorizontal = true;
159 beam.position = gridSize * (1 + i * 2);
160 beam.delay = i * 40; // Staggered delays
161 beams.push(beam);
162 }
163
164 // Vertical beams with different patterns
165 for (let i = 0; i < 3; i++) {
166 const beam = createBeam(i);
167 beam.isHorizontal = false;
168 beam.position = gridSize * (2 + i * 3);
169 beam.delay = 30 + i * 40; // Different staggered delays
170 beams.push(beam);
171 }
172
173 // Add additional random beams up to initialBeams count
174 const additionalBeams = Math.max(0, initialBeams - 6); // We already added 6 beams
175 for (let i = 0; i < additionalBeams; i++) {
176 beams.push(createBeam());
177 }
178 };
179
180 initializePatternedBeams();
181
182 // Update beam based on its pattern
183 const updateBeamProgress = (beam: Beam) => {
184 // Don't move if still in delay period
185 if (beam.delay > 0) {
186 beam.delay--;
187 return;
188 }
189
190 let speedFactor = 1;
191
192 switch (beam.pattern) {
193 case 0: // Steady movement
194 speedFactor = 1;
195 break;
196 case 1: // Pulsing - speed varies with sine wave
197 speedFactor = 0.5 + Math.sin(frame / 50) * 0.5;
198 break;
199 case 2: // Accelerate-decelerate
200 // Slower at beginning and end, faster in the middle
201 speedFactor =
202 beam.progress < 0.5 ? beam.progress * 2 : (1 - beam.progress) * 2;
203 speedFactor = 0.3 + speedFactor * 0.7; // Keep minimum speed
204 break;
205 }
206
207 beam.progress += (beam.speed / 400) * speedFactor; // Base speed div by 400 (slower)
208 };
209
210 // Animation loop
211 const animate = () => {
212 if (!canvas || !ctx) return;
213 frame++;
214
215 ctx.clearRect(0, 0, canvas.width, canvas.height);
216
217 // Draw grid
218 ctx.strokeStyle = "rgba(229, 231, 235, 0.1)"; // Light gray with transparency
219 ctx.lineWidth = 1;
220
221 // Draw vertical grid lines
222 for (let x = 0; x <= canvas.width; x += gridSize) {
223 ctx.beginPath();
224 ctx.moveTo(x, 0);
225 ctx.lineTo(x, canvas.height);
226 ctx.stroke();
227 }
228
229 // Draw horizontal grid lines
230 for (let y = 0; y <= canvas.height; y += gridSize) {
231 ctx.beginPath();
232 ctx.moveTo(0, y);
233 ctx.lineTo(canvas.width, y);
234 ctx.stroke();
235 }
236
237 // Draw and update beams
238 beams.forEach((beam, index) => {
239 ctx.beginPath();
240 ctx.globalAlpha = beam.opacity;
241
242 // For pulsing beams, also pulse opacity
243 if (beam.pattern === 1 && beam.delay === 0) {
244 ctx.globalAlpha = beam.opacity * (0.7 + Math.sin(frame / 50) * 0.3);
245 }
246
247 ctx.strokeStyle = beam.color;
248 ctx.lineWidth = beam.width;
249
250 if (beam.isHorizontal) {
251 const startX = canvas.width * beam.progress;
252 const endX = startX + canvas.width * beam.length;
253 ctx.moveTo(startX, beam.position);
254 ctx.lineTo(Math.min(endX, canvas.width), beam.position);
255 } else {
256 const startY = canvas.height * beam.progress;
257 const endY = startY + canvas.height * beam.length;
258 ctx.moveTo(beam.position, startY);
259 ctx.lineTo(beam.position, Math.min(endY, canvas.height));
260 }
261
262 ctx.stroke();
263 ctx.globalAlpha = 1;
264
265 // Update beam progress based on pattern
266 updateBeamProgress(beam);
267
268 // Reset beam when it reaches the end (fully off screen)
269 if (beam.progress > 1) {
270 // For patterned beams, maintain their pattern
271 if (index < 6) {
272 // The first 6 are our patterned beams
273 beam.progress = randomStarts
274 ? Math.random() * (1 - beam.length)
275 : 0;
276 beam.delay =
277 beamDelay.min +
278 Math.floor(Math.random() * (beamDelay.max - beamDelay.min));
279
280 // Maybe change color
281 if (Math.random() > 0.7) {
282 beam.color = colors[Math.floor(Math.random() * colors.length)];
283 }
284 } else {
285 // Random beams can change completely
286 if (Math.random() > 0.3) {
287 beam.progress = randomStarts
288 ? Math.random() * (1 - beam.length)
289 : 0;
290 beam.delay =
291 beamDelay.min +
292 Math.floor(Math.random() * (beamDelay.max - beamDelay.min));
293 } else {
294 beams[index] = createBeam();
295 }
296 }
297 }
298 });
299
300 // Add a new random beam occasionally but keep total reasonable
301 if (beams.length < maxBeams && frame % 300 === 0) {
302 beams.push(createBeam());
303 }
304
305 animationFrameId = requestAnimationFrame(animate);
306 };
307
308 animate();
309
310 // Clean up
311 return () => {
312 window.removeEventListener("resize", resizeCanvas);
313 cancelAnimationFrame(animationFrameId);
314 };
315 }, [
316 baseSpeed,
317 maxBeams,
318 initialBeams,
319 beamDelay,
320 defaultPattern,
321 patternDistribution,
322 colors,
323 gridSize,
324 randomStarts,
325 beamLength,
326 ]);
327
328 return (
329 <div className="relative w-full h-[40vh] bg-gray-900 overflow-hidden">
330 {/* Canvas for grid and beams */}
331 <canvas ref={canvasRef} className="absolute inset-0 w-full h-[40vh]" />
332
333 {/* Overlay gradient for better text visibility */}
334 <div className="absolute inset-0 bg-gradient-to-b from-gray-900/70 to-gray-900/90"></div>
335
336 {/* Content */}
337 <div className="relative z-10 flex flex-col items-center justify-center h-full px-4 text-center">
338 <h1 className="text-4xl md:text-6xl font-bold text-white mb-4">
339 {title || "Building The Future"}
340 </h1>
341 <p className="text-xl md:text-2xl text-gray-300 max-w-2xl">
342 {subtitle ||
343 "Create something amazing with our next-generation technology platform"}
344 </p>
345 <div className="mt-8">
346 <button className="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-lg mr-4 transition-colors">
347 Get Started
348 </button>
349 <button className="bg-transparent border border-white text-white font-bold py-3 px-6 rounded-lg hover:bg-white/10 transition-colors">
350 Learn More
351 </button>
352 </div>
353 </div>
354 </div>
355 );
356};
357
358export default GridBeams;
Quick Pulses
Short, quick pulses across the grid

Quick Pulses

Short, quick pulses across the grid

1"use client";
2
3import React, { useEffect, useRef } from "react";
4
5interface Beam {
6 color: string; // Color of the beam
7 position: number; // Position of the beam
8 progress: number; // Progress of the beam
9 speed: number; // Speed of the beam
10 width: number; // Width of the beam
11 isHorizontal: boolean; // Whether the beam is horizontal
12 opacity: number; // Opacity of the beam
13 delay: number; // Time before beam starts moving
14 pattern: number; // Pattern type (0: straight, 1: pulse, 2: accelerate-decelerate)
15 length: number; // Length of the beam (0-1)
16}
17
18// Pattern types as a type for better TypeScript support
19type BeamPattern = "steady" | "pulse" | "accelerate-decelerate";
20
21interface GridBeamsProps {
22 title?: string; // Title of the grid beams
23 subtitle?: string; // Subtitle of the grid beams
24 // Animation customization props
25 baseSpeed?: number; // Base speed multiplier
26 maxBeams?: number; // Maximum number of beams
27 initialBeams?: number; // Initial number of beams
28 beamDelay?: {
29 // Delay configuration
30 min: number; // Minimum delay frames
31 max: number; // Maximum delay frames
32 };
33 defaultPattern?: BeamPattern; // Default pattern for random beams
34 patternDistribution?: {
35 // Distribution of patterns (must sum to 1)
36 steady: number; // Proportion of steady beams
37 pulse: number; // Proportion of pulsing beams
38 "accelerate-decelerate": number; // Proportion of accelerate-decelerate beams
39 };
40 colors?: string[]; // Custom colors for beams
41 gridSize?: number; // Size of grid cells in pixels
42 randomStarts?: boolean; // Whether to randomize starting positions
43 beamLength?: { min: number; max: number }; // Length of beams
44}
45
46const GridBeams: React.FC<GridBeamsProps> = ({
47 title,
48 subtitle,
49 baseSpeed = 1.0,
50 maxBeams = 14,
51 initialBeams = 10,
52 beamDelay = { min: 0, max: 200 },
53 defaultPattern,
54 patternDistribution = {
55 steady: 0.3,
56 pulse: 0.4,
57 "accelerate-decelerate": 0.3,
58 },
59 colors = ["#3B82F6", "#8B5CF6", "#EC4899", "#10B981"],
60 gridSize = 40,
61 randomStarts = true,
62 beamLength = { min: 0.5, max: 0.9 },
63}) => {
64 const canvasRef = useRef<HTMLCanvasElement>(null);
65
66 // Convert pattern name to number
67 const getPatternNumber = (pattern: BeamPattern): number => {
68 switch (pattern) {
69 case "steady":
70 return 0;
71 case "pulse":
72 return 1;
73 case "accelerate-decelerate":
74 return 2;
75 default:
76 return 0;
77 }
78 };
79
80 // Get a random pattern based on distribution
81 const getRandomPattern = (): number => {
82 if (defaultPattern) {
83 return getPatternNumber(defaultPattern);
84 }
85
86 const rand = Math.random();
87 let cumulativeProbability = 0;
88
89 if (rand < (cumulativeProbability += patternDistribution.steady)) {
90 return 0; // Steady
91 } else if (rand < (cumulativeProbability += patternDistribution.pulse)) {
92 return 1; // Pulse
93 } else {
94 return 2; // Accelerate-decelerate
95 }
96 };
97
98 useEffect(() => {
99 const canvas = canvasRef.current;
100 if (!canvas) return;
101
102 const ctx = canvas.getContext("2d");
103 if (!ctx) return;
104
105 let animationFrameId: number;
106 let frame = 0;
107
108 // Set canvas dimensions to match parent container
109 const resizeCanvas = () => {
110 if (!canvas) return;
111 canvas.width = canvas.offsetWidth;
112 canvas.height = canvas.offsetHeight;
113 };
114
115 resizeCanvas();
116 window.addEventListener("resize", resizeCanvas);
117
118 const beams: Beam[] = [];
119
120 // Create initial beams with patterns
121 const createBeam = (patternOverride?: number): Beam => {
122 const isHorizontal = Math.random() > 0.5;
123 const pattern =
124 patternOverride !== undefined ? patternOverride : getRandomPattern();
125
126 // Calculate beam length based on the beamLength prop
127 const length =
128 beamLength.min + Math.random() * (beamLength.max - beamLength.min);
129
130 // Determine starting progress based on randomStarts
131 const startProgress = randomStarts ? Math.random() * (1 - length) : 0;
132
133 return {
134 color: colors[Math.floor(Math.random() * colors.length)],
135 position:
136 Math.floor(
137 (Math.random() * (isHorizontal ? canvas.height : canvas.width)) /
138 gridSize
139 ) * gridSize,
140 progress: startProgress,
141 speed: (0.2 + Math.random() * 0.6) * baseSpeed, // Apply base speed multiplier
142 width: 2 + Math.random() * 2,
143 isHorizontal,
144 opacity: 0.5 + Math.random() * 0.5,
145 delay:
146 beamDelay.min +
147 Math.floor(Math.random() * (beamDelay.max - beamDelay.min)),
148 pattern,
149 length: length, // Store the beam length
150 };
151 };
152
153 // Create beams with specific patterns - one of each type for each direction
154 const initializePatternedBeams = () => {
155 // Horizontal beams with different patterns
156 for (let i = 0; i < 3; i++) {
157 const beam = createBeam(i);
158 beam.isHorizontal = true;
159 beam.position = gridSize * (1 + i * 2);
160 beam.delay = i * 40; // Staggered delays
161 beams.push(beam);
162 }
163
164 // Vertical beams with different patterns
165 for (let i = 0; i < 3; i++) {
166 const beam = createBeam(i);
167 beam.isHorizontal = false;
168 beam.position = gridSize * (2 + i * 3);
169 beam.delay = 30 + i * 40; // Different staggered delays
170 beams.push(beam);
171 }
172
173 // Add additional random beams up to initialBeams count
174 const additionalBeams = Math.max(0, initialBeams - 6); // We already added 6 beams
175 for (let i = 0; i < additionalBeams; i++) {
176 beams.push(createBeam());
177 }
178 };
179
180 initializePatternedBeams();
181
182 // Update beam based on its pattern
183 const updateBeamProgress = (beam: Beam) => {
184 // Don't move if still in delay period
185 if (beam.delay > 0) {
186 beam.delay--;
187 return;
188 }
189
190 let speedFactor = 1;
191
192 switch (beam.pattern) {
193 case 0: // Steady movement
194 speedFactor = 1;
195 break;
196 case 1: // Pulsing - speed varies with sine wave
197 speedFactor = 0.5 + Math.sin(frame / 50) * 0.5;
198 break;
199 case 2: // Accelerate-decelerate
200 // Slower at beginning and end, faster in the middle
201 speedFactor =
202 beam.progress < 0.5 ? beam.progress * 2 : (1 - beam.progress) * 2;
203 speedFactor = 0.3 + speedFactor * 0.7; // Keep minimum speed
204 break;
205 }
206
207 beam.progress += (beam.speed / 400) * speedFactor; // Base speed div by 400 (slower)
208 };
209
210 // Animation loop
211 const animate = () => {
212 if (!canvas || !ctx) return;
213 frame++;
214
215 ctx.clearRect(0, 0, canvas.width, canvas.height);
216
217 // Draw grid
218 ctx.strokeStyle = "rgba(229, 231, 235, 0.1)"; // Light gray with transparency
219 ctx.lineWidth = 1;
220
221 // Draw vertical grid lines
222 for (let x = 0; x <= canvas.width; x += gridSize) {
223 ctx.beginPath();
224 ctx.moveTo(x, 0);
225 ctx.lineTo(x, canvas.height);
226 ctx.stroke();
227 }
228
229 // Draw horizontal grid lines
230 for (let y = 0; y <= canvas.height; y += gridSize) {
231 ctx.beginPath();
232 ctx.moveTo(0, y);
233 ctx.lineTo(canvas.width, y);
234 ctx.stroke();
235 }
236
237 // Draw and update beams
238 beams.forEach((beam, index) => {
239 ctx.beginPath();
240 ctx.globalAlpha = beam.opacity;
241
242 // For pulsing beams, also pulse opacity
243 if (beam.pattern === 1 && beam.delay === 0) {
244 ctx.globalAlpha = beam.opacity * (0.7 + Math.sin(frame / 50) * 0.3);
245 }
246
247 ctx.strokeStyle = beam.color;
248 ctx.lineWidth = beam.width;
249
250 if (beam.isHorizontal) {
251 const startX = canvas.width * beam.progress;
252 const endX = startX + canvas.width * beam.length;
253 ctx.moveTo(startX, beam.position);
254 ctx.lineTo(Math.min(endX, canvas.width), beam.position);
255 } else {
256 const startY = canvas.height * beam.progress;
257 const endY = startY + canvas.height * beam.length;
258 ctx.moveTo(beam.position, startY);
259 ctx.lineTo(beam.position, Math.min(endY, canvas.height));
260 }
261
262 ctx.stroke();
263 ctx.globalAlpha = 1;
264
265 // Update beam progress based on pattern
266 updateBeamProgress(beam);
267
268 // Reset beam when it reaches the end (fully off screen)
269 if (beam.progress > 1) {
270 // For patterned beams, maintain their pattern
271 if (index < 6) {
272 // The first 6 are our patterned beams
273 beam.progress = randomStarts
274 ? Math.random() * (1 - beam.length)
275 : 0;
276 beam.delay =
277 beamDelay.min +
278 Math.floor(Math.random() * (beamDelay.max - beamDelay.min));
279
280 // Maybe change color
281 if (Math.random() > 0.7) {
282 beam.color = colors[Math.floor(Math.random() * colors.length)];
283 }
284 } else {
285 // Random beams can change completely
286 if (Math.random() > 0.3) {
287 beam.progress = randomStarts
288 ? Math.random() * (1 - beam.length)
289 : 0;
290 beam.delay =
291 beamDelay.min +
292 Math.floor(Math.random() * (beamDelay.max - beamDelay.min));
293 } else {
294 beams[index] = createBeam();
295 }
296 }
297 }
298 });
299
300 // Add a new random beam occasionally but keep total reasonable
301 if (beams.length < maxBeams && frame % 300 === 0) {
302 beams.push(createBeam());
303 }
304
305 animationFrameId = requestAnimationFrame(animate);
306 };
307
308 animate();
309
310 // Clean up
311 return () => {
312 window.removeEventListener("resize", resizeCanvas);
313 cancelAnimationFrame(animationFrameId);
314 };
315 }, [
316 baseSpeed,
317 maxBeams,
318 initialBeams,
319 beamDelay,
320 defaultPattern,
321 patternDistribution,
322 colors,
323 gridSize,
324 randomStarts,
325 beamLength,
326 ]);
327
328 return (
329 <div className="relative w-full h-[40vh] bg-gray-900 overflow-hidden">
330 {/* Canvas for grid and beams */}
331 <canvas ref={canvasRef} className="absolute inset-0 w-full h-[40vh]" />
332
333 {/* Overlay gradient for better text visibility */}
334 <div className="absolute inset-0 bg-gradient-to-b from-gray-900/70 to-gray-900/90"></div>
335
336 {/* Content */}
337 <div className="relative z-10 flex flex-col items-center justify-center h-full px-4 text-center">
338 <h1 className="text-4xl md:text-6xl font-bold text-white mb-4">
339 {title || "Building The Future"}
340 </h1>
341 <p className="text-xl md:text-2xl text-gray-300 max-w-2xl">
342 {subtitle ||
343 "Create something amazing with our next-generation technology platform"}
344 </p>
345 <div className="mt-8">
346 <button className="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-lg mr-4 transition-colors">
347 Get Started
348 </button>
349 <button className="bg-transparent border border-white text-white font-bold py-3 px-6 rounded-lg hover:bg-white/10 transition-colors">
350 Learn More
351 </button>
352 </div>
353 </div>
354 </div>
355 );
356};
357
358export default GridBeams;
Slow Motion Grid
Slow moving beams.

Slow Motion Grid

Custom animation with slow-moving beams

1"use client";
2
3import React, { useEffect, useRef } from "react";
4
5interface Beam {
6 color: string; // Color of the beam
7 position: number; // Position of the beam
8 progress: number; // Progress of the beam
9 speed: number; // Speed of the beam
10 width: number; // Width of the beam
11 isHorizontal: boolean; // Whether the beam is horizontal
12 opacity: number; // Opacity of the beam
13 delay: number; // Time before beam starts moving
14 pattern: number; // Pattern type (0: straight, 1: pulse, 2: accelerate-decelerate)
15 length: number; // Length of the beam (0-1)
16}
17
18// Pattern types as a type for better TypeScript support
19type BeamPattern = "steady" | "pulse" | "accelerate-decelerate";
20
21interface GridBeamsProps {
22 title?: string; // Title of the grid beams
23 subtitle?: string; // Subtitle of the grid beams
24 // Animation customization props
25 baseSpeed?: number; // Base speed multiplier
26 maxBeams?: number; // Maximum number of beams
27 initialBeams?: number; // Initial number of beams
28 beamDelay?: {
29 // Delay configuration
30 min: number; // Minimum delay frames
31 max: number; // Maximum delay frames
32 };
33 defaultPattern?: BeamPattern; // Default pattern for random beams
34 patternDistribution?: {
35 // Distribution of patterns (must sum to 1)
36 steady: number; // Proportion of steady beams
37 pulse: number; // Proportion of pulsing beams
38 "accelerate-decelerate": number; // Proportion of accelerate-decelerate beams
39 };
40 colors?: string[]; // Custom colors for beams
41 gridSize?: number; // Size of grid cells in pixels
42 randomStarts?: boolean; // Whether to randomize starting positions
43 beamLength?: { min: number; max: number }; // Length of beams
44}
45
46const GridBeams: React.FC<GridBeamsProps> = ({
47 title,
48 subtitle,
49 baseSpeed = 1.0,
50 maxBeams = 14,
51 initialBeams = 10,
52 beamDelay = { min: 0, max: 200 },
53 defaultPattern,
54 patternDistribution = {
55 steady: 0.3,
56 pulse: 0.4,
57 "accelerate-decelerate": 0.3,
58 },
59 colors = ["#3B82F6", "#8B5CF6", "#EC4899", "#10B981"],
60 gridSize = 40,
61 randomStarts = true,
62 beamLength = { min: 0.5, max: 0.9 },
63}) => {
64 const canvasRef = useRef<HTMLCanvasElement>(null);
65
66 // Convert pattern name to number
67 const getPatternNumber = (pattern: BeamPattern): number => {
68 switch (pattern) {
69 case "steady":
70 return 0;
71 case "pulse":
72 return 1;
73 case "accelerate-decelerate":
74 return 2;
75 default:
76 return 0;
77 }
78 };
79
80 // Get a random pattern based on distribution
81 const getRandomPattern = (): number => {
82 if (defaultPattern) {
83 return getPatternNumber(defaultPattern);
84 }
85
86 const rand = Math.random();
87 let cumulativeProbability = 0;
88
89 if (rand < (cumulativeProbability += patternDistribution.steady)) {
90 return 0; // Steady
91 } else if (rand < (cumulativeProbability += patternDistribution.pulse)) {
92 return 1; // Pulse
93 } else {
94 return 2; // Accelerate-decelerate
95 }
96 };
97
98 useEffect(() => {
99 const canvas = canvasRef.current;
100 if (!canvas) return;
101
102 const ctx = canvas.getContext("2d");
103 if (!ctx) return;
104
105 let animationFrameId: number;
106 let frame = 0;
107
108 // Set canvas dimensions to match parent container
109 const resizeCanvas = () => {
110 if (!canvas) return;
111 canvas.width = canvas.offsetWidth;
112 canvas.height = canvas.offsetHeight;
113 };
114
115 resizeCanvas();
116 window.addEventListener("resize", resizeCanvas);
117
118 const beams: Beam[] = [];
119
120 // Create initial beams with patterns
121 const createBeam = (patternOverride?: number): Beam => {
122 const isHorizontal = Math.random() > 0.5;
123 const pattern =
124 patternOverride !== undefined ? patternOverride : getRandomPattern();
125
126 // Calculate beam length based on the beamLength prop
127 const length =
128 beamLength.min + Math.random() * (beamLength.max - beamLength.min);
129
130 // Determine starting progress based on randomStarts
131 const startProgress = randomStarts ? Math.random() * (1 - length) : 0;
132
133 return {
134 color: colors[Math.floor(Math.random() * colors.length)],
135 position:
136 Math.floor(
137 (Math.random() * (isHorizontal ? canvas.height : canvas.width)) /
138 gridSize
139 ) * gridSize,
140 progress: startProgress,
141 speed: (0.2 + Math.random() * 0.6) * baseSpeed, // Apply base speed multiplier
142 width: 2 + Math.random() * 2,
143 isHorizontal,
144 opacity: 0.5 + Math.random() * 0.5,
145 delay:
146 beamDelay.min +
147 Math.floor(Math.random() * (beamDelay.max - beamDelay.min)),
148 pattern,
149 length: length, // Store the beam length
150 };
151 };
152
153 // Create beams with specific patterns - one of each type for each direction
154 const initializePatternedBeams = () => {
155 // Horizontal beams with different patterns
156 for (let i = 0; i < 3; i++) {
157 const beam = createBeam(i);
158 beam.isHorizontal = true;
159 beam.position = gridSize * (1 + i * 2);
160 beam.delay = i * 40; // Staggered delays
161 beams.push(beam);
162 }
163
164 // Vertical beams with different patterns
165 for (let i = 0; i < 3; i++) {
166 const beam = createBeam(i);
167 beam.isHorizontal = false;
168 beam.position = gridSize * (2 + i * 3);
169 beam.delay = 30 + i * 40; // Different staggered delays
170 beams.push(beam);
171 }
172
173 // Add additional random beams up to initialBeams count
174 const additionalBeams = Math.max(0, initialBeams - 6); // We already added 6 beams
175 for (let i = 0; i < additionalBeams; i++) {
176 beams.push(createBeam());
177 }
178 };
179
180 initializePatternedBeams();
181
182 // Update beam based on its pattern
183 const updateBeamProgress = (beam: Beam) => {
184 // Don't move if still in delay period
185 if (beam.delay > 0) {
186 beam.delay--;
187 return;
188 }
189
190 let speedFactor = 1;
191
192 switch (beam.pattern) {
193 case 0: // Steady movement
194 speedFactor = 1;
195 break;
196 case 1: // Pulsing - speed varies with sine wave
197 speedFactor = 0.5 + Math.sin(frame / 50) * 0.5;
198 break;
199 case 2: // Accelerate-decelerate
200 // Slower at beginning and end, faster in the middle
201 speedFactor =
202 beam.progress < 0.5 ? beam.progress * 2 : (1 - beam.progress) * 2;
203 speedFactor = 0.3 + speedFactor * 0.7; // Keep minimum speed
204 break;
205 }
206
207 beam.progress += (beam.speed / 400) * speedFactor; // Base speed div by 400 (slower)
208 };
209
210 // Animation loop
211 const animate = () => {
212 if (!canvas || !ctx) return;
213 frame++;
214
215 ctx.clearRect(0, 0, canvas.width, canvas.height);
216
217 // Draw grid
218 ctx.strokeStyle = "rgba(229, 231, 235, 0.1)"; // Light gray with transparency
219 ctx.lineWidth = 1;
220
221 // Draw vertical grid lines
222 for (let x = 0; x <= canvas.width; x += gridSize) {
223 ctx.beginPath();
224 ctx.moveTo(x, 0);
225 ctx.lineTo(x, canvas.height);
226 ctx.stroke();
227 }
228
229 // Draw horizontal grid lines
230 for (let y = 0; y <= canvas.height; y += gridSize) {
231 ctx.beginPath();
232 ctx.moveTo(0, y);
233 ctx.lineTo(canvas.width, y);
234 ctx.stroke();
235 }
236
237 // Draw and update beams
238 beams.forEach((beam, index) => {
239 ctx.beginPath();
240 ctx.globalAlpha = beam.opacity;
241
242 // For pulsing beams, also pulse opacity
243 if (beam.pattern === 1 && beam.delay === 0) {
244 ctx.globalAlpha = beam.opacity * (0.7 + Math.sin(frame / 50) * 0.3);
245 }
246
247 ctx.strokeStyle = beam.color;
248 ctx.lineWidth = beam.width;
249
250 if (beam.isHorizontal) {
251 const startX = canvas.width * beam.progress;
252 const endX = startX + canvas.width * beam.length;
253 ctx.moveTo(startX, beam.position);
254 ctx.lineTo(Math.min(endX, canvas.width), beam.position);
255 } else {
256 const startY = canvas.height * beam.progress;
257 const endY = startY + canvas.height * beam.length;
258 ctx.moveTo(beam.position, startY);
259 ctx.lineTo(beam.position, Math.min(endY, canvas.height));
260 }
261
262 ctx.stroke();
263 ctx.globalAlpha = 1;
264
265 // Update beam progress based on pattern
266 updateBeamProgress(beam);
267
268 // Reset beam when it reaches the end (fully off screen)
269 if (beam.progress > 1) {
270 // For patterned beams, maintain their pattern
271 if (index < 6) {
272 // The first 6 are our patterned beams
273 beam.progress = randomStarts
274 ? Math.random() * (1 - beam.length)
275 : 0;
276 beam.delay =
277 beamDelay.min +
278 Math.floor(Math.random() * (beamDelay.max - beamDelay.min));
279
280 // Maybe change color
281 if (Math.random() > 0.7) {
282 beam.color = colors[Math.floor(Math.random() * colors.length)];
283 }
284 } else {
285 // Random beams can change completely
286 if (Math.random() > 0.3) {
287 beam.progress = randomStarts
288 ? Math.random() * (1 - beam.length)
289 : 0;
290 beam.delay =
291 beamDelay.min +
292 Math.floor(Math.random() * (beamDelay.max - beamDelay.min));
293 } else {
294 beams[index] = createBeam();
295 }
296 }
297 }
298 });
299
300 // Add a new random beam occasionally but keep total reasonable
301 if (beams.length < maxBeams && frame % 300 === 0) {
302 beams.push(createBeam());
303 }
304
305 animationFrameId = requestAnimationFrame(animate);
306 };
307
308 animate();
309
310 // Clean up
311 return () => {
312 window.removeEventListener("resize", resizeCanvas);
313 cancelAnimationFrame(animationFrameId);
314 };
315 }, [
316 baseSpeed,
317 maxBeams,
318 initialBeams,
319 beamDelay,
320 defaultPattern,
321 patternDistribution,
322 colors,
323 gridSize,
324 randomStarts,
325 beamLength,
326 ]);
327
328 return (
329 <div className="relative w-full h-[40vh] bg-gray-900 overflow-hidden">
330 {/* Canvas for grid and beams */}
331 <canvas ref={canvasRef} className="absolute inset-0 w-full h-[40vh]" />
332
333 {/* Overlay gradient for better text visibility */}
334 <div className="absolute inset-0 bg-gradient-to-b from-gray-900/70 to-gray-900/90"></div>
335
336 {/* Content */}
337 <div className="relative z-10 flex flex-col items-center justify-center h-full px-4 text-center">
338 <h1 className="text-4xl md:text-6xl font-bold text-white mb-4">
339 {title || "Building The Future"}
340 </h1>
341 <p className="text-xl md:text-2xl text-gray-300 max-w-2xl">
342 {subtitle ||
343 "Create something amazing with our next-generation technology platform"}
344 </p>
345 <div className="mt-8">
346 <button className="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-lg mr-4 transition-colors">
347 Get Started
348 </button>
349 <button className="bg-transparent border border-white text-white font-bold py-3 px-6 rounded-lg hover:bg-white/10 transition-colors">
350 Learn More
351 </button>
352 </div>
353 </div>
354 </div>
355 );
356};
357
358export default GridBeams;
Mostly Accelerating Beams
Beams that accelerate and decelerate.

Mostly Accelerating Beams

More accelerate-decelerate pattern beams

1"use client";
2
3import React, { useEffect, useRef } from "react";
4
5interface Beam {
6 color: string; // Color of the beam
7 position: number; // Position of the beam
8 progress: number; // Progress of the beam
9 speed: number; // Speed of the beam
10 width: number; // Width of the beam
11 isHorizontal: boolean; // Whether the beam is horizontal
12 opacity: number; // Opacity of the beam
13 delay: number; // Time before beam starts moving
14 pattern: number; // Pattern type (0: straight, 1: pulse, 2: accelerate-decelerate)
15 length: number; // Length of the beam (0-1)
16}
17
18// Pattern types as a type for better TypeScript support
19type BeamPattern = "steady" | "pulse" | "accelerate-decelerate";
20
21interface GridBeamsProps {
22 title?: string; // Title of the grid beams
23 subtitle?: string; // Subtitle of the grid beams
24 // Animation customization props
25 baseSpeed?: number; // Base speed multiplier
26 maxBeams?: number; // Maximum number of beams
27 initialBeams?: number; // Initial number of beams
28 beamDelay?: {
29 // Delay configuration
30 min: number; // Minimum delay frames
31 max: number; // Maximum delay frames
32 };
33 defaultPattern?: BeamPattern; // Default pattern for random beams
34 patternDistribution?: {
35 // Distribution of patterns (must sum to 1)
36 steady: number; // Proportion of steady beams
37 pulse: number; // Proportion of pulsing beams
38 "accelerate-decelerate": number; // Proportion of accelerate-decelerate beams
39 };
40 colors?: string[]; // Custom colors for beams
41 gridSize?: number; // Size of grid cells in pixels
42 randomStarts?: boolean; // Whether to randomize starting positions
43 beamLength?: { min: number; max: number }; // Length of beams
44}
45
46const GridBeams: React.FC<GridBeamsProps> = ({
47 title,
48 subtitle,
49 baseSpeed = 1.0,
50 maxBeams = 14,
51 initialBeams = 10,
52 beamDelay = { min: 0, max: 200 },
53 defaultPattern,
54 patternDistribution = {
55 steady: 0.3,
56 pulse: 0.4,
57 "accelerate-decelerate": 0.3,
58 },
59 colors = ["#3B82F6", "#8B5CF6", "#EC4899", "#10B981"],
60 gridSize = 40,
61 randomStarts = true,
62 beamLength = { min: 0.5, max: 0.9 },
63}) => {
64 const canvasRef = useRef<HTMLCanvasElement>(null);
65
66 // Convert pattern name to number
67 const getPatternNumber = (pattern: BeamPattern): number => {
68 switch (pattern) {
69 case "steady":
70 return 0;
71 case "pulse":
72 return 1;
73 case "accelerate-decelerate":
74 return 2;
75 default:
76 return 0;
77 }
78 };
79
80 // Get a random pattern based on distribution
81 const getRandomPattern = (): number => {
82 if (defaultPattern) {
83 return getPatternNumber(defaultPattern);
84 }
85
86 const rand = Math.random();
87 let cumulativeProbability = 0;
88
89 if (rand < (cumulativeProbability += patternDistribution.steady)) {
90 return 0; // Steady
91 } else if (rand < (cumulativeProbability += patternDistribution.pulse)) {
92 return 1; // Pulse
93 } else {
94 return 2; // Accelerate-decelerate
95 }
96 };
97
98 useEffect(() => {
99 const canvas = canvasRef.current;
100 if (!canvas) return;
101
102 const ctx = canvas.getContext("2d");
103 if (!ctx) return;
104
105 let animationFrameId: number;
106 let frame = 0;
107
108 // Set canvas dimensions to match parent container
109 const resizeCanvas = () => {
110 if (!canvas) return;
111 canvas.width = canvas.offsetWidth;
112 canvas.height = canvas.offsetHeight;
113 };
114
115 resizeCanvas();
116 window.addEventListener("resize", resizeCanvas);
117
118 const beams: Beam[] = [];
119
120 // Create initial beams with patterns
121 const createBeam = (patternOverride?: number): Beam => {
122 const isHorizontal = Math.random() > 0.5;
123 const pattern =
124 patternOverride !== undefined ? patternOverride : getRandomPattern();
125
126 // Calculate beam length based on the beamLength prop
127 const length =
128 beamLength.min + Math.random() * (beamLength.max - beamLength.min);
129
130 // Determine starting progress based on randomStarts
131 const startProgress = randomStarts ? Math.random() * (1 - length) : 0;
132
133 return {
134 color: colors[Math.floor(Math.random() * colors.length)],
135 position:
136 Math.floor(
137 (Math.random() * (isHorizontal ? canvas.height : canvas.width)) /
138 gridSize
139 ) * gridSize,
140 progress: startProgress,
141 speed: (0.2 + Math.random() * 0.6) * baseSpeed, // Apply base speed multiplier
142 width: 2 + Math.random() * 2,
143 isHorizontal,
144 opacity: 0.5 + Math.random() * 0.5,
145 delay:
146 beamDelay.min +
147 Math.floor(Math.random() * (beamDelay.max - beamDelay.min)),
148 pattern,
149 length: length, // Store the beam length
150 };
151 };
152
153 // Create beams with specific patterns - one of each type for each direction
154 const initializePatternedBeams = () => {
155 // Horizontal beams with different patterns
156 for (let i = 0; i < 3; i++) {
157 const beam = createBeam(i);
158 beam.isHorizontal = true;
159 beam.position = gridSize * (1 + i * 2);
160 beam.delay = i * 40; // Staggered delays
161 beams.push(beam);
162 }
163
164 // Vertical beams with different patterns
165 for (let i = 0; i < 3; i++) {
166 const beam = createBeam(i);
167 beam.isHorizontal = false;
168 beam.position = gridSize * (2 + i * 3);
169 beam.delay = 30 + i * 40; // Different staggered delays
170 beams.push(beam);
171 }
172
173 // Add additional random beams up to initialBeams count
174 const additionalBeams = Math.max(0, initialBeams - 6); // We already added 6 beams
175 for (let i = 0; i < additionalBeams; i++) {
176 beams.push(createBeam());
177 }
178 };
179
180 initializePatternedBeams();
181
182 // Update beam based on its pattern
183 const updateBeamProgress = (beam: Beam) => {
184 // Don't move if still in delay period
185 if (beam.delay > 0) {
186 beam.delay--;
187 return;
188 }
189
190 let speedFactor = 1;
191
192 switch (beam.pattern) {
193 case 0: // Steady movement
194 speedFactor = 1;
195 break;
196 case 1: // Pulsing - speed varies with sine wave
197 speedFactor = 0.5 + Math.sin(frame / 50) * 0.5;
198 break;
199 case 2: // Accelerate-decelerate
200 // Slower at beginning and end, faster in the middle
201 speedFactor =
202 beam.progress < 0.5 ? beam.progress * 2 : (1 - beam.progress) * 2;
203 speedFactor = 0.3 + speedFactor * 0.7; // Keep minimum speed
204 break;
205 }
206
207 beam.progress += (beam.speed / 400) * speedFactor; // Base speed div by 400 (slower)
208 };
209
210 // Animation loop
211 const animate = () => {
212 if (!canvas || !ctx) return;
213 frame++;
214
215 ctx.clearRect(0, 0, canvas.width, canvas.height);
216
217 // Draw grid
218 ctx.strokeStyle = "rgba(229, 231, 235, 0.1)"; // Light gray with transparency
219 ctx.lineWidth = 1;
220
221 // Draw vertical grid lines
222 for (let x = 0; x <= canvas.width; x += gridSize) {
223 ctx.beginPath();
224 ctx.moveTo(x, 0);
225 ctx.lineTo(x, canvas.height);
226 ctx.stroke();
227 }
228
229 // Draw horizontal grid lines
230 for (let y = 0; y <= canvas.height; y += gridSize) {
231 ctx.beginPath();
232 ctx.moveTo(0, y);
233 ctx.lineTo(canvas.width, y);
234 ctx.stroke();
235 }
236
237 // Draw and update beams
238 beams.forEach((beam, index) => {
239 ctx.beginPath();
240 ctx.globalAlpha = beam.opacity;
241
242 // For pulsing beams, also pulse opacity
243 if (beam.pattern === 1 && beam.delay === 0) {
244 ctx.globalAlpha = beam.opacity * (0.7 + Math.sin(frame / 50) * 0.3);
245 }
246
247 ctx.strokeStyle = beam.color;
248 ctx.lineWidth = beam.width;
249
250 if (beam.isHorizontal) {
251 const startX = canvas.width * beam.progress;
252 const endX = startX + canvas.width * beam.length;
253 ctx.moveTo(startX, beam.position);
254 ctx.lineTo(Math.min(endX, canvas.width), beam.position);
255 } else {
256 const startY = canvas.height * beam.progress;
257 const endY = startY + canvas.height * beam.length;
258 ctx.moveTo(beam.position, startY);
259 ctx.lineTo(beam.position, Math.min(endY, canvas.height));
260 }
261
262 ctx.stroke();
263 ctx.globalAlpha = 1;
264
265 // Update beam progress based on pattern
266 updateBeamProgress(beam);
267
268 // Reset beam when it reaches the end (fully off screen)
269 if (beam.progress > 1) {
270 // For patterned beams, maintain their pattern
271 if (index < 6) {
272 // The first 6 are our patterned beams
273 beam.progress = randomStarts
274 ? Math.random() * (1 - beam.length)
275 : 0;
276 beam.delay =
277 beamDelay.min +
278 Math.floor(Math.random() * (beamDelay.max - beamDelay.min));
279
280 // Maybe change color
281 if (Math.random() > 0.7) {
282 beam.color = colors[Math.floor(Math.random() * colors.length)];
283 }
284 } else {
285 // Random beams can change completely
286 if (Math.random() > 0.3) {
287 beam.progress = randomStarts
288 ? Math.random() * (1 - beam.length)
289 : 0;
290 beam.delay =
291 beamDelay.min +
292 Math.floor(Math.random() * (beamDelay.max - beamDelay.min));
293 } else {
294 beams[index] = createBeam();
295 }
296 }
297 }
298 });
299
300 // Add a new random beam occasionally but keep total reasonable
301 if (beams.length < maxBeams && frame % 300 === 0) {
302 beams.push(createBeam());
303 }
304
305 animationFrameId = requestAnimationFrame(animate);
306 };
307
308 animate();
309
310 // Clean up
311 return () => {
312 window.removeEventListener("resize", resizeCanvas);
313 cancelAnimationFrame(animationFrameId);
314 };
315 }, [
316 baseSpeed,
317 maxBeams,
318 initialBeams,
319 beamDelay,
320 defaultPattern,
321 patternDistribution,
322 colors,
323 gridSize,
324 randomStarts,
325 beamLength,
326 ]);
327
328 return (
329 <div className="relative w-full h-[40vh] bg-gray-900 overflow-hidden">
330 {/* Canvas for grid and beams */}
331 <canvas ref={canvasRef} className="absolute inset-0 w-full h-[40vh]" />
332
333 {/* Overlay gradient for better text visibility */}
334 <div className="absolute inset-0 bg-gradient-to-b from-gray-900/70 to-gray-900/90"></div>
335
336 {/* Content */}
337 <div className="relative z-10 flex flex-col items-center justify-center h-full px-4 text-center">
338 <h1 className="text-4xl md:text-6xl font-bold text-white mb-4">
339 {title || "Building The Future"}
340 </h1>
341 <p className="text-xl md:text-2xl text-gray-300 max-w-2xl">
342 {subtitle ||
343 "Create something amazing with our next-generation technology platform"}
344 </p>
345 <div className="mt-8">
346 <button className="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-lg mr-4 transition-colors">
347 Get Started
348 </button>
349 <button className="bg-transparent border border-white text-white font-bold py-3 px-6 rounded-lg hover:bg-white/10 transition-colors">
350 Learn More
351 </button>
352 </div>
353 </div>
354 </div>
355 );
356};
357
358export default GridBeams;

Props

NameTypeDefaultDescription
titlestringModern SolutionsTitle of the grid beams
subtitlestringInnovative technology for tomorrow's challengesSubtitle of the grid beams
baseSpeednumber1.0Base speed of the grid beams
maxBeamsnumber14Maximum number of beams
initialBeamsnumber10Initial number of beams
beamDelayobject{ min: 0, max: 200 }Delay configuration for beams
defaultPatternstringsteadyDefault pattern for beams. Use "Steady", "Pulse", or "Accelerate-Decelerate"
patternDistributionobject{ steady: 0.3, pulse: 0.4, 'accelerate-decelerate': 0.3 }Distribution of patterns. Must sum up to 1.
colorsstring[]['#3B82F6', '#8B5CF6', '#EC4899', '#10B981']Custom colors for beams
gridSizenumber40Size of grid cells in pixels