"use strict"; function create_night(canvas_element) { const context = canvas_element.getContext("2d"); const star_range = 0.7; /* Variation in star size */ const star_spread = 0.002; /* Star density per pixel */ const concentration_corner = "top-left"; /* Concentration corner position */ const concentration_strength = 5.0; /* How much should it be concentrated? */ const twinkle_intensity = 5.0; /* How noticeable should the twinkle be? */ const interaction_radius = 150; /* Radius around mouse/touch where stars react */ const interaction_brightness_factor = 5.0; /* How much brightness increases */ const interaction_size_factor = 1.5; /* How much the stars should grow in size (only during interaction) */ const magic = [ 16.31189, 32454.4619, 9371.1474, 6848, 1544, 6848, 2156, ]; /* Just some magic numbers */ /* Seeded "random" number */ function seeded_random(seed) { const value = Math.sin(seed) * 10000; return value - Math.floor(value); } let stars = []; let time = 0; let mouse_x = -1, mouse_y = -1; /* Determine corner position for concentration */ function get_corner(width, height) { switch (concentration_corner) { case "top-right": return { x: width, y: 0 }; case "bottom-left": return { x: 0, y: height }; case "bottom-right": return { x: width, y: height }; default: return { x: 0, y: 0 }; /* top-left */ } } /* Function to generate star shape */ function draw_star(x, y, radius, alpha) { const num_points = 5 + Math.floor(Math.random() * 3); const angle = Math.PI / num_points; context.beginPath(); for (let idx = 0; idx < num_points; idx++) { const angle_offset = (idx * 2 * Math.PI) / num_points; const outer_x = x + radius * Math.cos(angle_offset); const outer_y = y + radius * Math.sin(angle_offset); context.lineTo(outer_x, outer_y); /* Inner points for star shape */ const inner_radius = radius / 2 + Math.random() * 0.2; const inner_x = x + inner_radius * Math.cos(angle_offset + angle); const inner_y = y + inner_radius * Math.sin(angle_offset + angle); context.lineTo(inner_x, inner_y); } context.closePath(); context.fillStyle = `rgba(255, 255, 180, ${alpha})`; context.fill(); } /* Function to generate star background */ function generate_stars() { const { width, height } = canvas_element; const star_count = Math.floor(width * height * star_spread); const corner = get_corner(width, height); stars = []; for (let idx = 0; idx < star_count; idx++) { const base_random = seeded_random(idx * magic[0]); /* Uniform random position */ const raw_x = seeded_random(base_random * magic[1]) * width; const raw_y = seeded_random(base_random * magic[2]) * height; /* Calculate distance from the corner to control concentration */ const dx = raw_x - corner.x; const dy = raw_y - corner.y; const distance = Math.sqrt(dx * dx + dy * dy); /* Apply exponential falloff based on distance (closer = more likely to be drawn) */ const max_distance = Math.sqrt(width * width + height * height); const weight = Math.exp( -concentration_strength * (distance / max_distance), ); /* Skip stars that don't meet the concentration threshold */ if (seeded_random(base_random * magic[3]) > weight) { continue; } /* Randomized star properties */ const radius = seeded_random(base_random * magic[4]) * star_range + 0.4 + Math.random() * 0.2; const twinkle_speed = seeded_random(base_random * magic[5]) * 1.5 + 0.3; const twinkle_phase = seeded_random(base_random * magic[6]) * Math.PI * 2; stars.push({ x: raw_x, y: raw_y, radius, twinkle_speed, twinkle_phase, original_radius: radius, }); } } /* Resize canvas and regenerate stars on window resize */ function resize_canvas() { canvas_element.width = window.innerWidth; canvas_element.height = window.innerHeight; generate_stars(); } window.addEventListener("resize", resize_canvas); resize_canvas(); /* Handle mouse/touch movement effects */ function handle_interaction(event) { if (event.touches) { mouse_x = event.touches[0].clientX; mouse_y = event.touches[0].clientY; } else { mouse_x = event.clientX; mouse_y = event.clientY; } } window.addEventListener("mousemove", handle_interaction); window.addEventListener("touchmove", handle_interaction); /* Animate stars (twinke) */ function animate() { time += 0.01; const { width, height } = canvas_element; context.fillStyle = "#0e0016"; context.fillRect(0, 0, width, height); for (const star of stars) { const twinkle = Math.sin( time * star.twinkle_speed + star.twinkle_phase, ); let alpha = 0.6 + twinkle_intensity * (0.5 + 0.5 * twinkle); /* Interaction effect based on mouse/touch proximity */ if (mouse_x !== -1 && mouse_y !== -1) { const dx = mouse_x - star.x; const dy = mouse_y - star.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < interaction_radius) { const effect = Math.max( 0, 1 - distance / interaction_radius, ); alpha *= 1 + effect * interaction_brightness_factor; /* Make the star brighter */ star.radius = star.original_radius * (1 + effect * interaction_size_factor); /* Grow the star size */ } } draw_star(star.x, star.y, star.radius, alpha); } requestAnimationFrame(animate); } animate(); } function base_main() { const night = document.getElementById("night"); create_night(night); } document.addEventListener("DOMContentLoaded", () => { base_main(); }); document.addEventListener("DOMContentLoaded", () => { const toggle = document.querySelector(".mobile-nav-toggle"); const navbar = document.querySelector(".navbar"); if (toggle && navbar) { toggle.addEventListener("click", () => { navbar.classList.toggle("open"); }); } });