fixes lol
All checks were successful
Build and Push / build-all (push) Successful in 2m26s

This commit is contained in:
doomtube 2026-01-11 17:57:59 -05:00
parent 1b1d54315c
commit cef4707307
4 changed files with 232 additions and 13 deletions

View file

@ -4,11 +4,13 @@
import { screensaver, isScreensaverActive, activeScreensaverType } from '$lib/stores/screensaver'; import { screensaver, isScreensaverActive, activeScreensaverType } from '$lib/stores/screensaver';
import Snowfall from './screensavers/Snowfall.svelte'; import Snowfall from './screensavers/Snowfall.svelte';
import FractalCrystalline from './screensavers/FractalCrystalline.svelte'; import FractalCrystalline from './screensavers/FractalCrystalline.svelte';
import MandelbrotZoom from './screensavers/MandelbrotZoom.svelte';
// Map type to component // Map type to component
const screensaverComponents = { const screensaverComponents = {
snowfall: Snowfall, snowfall: Snowfall,
fractal_crystalline: FractalCrystalline fractal_crystalline: FractalCrystalline,
mandelbrot_zoom: MandelbrotZoom
}; };
// Dismiss on any interaction // Dismiss on any interaction

View file

@ -24,7 +24,7 @@
maxBranches: 800, // Max simultaneous branch tips maxBranches: 800, // Max simultaneous branch tips
branchChance: 0.03, // Chance to spawn new branch per frame branchChance: 0.03, // Chance to spawn new branch per frame
turnAngle: 0.3, // Max random turn per frame (radians) turnAngle: 0.3, // Max random turn per frame (radians)
hueShiftSpeed: 0.2, // Color cycling speed hueShiftSpeed: 0.5, // Color cycling speed (faster for more color variety)
maxPoints: 15000, // Max crystal points (kept for reference) maxPoints: 15000, // Max crystal points (kept for reference)
shatterDuration: 2500, // Milliseconds for shatter effect shatterDuration: 2500, // Milliseconds for shatter effect
lineWidth: 2 lineWidth: 2
@ -112,11 +112,14 @@
ctx.fillRect(0, 0, width, height); ctx.fillRect(0, 0, width, height);
} }
// Create seed points at random locations // Create seed points at random locations with spread colors
for (let i = 0; i < CONFIG.seedCount; i++) { for (let i = 0; i < CONFIG.seedCount; i++) {
const x = Math.random() * width; const x = Math.random() * width;
const y = Math.random() * height; const y = Math.random() * height;
// Base hue for this seed - spread evenly across spectrum
const seedHue = (i * 360 / CONFIG.seedCount) + Math.random() * 30;
// Each seed spawns multiple branches in different directions // Each seed spawns multiple branches in different directions
const branchCount = 3 + Math.floor(Math.random() * 4); const branchCount = 3 + Math.floor(Math.random() * 4);
for (let j = 0; j < branchCount; j++) { for (let j = 0; j < branchCount; j++) {
@ -125,7 +128,7 @@
x, x,
y, y,
angle, angle,
hue: Math.random() * 360, hue: (seedHue + j * 25 + Math.random() * 20) % 360, // Each branch gets varied hue
age: 0, age: 0,
generation: 0 generation: 0
}); });
@ -178,9 +181,11 @@
hue: (branch.hue + branch.age * 0.5) % 360 hue: (branch.hue + branch.age * 0.5) % 360
}); });
// Draw the branch segment // Draw the branch segment with varied colors
const h = (branch.hue + branch.age * 0.5) % 360; const h = (branch.hue + branch.age * 1.5) % 360; // Faster hue shift
ctx.strokeStyle = `hsla(${h}, 80%, 60%, 0.9)`; const s = 70 + Math.sin(branch.age * 0.1) * 20; // Saturation varies 50-90%
const l = 50 + Math.cos(branch.age * 0.05) * 15; // Lightness varies 35-65%
ctx.strokeStyle = `hsla(${h}, ${s}%, ${l}%, 0.9)`;
ctx.lineWidth = CONFIG.lineWidth; ctx.lineWidth = CONFIG.lineWidth;
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(branch.x, branch.y); ctx.moveTo(branch.x, branch.y);
@ -192,14 +197,14 @@
branch.y = newY; branch.y = newY;
branch.age++; branch.age++;
// Chance to spawn a new branch (fork) // Chance to spawn a new branch (fork) with distinct color
if (Math.random() < CONFIG.branchChance && branches.length + newBranches.length < CONFIG.maxBranches) { if (Math.random() < CONFIG.branchChance && branches.length + newBranches.length < CONFIG.maxBranches) {
const forkAngle = branch.angle + (Math.random() > 0.5 ? 1 : -1) * (0.3 + Math.random() * 0.7); const forkAngle = branch.angle + (Math.random() > 0.5 ? 1 : -1) * (0.3 + Math.random() * 0.7);
newBranches.push({ newBranches.push({
x: newX, x: newX,
y: newY, y: newY,
angle: forkAngle, angle: forkAngle,
hue: (branch.hue + 20 + Math.random() * 40) % 360, hue: (branch.hue + 30 + Math.random() * 90) % 360, // More color variation
age: 0, age: 0,
generation: branch.generation + 1 generation: branch.generation + 1
}); });
@ -228,6 +233,7 @@
// Only spawn if we found an unoccupied spot // Only spawn if we found an unoccupied spot
if (!isOccupied(x, y)) { if (!isOccupied(x, y)) {
const seedHue = Math.random() * 360; // Random base hue for new seed
const branchCount = 2 + Math.floor(Math.random() * 3); const branchCount = 2 + Math.floor(Math.random() * 3);
for (let j = 0; j < branchCount; j++) { for (let j = 0; j < branchCount; j++) {
const angle = Math.random() * Math.PI * 2; const angle = Math.random() * Math.PI * 2;
@ -235,7 +241,7 @@
x, x,
y, y,
angle, angle,
hue: Math.random() * 360, hue: (seedHue + j * 40 + Math.random() * 30) % 360, // Varied colors
age: 0, age: 0,
generation: 0 generation: 0
}); });

View file

@ -0,0 +1,209 @@
<script>
import { onMount, onDestroy } from 'svelte';
import { browser } from '$app/environment';
let canvas;
let ctx;
let animationId;
let width, height;
// Mandelbrot parameters
let centerX = -0.743643887037151; // Interesting zoom point
let centerY = 0.131825904205330;
let zoom = 1;
let zoomSpeed = 1.02; // Zoom multiplier per frame
// Alternative interesting points to cycle through
const interestingPoints = [
{ x: -0.743643887037151, y: 0.131825904205330 }, // Seahorse valley
{ x: -0.74529, y: 0.113075 }, // Spiral
{ x: -0.16, y: 1.0405 }, // Branch
{ x: -1.25066, y: 0.02012 }, // Mini mandelbrot
{ x: -0.235125, y: 0.827215 }, // Tendril
];
let currentPointIndex = 0;
const CONFIG = {
maxIterations: 150,
zoomResetThreshold: 1e12, // Reset zoom after this level
colorCycles: 3 // Number of color cycles in the gradient
};
function initCanvas() {
if (!canvas) return;
width = window.innerWidth;
height = window.innerHeight;
canvas.width = width;
canvas.height = height;
ctx = canvas.getContext('2d');
}
function resetZoom() {
// Pick next interesting point
currentPointIndex = (currentPointIndex + 1) % interestingPoints.length;
const point = interestingPoints[currentPointIndex];
centerX = point.x;
centerY = point.y;
zoom = 1;
}
// Gold color palette
function getGoldColor(iteration, maxIter) {
if (iteration === maxIter) return { r: 0, g: 0, b: 0 }; // Black for inside
const t = iteration / maxIter;
const cycled = (t * CONFIG.colorCycles) % 1;
// Gold gradient: dark brown -> gold -> bright gold -> white gold
let r, g, b;
if (cycled < 0.25) {
// Dark brown to gold
const s = cycled / 0.25;
r = Math.floor(40 + s * 175); // 40 -> 215
g = Math.floor(20 + s * 155); // 20 -> 175
b = Math.floor(5 + s * 30); // 5 -> 35
} else if (cycled < 0.5) {
// Gold to bright gold
const s = (cycled - 0.25) / 0.25;
r = Math.floor(215 + s * 40); // 215 -> 255
g = Math.floor(175 + s * 40); // 175 -> 215
b = Math.floor(35 + s * 65); // 35 -> 100
} else if (cycled < 0.75) {
// Bright gold to white gold
const s = (cycled - 0.5) / 0.25;
r = 255;
g = Math.floor(215 + s * 30); // 215 -> 245
b = Math.floor(100 + s * 100); // 100 -> 200
} else {
// White gold back to dark brown
const s = (cycled - 0.75) / 0.25;
r = Math.floor(255 - s * 215); // 255 -> 40
g = Math.floor(245 - s * 225); // 245 -> 20
b = Math.floor(200 - s * 195); // 200 -> 5
}
return { r, g, b };
}
function mandelbrot(cx, cy, maxIter) {
let x = 0, y = 0;
let iteration = 0;
while (x * x + y * y <= 4 && iteration < maxIter) {
const xTemp = x * x - y * y + cx;
y = 2 * x * y + cy;
x = xTemp;
iteration++;
}
// Smooth coloring
if (iteration < maxIter) {
const logZn = Math.log(x * x + y * y) / 2;
const nu = Math.log(logZn / Math.log(2)) / Math.log(2);
iteration = iteration + 1 - nu;
}
return iteration;
}
function render() {
if (!ctx) return;
const imageData = ctx.createImageData(width, height);
const data = imageData.data;
// Calculate view bounds
const aspectRatio = width / height;
const viewWidth = 4 / zoom;
const viewHeight = viewWidth / aspectRatio;
const minX = centerX - viewWidth / 2;
const maxX = centerX + viewWidth / 2;
const minY = centerY - viewHeight / 2;
const maxY = centerY + viewHeight / 2;
// Adaptive iteration count based on zoom
const iterations = Math.min(CONFIG.maxIterations + Math.log2(zoom) * 20, 500);
// Render with reduced resolution for performance, then scale
const scale = zoom > 1000 ? 2 : 1; // Lower res at high zoom for performance
for (let py = 0; py < height; py += scale) {
for (let px = 0; px < width; px += scale) {
const x0 = minX + (px / width) * (maxX - minX);
const y0 = minY + (py / height) * (maxY - minY);
const iter = mandelbrot(x0, y0, iterations);
const color = getGoldColor(iter, iterations);
// Fill pixels (handle scaling)
for (let dy = 0; dy < scale && py + dy < height; dy++) {
for (let dx = 0; dx < scale && px + dx < width; dx++) {
const idx = ((py + dy) * width + (px + dx)) * 4;
data[idx] = color.r;
data[idx + 1] = color.g;
data[idx + 2] = color.b;
data[idx + 3] = 255;
}
}
}
}
ctx.putImageData(imageData, 0, 0);
// Update zoom
zoom *= zoomSpeed;
// Reset if zoomed too far
if (zoom > CONFIG.zoomResetThreshold) {
resetZoom();
}
}
function startAnimation() {
function loop() {
render();
animationId = requestAnimationFrame(loop);
}
loop();
}
function handleResize() {
initCanvas();
}
onMount(() => {
if (!browser) return;
// Randomly pick starting point
currentPointIndex = Math.floor(Math.random() * interestingPoints.length);
const point = interestingPoints[currentPointIndex];
centerX = point.x;
centerY = point.y;
initCanvas();
startAnimation();
window.addEventListener('resize', handleResize);
});
onDestroy(() => {
if (animationId) {
cancelAnimationFrame(animationId);
}
if (browser) {
window.removeEventListener('resize', handleResize);
}
});
</script>
<canvas bind:this={canvas} class="mandelbrot-canvas"></canvas>
<style>
.mandelbrot-canvas {
position: absolute;
inset: 0;
pointer-events: none;
}
</style>

View file

@ -93,9 +93,11 @@ function createScreensaverStore() {
// Check if idle time exceeds timeout // Check if idle time exceeds timeout
if (newIdleTime >= state.timeoutMinutes * 60) { if (newIdleTime >= state.timeoutMinutes * 60) {
// Resolve random type at activation time // Resolve random type at activation time
const activeType = state.type === 'random' let activeType = state.type;
? (Math.random() < 0.5 ? 'snowfall' : 'fractal_crystalline') if (state.type === 'random') {
: state.type; const types = ['snowfall', 'fractal_crystalline', 'mandelbrot_zoom'];
activeType = types[Math.floor(Math.random() * types.length)];
}
return { ...newState, active: true, activeType }; return { ...newState, active: true, activeType };
} }