diff --git a/css/main.css b/css/main.css
index c0bfef8..fe622ca 100644
--- a/css/main.css
+++ b/css/main.css
@@ -44,7 +44,8 @@ img {
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border-radius: var(--border-radius);
- border: 1px solid var(--glass-border);
+ border-top: 1px solid var(--glass-border);
+ border-bottom: 1px solid var(--glass-border);
box-shadow: var(--box-shadow), var(--inset-shadow);
}
diff --git a/css/matrix.css b/css/matrix.css
index f904b6a..0db27cb 100644
--- a/css/matrix.css
+++ b/css/matrix.css
@@ -12,46 +12,3 @@
filter: brightness(0.55);
}
-.matrix-dot {
- position: absolute;
- background: rgba(255, 255, 255, 0.8);
- border-radius: 50%;
- box-shadow: 0 0 6px rgba(255, 255, 255, 0.4), 0 0 12px rgba(255, 255, 255, 0.2);
- transition: all 0.3s ease;
- pointer-events: none;
-}
-
-.connection-line {
- position: absolute;
- height: 2px;
- background: linear-gradient(90deg,
- rgba(100, 149, 237, 0) 0%,
- rgba(138, 43, 226, 0.8) 50%,
- rgba(100, 149, 237, 0) 100%);
- transform-origin: left center;
- opacity: 0;
- transition: opacity 0.5s ease-in-out;
- pointer-events: none;
- z-index: -1;
- box-shadow: 0 0 4px rgba(138, 43, 226, 0.4);
-}
-
-.connection-line.active {
- opacity: 1;
- animation: connectionPulse 2s ease-in-out infinite;
-}
-
-@keyframes connectionPulse {
- 0% {
- filter: brightness(1);
- box-shadow: 0 0 4px rgba(138, 43, 226, 0.4);
- }
- 50% {
- filter: brightness(1.5);
- box-shadow: 0 0 8px rgba(138, 43, 226, 0.6), 0 0 16px rgba(100, 149, 237, 0.3);
- }
- 100% {
- filter: brightness(1);
- box-shadow: 0 0 4px rgba(138, 43, 226, 0.4);
- }
-}
\ No newline at end of file
diff --git a/css/variables.css b/css/variables.css
index 5295951..02e4a37 100644
--- a/css/variables.css
+++ b/css/variables.css
@@ -11,7 +11,7 @@
--background-color: #000000;
--glass-background: rgba(20, 20, 20, 0.125);
--glass-border: rgba(255, 255, 255, 0.18);
- --glass-shadow: rgba(31, 38, 135, 0.37);
+ --glass-shadow: rgba(31, 38, 135, 0.30);
--text-color: #ffffff;
--text-color-light: rgba(255, 255, 255, 0.8);
@@ -22,6 +22,6 @@
--border-radius: 16px;
--border-radius-large: 24px;
- --box-shadow: 0 8px 32px 0 var(--glass-shadow);
- --inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2);
+ --box-shadow: 0 8px 36px 0px var(--glass-shadow);
+ --inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
diff --git a/index.html b/index.html
index c51df74..144dadc 100644
--- a/index.html
+++ b/index.html
@@ -20,7 +20,7 @@
-
+
diff --git a/js/matrix.js b/js/matrix.js
index a47ff01..668e5ef 100644
--- a/js/matrix.js
+++ b/js/matrix.js
@@ -1,14 +1,15 @@
class MatrixBackground {
constructor() {
this.canvas = document.getElementById('matrixCanvas');
+ this.ctx = this.canvas.getContext('2d');
this.dots = [];
this.connections = [];
this.config = {
dotCount: 50,
- dotSpeed: 0.2, // pixels per frame
- connectionDuration: { min: 2000, max: 6000 }, // milliseconds
- connectionChance: 0.0002, // chance per frame per dot pair
- maxConnections: 8, // maximum simultaneous connections
+ dotSpeed: 0.2,
+ connectionDuration: { min: 2000, max: 6000 },
+ connectionChance: 0.0002,
+ maxConnections: 8,
dotSize: 3,
colors: {
dotDefault: 'rgba(255, 255, 255, 0.8)',
@@ -22,40 +23,22 @@ class MatrixBackground {
}
}
};
-
+
this.init();
}
init() {
- this.createDots();
- this.animate();
this.handleResize();
-
- // Handle window resize
+ this.animate();
window.addEventListener('resize', () => this.handleResize());
}
handleResize() {
- // Recreate dots with new screen dimensions
- // Clear previous DOM elements to avoid orphan/sticky dots
- this.clearCanvas();
+ this.canvas.width = window.innerWidth;
+ this.canvas.height = window.innerHeight;
this.createDots();
}
- clearCanvas() {
- // Remove all dot and connection elements from the canvas
- try {
- while (this.canvas.firstChild) {
- this.canvas.removeChild(this.canvas.firstChild);
- }
- } catch (e) {
- // ignore
- }
- // Reset arrays
- this.dots = [];
- this.connections = [];
- }
-
createDots() {
this.dots = [];
for (let i = 0; i < this.config.dotCount; i++) {
@@ -64,65 +47,44 @@ class MatrixBackground {
}
createDot() {
- const dot = document.createElement('div');
- dot.className = 'matrix-dot';
-
- // Random starting position within screen bounds
- const startX = Math.random() * (window.innerWidth - 20) + 10;
- const startY = Math.random() * (window.innerHeight - 20) + 10;
-
- // Random movement direction (flying in all directions)
+ const startX = Math.random() * (this.canvas.width - 20) + 10;
+ const startY = Math.random() * (this.canvas.height - 20) + 10;
+
let velocityX = 0, velocityY = 0;
let attempts = 0;
while ((Math.abs(velocityX) < 0.15 || Math.abs(velocityY) < 0.15) && attempts < 10) {
const angle = Math.random() * Math.PI * 2;
- const speed = this.config.dotSpeed * (0.8 + Math.random() * 0.4); // Ensure minimum speed
+ const speed = this.config.dotSpeed * (0.8 + Math.random() * 0.4);
velocityX = Math.cos(angle) * speed;
velocityY = Math.sin(angle) * speed;
attempts++;
}
- // Final fallback if still too low
if (Math.abs(velocityX) < 0.15) velocityX = velocityX < 0 ? -0.15 : 0.15;
if (Math.abs(velocityY) < 0.15) velocityY = velocityY < 0 ? -0.15 : 0.15;
- const dotData = {
- element: dot,
+
+ this.dots.push({
x: startX,
y: startY,
vx: velocityX,
vy: velocityY,
opacity: Math.random() * 0.3 + 0.7,
size: this.config.dotSize + Math.random() * 2,
- isConnected: false,
connectionCount: 0,
- stuckFrames: 0 // Track how long dot is stuck
- };
-
- // Set initial styles (white by default)
- dot.style.left = startX + 'px';
- dot.style.top = startY + 'px';
- dot.style.width = dotData.size + 'px';
- dot.style.height = dotData.size + 'px';
- dot.style.opacity = dotData.opacity;
- dot.style.background = this.config.colors.dotDefault;
- dot.style.boxShadow = `0 0 6px ${this.config.colors.dotDefaultGlow}, 0 0 12px rgba(255, 255, 255, 0.2)`;
-
- this.canvas.appendChild(dot);
- this.dots.push(dotData);
+ stuckFrames: 0
+ });
}
updateDots() {
- this.dots.forEach((dot, index) => {
- // Update position
+ this.dots.forEach(dot => {
dot.x += dot.vx;
dot.y += dot.vy;
- // Track if dot is stuck (velocity very low for several frames)
if (Math.abs(dot.vx) < 0.03 && Math.abs(dot.vy) < 0.03) {
- dot.stuckFrames = (dot.stuckFrames || 0) + 1;
+ dot.stuckFrames++;
} else {
dot.stuckFrames = 0;
}
- // If stuck for more than 30 frames, give a strong nudge
+
if (dot.stuckFrames > 30) {
const angle = Math.random() * Math.PI * 2;
const speed = this.config.dotSpeed * (0.8 + Math.random() * 0.4);
@@ -130,18 +92,12 @@ class MatrixBackground {
dot.vy = Math.sin(angle) * speed;
dot.stuckFrames = 0;
} else {
- // Prevent dots from getting stuck - add small random nudge if velocity is too low
- if (Math.abs(dot.vx) < 0.05) {
- dot.vx += (Math.random() - 0.5) * 0.1;
- }
- if (Math.abs(dot.vy) < 0.05) {
- dot.vy += (Math.random() - 0.5) * 0.1;
- }
+ if (Math.abs(dot.vx) < 0.05) dot.vx += (Math.random() - 0.5) * 0.1;
+ if (Math.abs(dot.vy) < 0.05) dot.vy += (Math.random() - 0.5) * 0.1;
}
- // Bounce off screen edges (consider dot size so centers stay inside)
- const maxX = window.innerWidth - dot.size;
- const maxY = window.innerHeight - dot.size;
+ const maxX = this.canvas.width - dot.size;
+ const maxY = this.canvas.height - dot.size;
if (dot.x <= 0 || dot.x >= maxX) {
dot.vx = -dot.vx;
dot.x = Math.max(0, Math.min(maxX, dot.x));
@@ -150,146 +106,96 @@ class MatrixBackground {
dot.vy = -dot.vy;
dot.y = Math.max(0, Math.min(maxY, dot.y));
}
-
- // Update DOM element position (top-left)
- dot.element.style.left = dot.x + 'px';
- dot.element.style.top = dot.y + 'px';
-
- // Update dot color based on connection status
- if (dot.connectionCount > 0 && !dot.isConnected) {
- dot.isConnected = true;
- dot.element.style.background = this.config.colors.dotConnected;
- dot.element.style.boxShadow = `0 0 6px ${this.config.colors.dotConnectedGlow}, 0 0 12px rgba(100, 149, 237, 0.3)`;
- } else if (dot.connectionCount === 0 && dot.isConnected) {
- dot.isConnected = false;
- dot.element.style.background = this.config.colors.dotDefault;
- dot.element.style.boxShadow = `0 0 6px ${this.config.colors.dotDefaultGlow}, 0 0 12px rgba(255, 255, 255, 0.2)`;
- }
});
}
+ drawDots() {
+ this.dots.forEach(dot => {
+ const isConnected = dot.connectionCount > 0;
+ this.ctx.beginPath();
+ this.ctx.arc(dot.x, dot.y, dot.size / 2, 0, Math.PI * 2);
+ this.ctx.fillStyle = isConnected ? this.config.colors.dotConnected : this.config.colors.dotDefault;
+ this.ctx.globalAlpha = dot.opacity;
+ this.ctx.shadowBlur = 6;
+ this.ctx.shadowColor = isConnected ? this.config.colors.dotConnectedGlow : this.config.colors.dotDefaultGlow;
+ this.ctx.fill();
+ });
+ this.ctx.globalAlpha = 1;
+ this.ctx.shadowBlur = 0;
+ }
+
createConnection(dot1, dot2) {
- if (this.connections.length >= this.config.maxConnections) {
- return;
- }
+ if (this.connections.length >= this.config.maxConnections) return;
- const connection = document.createElement('div');
- connection.className = 'connection-line';
-
- // Calculate center positions of dots
- const dot1Rect = dot1.element.getBoundingClientRect();
- const dot2Rect = dot2.element.getBoundingClientRect();
- const canvasRect = this.canvas.getBoundingClientRect();
- const dot1CenterX = dot1Rect.left - canvasRect.left + dot1Rect.width / 2;
- const dot1CenterY = dot1Rect.top - canvasRect.top + dot1Rect.height / 2;
- const dot2CenterX = dot2Rect.left - canvasRect.left + dot2Rect.width / 2;
- const dot2CenterY = dot2Rect.top - canvasRect.top + dot2Rect.height / 2;
+ dot1.connectionCount++;
+ dot2.connectionCount++;
- const dx = dot2CenterX - dot1CenterX;
- const dy = dot2CenterY - dot1CenterY;
- const distance = Math.sqrt(dx * dx + dy * dy);
- const angle = Math.atan2(dy, dx) * (180 / Math.PI);
-
- // Position and style the connection line from center to center
- const lineHalf = 1;
- connection.style.left = Math.round(dot1CenterX) + 'px';
- connection.style.top = Math.round(dot1CenterY - lineHalf) + 'px';
- connection.style.width = Math.round(distance) + 'px';
- connection.style.transformOrigin = '0 50%';
- connection.style.transform = `rotate(${angle}deg)`;
- connection.style.background = `linear-gradient(90deg,
- ${this.config.colors.connection.start} 0%,
- ${this.config.colors.connection.middle} 50%,
- ${this.config.colors.connection.end} 100%)`;
-
- this.canvas.appendChild(connection);
-
- // Fade in
- setTimeout(() => {
- connection.classList.add('active');
- }, 50);
-
- const connectionData = {
- element: connection,
+ this.connections.push({
dot1: dot1,
dot2: dot2,
startTime: Date.now(),
duration: Math.random() * (this.config.connectionDuration.max - this.config.connectionDuration.min) + this.config.connectionDuration.min
- };
-
- // Mark dots as connected
- dot1.connectionCount++;
- dot2.connectionCount++;
-
- this.connections.push(connectionData);
- }
-
- updateConnections() {
- this.connections.forEach((connection, index) => {
- const elapsed = Date.now() - connection.startTime;
-
- // Update connection position as dots move (using center positions)
- const dot1Rect = connection.dot1.element.getBoundingClientRect();
- const dot2Rect = connection.dot2.element.getBoundingClientRect();
- const canvasRect = this.canvas.getBoundingClientRect();
- const dot1CenterX = dot1Rect.left - canvasRect.left + dot1Rect.width / 2;
- const dot1CenterY = dot1Rect.top - canvasRect.top + dot1Rect.height / 2;
- const dot2CenterX = dot2Rect.left - canvasRect.left + dot2Rect.width / 2;
- const dot2CenterY = dot2Rect.top - canvasRect.top + dot2Rect.height / 2;
-
- const dx = dot2CenterX - dot1CenterX;
- const dy = dot2CenterY - dot1CenterY;
- const distance = Math.sqrt(dx * dx + dy * dy);
- const angle = Math.atan2(dy, dx) * (180 / Math.PI);
-
- const lineHalf = 1;
- connection.element.style.left = Math.round(dot1CenterX) + 'px';
- connection.element.style.top = Math.round(dot1CenterY - lineHalf) + 'px';
- connection.element.style.width = Math.round(distance) + 'px';
- connection.element.style.transformOrigin = '0 50%';
- connection.element.style.transform = `rotate(${angle}deg)`;
-
- // Remove expired connections
- if (elapsed > connection.duration) {
- connection.element.classList.remove('active');
-
- // Mark dots as no longer connected
- connection.dot1.connectionCount--;
- connection.dot2.connectionCount--;
-
- setTimeout(() => {
- connection.element.remove();
- }, 500); // Wait for fade out
- this.connections.splice(index, 1);
- }
});
}
+ updateConnections() {
+ this.connections = this.connections.filter(conn => {
+ const elapsed = Date.now() - conn.startTime;
+ if (elapsed > conn.duration) {
+ conn.dot1.connectionCount--;
+ conn.dot2.connectionCount--;
+ return false;
+ }
+ return true;
+ });
+ }
+
+ drawConnections() {
+ this.connections.forEach(conn => {
+ const { dot1, dot2, startTime, duration } = conn;
+ const elapsed = Date.now() - startTime;
+ const opacity = Math.min(1, elapsed / 500); // Fade in
+
+ const gradient = this.ctx.createLinearGradient(dot1.x, dot1.y, dot2.x, dot2.y);
+ gradient.addColorStop(0, this.config.colors.connection.start);
+ gradient.addColorStop(0.5, this.config.colors.connection.middle);
+ gradient.addColorStop(1, this.config.colors.connection.end);
+
+ this.ctx.beginPath();
+ this.ctx.moveTo(dot1.x, dot1.y);
+ this.ctx.lineTo(dot2.x, dot2.y);
+
+ this.ctx.strokeStyle = gradient;
+ this.ctx.lineWidth = 2;
+ this.ctx.globalAlpha = opacity;
+
+ // Pulsing effect
+ const pulse = (Math.sin((elapsed / 2000) * Math.PI * 2) + 1) / 2; // 2-second pulse cycle
+ this.ctx.shadowBlur = 4 + pulse * 6;
+ this.ctx.shadowColor = 'rgba(138, 43, 226, 0.4)';
+
+ this.ctx.stroke();
+ });
+ this.ctx.globalAlpha = 1;
+ this.ctx.shadowBlur = 0;
+ }
+
tryCreateConnections() {
- // Randomly try to create connections between nearby dots
for (let i = 0; i < this.dots.length; i++) {
for (let j = i + 1; j < this.dots.length; j++) {
if (Math.random() < this.config.connectionChance) {
const dot1 = this.dots[i];
const dot2 = this.dots[j];
-
- // Calculate distance between dot centers
- const dot1CenterX = dot1.x + dot1.size / 2;
- const dot1CenterY = dot1.y + dot1.size / 2;
- const dot2CenterX = dot2.x + dot2.size / 2;
- const dot2CenterY = dot2.y + dot2.size / 2;
-
- const dx = dot2CenterX - dot1CenterX;
- const dy = dot2CenterY - dot1CenterY;
+
+ const dx = dot2.x - dot1.x;
+ const dy = dot2.y - dot1.y;
const distance = Math.sqrt(dx * dx + dy * dy);
-
+
if (distance < 150 && distance > 30) {
- // Check if these dots are already connected
- const alreadyConnected = this.connections.some(conn =>
+ const alreadyConnected = this.connections.some(conn =>
(conn.dot1 === dot1 && conn.dot2 === dot2) ||
(conn.dot1 === dot2 && conn.dot2 === dot1)
);
-
if (!alreadyConnected) {
this.createConnection(dot1, dot2);
}
@@ -300,15 +206,19 @@ class MatrixBackground {
}
animate() {
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
+
this.updateDots();
this.updateConnections();
this.tryCreateConnections();
-
+
+ this.drawConnections();
+ this.drawDots();
+
requestAnimationFrame(() => this.animate());
}
}
-// Initialize the matrix background when the DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
new MatrixBackground();
-});
+});
\ No newline at end of file