UNPKG

peepee

Version:

Visual Programming Language Where You Connect Ports Of One EventEmitter to Ports Of Another EventEmitter

891 lines (743 loc) 27.2 kB
export class ColorMath { static goldenRatio = 1.618033988749; static goldenAngle = 137.507764; // 360 / φ static clamp(value, min = 0, max = 255) { return Math.max(min, Math.min(max, value)); } static hexToRgb(hex) { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16), } : null; } static rgbToHex(r, g, b) { return "#" + ((1 << 24) + (parseInt(r) << 16) + (parseInt(g) << 8) + parseInt(b)).toString(16).slice(1); } static rgbToHsl(r, g, b) { r /= 255; g /= 255; b /= 255; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let h, s, l = (max + min) / 2; if (max === min) { h = s = 0; } else { const d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } return { h: h * 360, s: s * 100, l: l * 100 }; } static hslToRgb(h, s, l) { h = h / 360; s = s / 100; l = l / 100; const hue2rgb = (p, q, t) => { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; }; let r, g, b; if (s === 0) { r = g = b = l; } else { const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; r = hue2rgb(p, q, h + 1 / 3); g = hue2rgb(p, q, h); b = hue2rgb(p, q, h - 1 / 3); } return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255), }; } static fibonacci(n) { if (n <= 1) return n; let a = 0, b = 1; for (let i = 2; i <= n; i++) { [a, b] = [b, a + b]; } return b; } } export class ColorFilters { constructor() { this.startTime = Date.now(); this.time = 0; } updateTime() { this.time = (Date.now() - this.startTime) * 0.001; } // ====================== // SIMPLE FILTERS // ====================== simpleWarm(r, g, b) { return { r: ColorMath.clamp(r + 15), g: ColorMath.clamp(g + 5), b: ColorMath.clamp(b - 10), }; } simpleCool(r, g, b) { return { r: ColorMath.clamp(r - 10), g: ColorMath.clamp(g + 5), b: ColorMath.clamp(b + 15), }; } simpleVintage(r, g, b) { return { r: ColorMath.clamp(r * 0.9 + 20), g: ColorMath.clamp(g * 0.85 + 15), b: ColorMath.clamp(b * 0.7 + 10), }; } simpleNeon(r, g, b) { return { r: ColorMath.clamp(r * 1.2), g: ColorMath.clamp(g * 1.1), b: ColorMath.clamp(b * 1.15), }; } simpleChromatic(r, g, b) { return { r: ColorMath.clamp(r + 15), g: g, b: ColorMath.clamp(b - 10), }; } // ====================== // NATURE - DARK ATMOSPHERES // ====================== darkOceanDepths(baseColor, depth = 0.5) { this.updateTime(); const pressure = Math.pow(depth, 2); const currentFlow = Math.sin(this.time * 0.3 + depth * 2) * 0.1 + 0.9; const bioLuminescence = Math.sin(this.time * 0.8) * 0.05 * (1 - depth); return { r: ColorMath.clamp(baseColor.r * (0.1 + pressure * 0.2) * currentFlow), g: ColorMath.clamp(baseColor.g * (0.3 + pressure * 0.1) * currentFlow + bioLuminescence * 30), b: ColorMath.clamp(baseColor.b * (0.8 - pressure * 0.3) * currentFlow + bioLuminescence * 80), }; } nightLakeReflection(baseColor, stillness = 0.5) { this.updateTime(); const ripple = Math.sin(this.time * 0.5 + stillness * 3) * (1 - stillness) * 0.15; const moonlight = Math.cos(this.time * 0.2) * 0.1 + 0.9; const reflection = 0.6 + ripple; return { r: ColorMath.clamp(baseColor.r * 0.4 * reflection * moonlight), g: ColorMath.clamp(baseColor.g * 0.5 * reflection * moonlight), b: ColorMath.clamp(baseColor.b * 0.9 * reflection * moonlight + 20), }; } foxfire(baseColor, intensity = 0.5) { this.updateTime(); const flicker = Math.sin(this.time * 4 + intensity * 2) * 0.3 + 0.7; const glow = Math.cos(this.time * 2.5) * 0.2 + 0.8; const phosphorescence = intensity * flicker * glow; return { r: ColorMath.clamp(baseColor.r * 0.2 + phosphorescence * 50), g: ColorMath.clamp(baseColor.g * 0.3 + phosphorescence * 150), b: ColorMath.clamp(baseColor.b * 0.1 + phosphorescence * 30), }; } moonlessForest(baseColor, density = 0.5) { this.updateTime(); const shadow = Math.pow(density, 1.5); const whisper = Math.sin(this.time * 0.1 + density * 4) * 0.05 + 0.95; const mystery = Math.cos(this.time * 0.3) * 0.02 + 0.98; return { r: ColorMath.clamp(baseColor.r * (0.15 - shadow * 0.1) * whisper), g: ColorMath.clamp(baseColor.g * (0.25 - shadow * 0.05) * whisper * mystery), b: ColorMath.clamp(baseColor.b * (0.35 - shadow * 0.15) * mystery), }; } deepCaveAmbient(baseColor, depth = 0.5) { this.updateTime(); const echo = Math.sin(this.time * 0.2 + depth * 5) * 0.03 + 0.97; const minerals = Math.cos(this.time * 0.15) * 0.02 + 0.98; const dampness = 0.9 - depth * 0.3; return { r: ColorMath.clamp(baseColor.r * 0.1 * echo * dampness), g: ColorMath.clamp(baseColor.g * 0.2 * minerals * dampness), b: ColorMath.clamp(baseColor.b * 0.3 * echo * minerals), }; } // ====================== // GAMING - NIGHT EFFECTS // ====================== terminatorVision(baseColor, scanline = 0.5) { this.updateTime(); const scan = Math.sin(this.time * 8 + scanline * 10) * 0.2 + 0.8; const targeting = Math.cos(this.time * 12) * 0.1 + 0.9; const hud = Math.sin(this.time * 6) * 0.05 + 0.95; return { r: ColorMath.clamp(baseColor.r * 1.2 * scan * targeting), g: ColorMath.clamp(baseColor.g * 0.3 * hud), b: ColorMath.clamp(baseColor.b * 0.1 * scan), }; } predatorThermal(baseColor, heatSignature = 0.5) { this.updateTime(); const thermal = Math.sin(this.time * 3 + heatSignature * 5) * 0.3 + 0.7; const interference = Math.cos(this.time * 7) * 0.1 + 0.9; const signature = heatSignature * thermal; return { r: ColorMath.clamp(baseColor.r * 0.2 + signature * 200 * interference), g: ColorMath.clamp(baseColor.g * 0.1 + signature * 100), b: ColorMath.clamp(baseColor.b * 0.05 + signature * 50), }; } nightVisionGoggles(baseColor, amplification = 0.5) { this.updateTime(); const grain = Math.sin(this.time * 20) * 0.05 + 0.95; const boost = 1 + amplification * 2; const greenFilter = Math.cos(this.time * 0.5) * 0.1 + 0.9; return { r: ColorMath.clamp(baseColor.r * 0.1 * grain), g: ColorMath.clamp(baseColor.g * boost * greenFilter * grain), b: ColorMath.clamp(baseColor.b * 0.2 * grain), }; } cyberpunkNeon(baseColor, voltage = 0.5) { this.updateTime(); const flicker = Math.sin(this.time * 15 + voltage * 8) * 0.2 + 0.8; const buzz = Math.cos(this.time * 25) * 0.1 + 0.9; const neon = voltage * flicker * buzz; return { r: ColorMath.clamp(baseColor.r * 0.3 + neon * 150), g: ColorMath.clamp(baseColor.g * 0.2 + neon * 100), b: ColorMath.clamp(baseColor.b * 0.1 + neon * 200), }; } ghostlySpectral(baseColor, manifestation = 0.5) { this.updateTime(); const phase = Math.sin(this.time * 1.5 + manifestation * 3) * 0.4 + 0.6; const ethereal = Math.cos(this.time * 2.2) * 0.3 + 0.7; const spectral = manifestation * phase * ethereal; return { r: ColorMath.clamp(baseColor.r * 0.4 * spectral), g: ColorMath.clamp(baseColor.g * 0.6 * spectral), b: ColorMath.clamp(baseColor.b * 0.9 * spectral + 40), }; } // ====================== // UNIVERSE - DEEP SPACE // ====================== nebulaDust(baseColor, density = 0.5) { this.updateTime(); const drift = Math.sin(this.time * 0.1 + density * 2) * 0.2 + 0.8; const stellar = Math.cos(this.time * 0.3) * 0.1 + 0.9; const cosmic = density * drift * stellar; return { r: ColorMath.clamp(baseColor.r * 0.6 * cosmic + 30), g: ColorMath.clamp(baseColor.g * 0.4 * cosmic + 10), b: ColorMath.clamp(baseColor.b * 0.8 * cosmic + 60), }; } plutoAtmosphere(baseColor, altitude = 0.5) { this.updateTime(); const methane = Math.sin(this.time * 0.2 + altitude * 3) * 0.1 + 0.9; const nitrogen = Math.cos(this.time * 0.15) * 0.05 + 0.95; const frozen = 0.3 - altitude * 0.2; return { r: ColorMath.clamp(baseColor.r * frozen * methane), g: ColorMath.clamp(baseColor.g * (frozen + 0.1) * nitrogen), b: ColorMath.clamp(baseColor.b * (frozen + 0.2) * methane * nitrogen), }; } blackHoleAccretion(baseColor, eventHorizon = 0.5) { this.updateTime(); const gravity = Math.pow(eventHorizon, 2); const redshift = Math.sin(this.time * 0.8 + eventHorizon * 4) * 0.3 + 0.7; const spacetime = Math.cos(this.time * 1.2) * 0.2 + 0.8; return { r: ColorMath.clamp(baseColor.r * (1 - gravity * 0.8) * redshift), g: ColorMath.clamp(baseColor.g * (1 - gravity * 0.9) * spacetime), b: ColorMath.clamp(baseColor.b * (1 - gravity * 0.7) * redshift * spacetime), }; } voidCold(baseColor, emptiness = 0.5) { this.updateTime(); const absolute = Math.pow(emptiness, 3); const quantum = Math.sin(this.time * 0.05) * 0.01 + 0.99; const void_ = absolute * quantum; return { r: ColorMath.clamp(baseColor.r * (0.05 + void_ * 0.1)), g: ColorMath.clamp(baseColor.g * (0.08 + void_ * 0.12)), b: ColorMath.clamp(baseColor.b * (0.12 + void_ * 0.15)), }; } cosmicRadiation(baseColor, intensity = 0.5) { this.updateTime(); const gamma = Math.sin(this.time * 5 + intensity * 6) * 0.3 + 0.7; const particle = Math.cos(this.time * 8) * 0.2 + 0.8; const radiation = intensity * gamma * particle; return { r: ColorMath.clamp(baseColor.r * 0.3 + radiation * 80), g: ColorMath.clamp(baseColor.g * 0.4 + radiation * 120), b: ColorMath.clamp(baseColor.b * 0.2 + radiation * 180), }; } // ====================== // ATMOSPHERIC EFFECTS // ====================== atmosphericScatter(baseColor, scatterAmount = 0.3) { const r = baseColor.r * (1 - scatterAmount * 0.8); const g = baseColor.g * (1 - scatterAmount * 0.6); const b = baseColor.b * (1 - scatterAmount * 0.2); return { r: ColorMath.clamp(r), g: ColorMath.clamp(g), b: ColorMath.clamp(b), }; } sunsetGradient(baseColor, elevation = 0.5) { const sunsetFactor = Math.sin(elevation * Math.PI); const orangeShift = sunsetFactor * 0.8; const redShift = sunsetFactor * 0.6; return { r: ColorMath.clamp(baseColor.r + orangeShift * 100 + redShift * 50), g: ColorMath.clamp(baseColor.g + orangeShift * 60 - redShift * 30), b: ColorMath.clamp(baseColor.b - orangeShift * 80 - redShift * 100), }; } mistEffect(baseColor, density = 0.4) { const mistFactor = 1 - density; const whiteBlend = density * 0.7; return { r: ColorMath.clamp(baseColor.r * mistFactor + 255 * whiteBlend), g: ColorMath.clamp(baseColor.g * mistFactor + 255 * whiteBlend), b: ColorMath.clamp(baseColor.b * mistFactor + 255 * whiteBlend), }; } // ====================== // OPTICAL PHENOMENA // ====================== chromaticAberration(baseColor, intensity = 0.1) { return { r: ColorMath.clamp(baseColor.r + intensity * 50), g: baseColor.g, b: ColorMath.clamp(baseColor.b - intensity * 30), }; } iridescence(baseColor, angle = 0, intensity = 0.8) { this.updateTime(); const shimmer = Math.sin(angle + this.time * 2) * intensity; const hueShift = shimmer * 60; // Convert to HSL-like manipulation const max = Math.max(baseColor.r, baseColor.g, baseColor.b); const min = Math.min(baseColor.r, baseColor.g, baseColor.b); const delta = max - min; if (delta === 0) return baseColor; let h = 0; if (max === baseColor.r) h = ((baseColor.g - baseColor.b) / delta) % 6; else if (max === baseColor.g) h = (baseColor.b - baseColor.r) / delta + 2; else h = (baseColor.r - baseColor.g) / delta + 4; h = (h * 60 + hueShift) % 360; if (h < 0) h += 360; const l = (max + min) / 2 / 255; const s = delta === 0 ? 0 : delta / (255 - Math.abs(2 * l * 255 - 255)); return ColorMath.hslToRgb(h, s * 100, l * 100); } oilSlick(baseColor, thickness = 0.5) { this.updateTime(); const interference = Math.sin(thickness * 10 + this.time * 3) * 0.5 + 0.5; const hueShift = interference * 180 + 180; return ColorMath.hslToRgb(hueShift, 80 + interference * 20, 30 + interference * 40); } soapBubble(baseColor, surfaceAngle = 0) { this.updateTime(); const t = this.time * 0.5 + surfaceAngle; const r = Math.sin(t) * 0.5 + 0.5; const g = Math.sin(t + 2.094) * 0.5 + 0.5; // 2π/3 const b = Math.sin(t + 4.188) * 0.5 + 0.5; // 4π/3 return { r: ColorMath.clamp(r * 255 * 0.8 + baseColor.r * 0.2), g: ColorMath.clamp(g * 255 * 0.8 + baseColor.g * 0.2), b: ColorMath.clamp(b * 255 * 0.8 + baseColor.b * 0.2), }; } prismDispersion(wavelength) { let r = 0, g = 0, b = 0; if (wavelength >= 380 && wavelength <= 440) { r = -(wavelength - 440) / (440 - 380); b = 1.0; } else if (wavelength >= 440 && wavelength <= 490) { g = (wavelength - 440) / (490 - 440); b = 1.0; } else if (wavelength >= 490 && wavelength <= 510) { g = 1.0; b = -(wavelength - 510) / (510 - 490); } else if (wavelength >= 510 && wavelength <= 580) { r = (wavelength - 510) / (580 - 510); g = 1.0; } else if (wavelength >= 580 && wavelength <= 645) { r = 1.0; g = -(wavelength - 645) / (645 - 580); } else if (wavelength >= 645 && wavelength <= 750) { r = 1.0; } return { r: ColorMath.clamp(r * 255), g: ColorMath.clamp(g * 255), b: ColorMath.clamp(b * 255), }; } // ====================== // CELESTIAL EFFECTS // ====================== sunlightTransform(baseColor, timeOfDay = 0.5) { // 0 = midnight, 0.25 = sunrise, 0.5 = noon, 0.75 = sunset, 1 = midnight const sunAngle = Math.sin(timeOfDay * Math.PI * 2); const warmth = Math.max(0, sunAngle); const coolness = Math.max(0, -sunAngle); return { r: ColorMath.clamp(baseColor.r + warmth * 50 - coolness * 30), g: ColorMath.clamp(baseColor.g + warmth * 30 - coolness * 10), b: ColorMath.clamp(baseColor.b - warmth * 30 + coolness * 50), }; } moonlightTransform(baseColor, phase = 0.5) { const moonIntensity = Math.sin(phase * Math.PI); const silverShift = moonIntensity * 0.3; return { r: ColorMath.clamp(baseColor.r * (0.4 + silverShift)), g: ColorMath.clamp(baseColor.g * (0.5 + silverShift)), b: ColorMath.clamp(baseColor.b * (0.8 + silverShift)), }; } starlight(baseColor, twinkle = true) { this.updateTime(); const sparkle = twinkle ? Math.sin(this.time * 8) * 0.3 + 0.7 : 1; const cosmic = 0.1 + sparkle * 0.2; return { r: ColorMath.clamp(baseColor.r * cosmic + 40), g: ColorMath.clamp(baseColor.g * cosmic + 60), b: ColorMath.clamp(baseColor.b * cosmic + 100), }; } // ====================== // BIOLOGICAL EFFECTS // ====================== bioluminescence(baseColor, glowIntensity = 0.5) { this.updateTime(); const pulse = Math.sin(this.time * 2) * glowIntensity + 1; return { r: ColorMath.clamp(baseColor.r * pulse * 0.3), g: ColorMath.clamp(baseColor.g * pulse * 1.2), b: ColorMath.clamp(baseColor.b * pulse * 0.8), }; } butterflyWing(baseColor, scale = 0.5) { this.updateTime(); const iridescent = this.iridescence(baseColor, scale * 10, 0.6); const metallic = Math.sin(this.time + scale * 5) * 0.3 + 0.7; return { r: ColorMath.clamp(iridescent.r * metallic), g: ColorMath.clamp(iridescent.g * metallic), b: ColorMath.clamp(iridescent.b * metallic), }; } firefly(baseColor, energy = 1.0) { this.updateTime(); const flicker = Math.random() < 0.95 ? 1 : 0.3; const glow = Math.sin(this.time * 3) * 0.4 + 0.6; return { r: ColorMath.clamp(255 * energy * flicker * glow), g: ColorMath.clamp(255 * energy * flicker * glow * 0.8), b: ColorMath.clamp(50 * energy * flicker * glow), }; } // ====================== // AQUATIC EFFECTS // ====================== underwaterCaustics(baseColor, depth = 0.5) { this.updateTime(); const wave1 = Math.sin(this.time * 0.7 + depth * 3) * 0.3; const wave2 = Math.cos(this.time * 1.1 + depth * 2) * 0.2; const causticEffect = (wave1 + wave2) * 0.5 + 1; return { r: ColorMath.clamp(baseColor.r * (1 - depth * 0.7)), g: ColorMath.clamp(baseColor.g * (1 - depth * 0.4) * causticEffect), b: ColorMath.clamp(baseColor.b * causticEffect), }; } deepSeaGlow(baseColor, depth = 0.8) { const pressure = Math.min(1, depth); const abyssal = 1 - pressure; return { r: ColorMath.clamp(baseColor.r * abyssal * 0.1), g: ColorMath.clamp(baseColor.g * abyssal * 0.3), b: ColorMath.clamp(baseColor.b * (0.5 + abyssal * 0.5)), }; } // ====================== // NATURAL PHENOMENA // ====================== auroraTransform(baseColor, altitude = 0.5) { this.updateTime(); const dance = Math.sin(this.time * 0.5 + altitude * 2) * 0.5 + 0.5; const greenIntensity = Math.sin(altitude * Math.PI) * 0.8 * dance; const purpleIntensity = Math.cos(altitude * Math.PI * 0.7) * 0.6 * dance; return { r: ColorMath.clamp(baseColor.r + purpleIntensity * 100), g: ColorMath.clamp(baseColor.g + greenIntensity * 150), b: ColorMath.clamp(baseColor.b + (greenIntensity + purpleIntensity) * 80), }; } lightning(baseColor, intensity = 1.0) { this.updateTime(); const strike = Math.random() < 0.05 ? 1 : 0; const afterglow = Math.max(0, Math.sin(this.time * 10) * 0.3); const flash = strike + afterglow; return { r: ColorMath.clamp(baseColor.r + flash * 200 * intensity), g: ColorMath.clamp(baseColor.g + flash * 220 * intensity), b: ColorMath.clamp(baseColor.b + flash * 255 * intensity), }; } canopyFilter(baseColor, density = 0.6) { const greenShift = Math.random() * 0.3 + 0.2; const dappling = Math.sin(Math.random() * Math.PI * 2) * 0.4 + 0.6; return { r: ColorMath.clamp(baseColor.r * (1 - density * 0.7) * dappling), g: ColorMath.clamp(baseColor.g * (1 + greenShift) * dappling), b: ColorMath.clamp(baseColor.b * (1 - density * 0.5) * dappling), }; } // ====================== // SCI-FI GAME EFFECTS // ====================== xenCrystal(baseColor, resonance = 0.5) { this.updateTime(); const harmonic = Math.sin(this.time * 4 + resonance * 6) * 0.4 + 0.6; const energy = Math.cos(this.time * 2.3) * 0.3 + 0.7; return { r: ColorMath.clamp(baseColor.r * 0.2 + 100 * harmonic), g: ColorMath.clamp(baseColor.g * 0.8 + 150 * energy), b: ColorMath.clamp(baseColor.b * 1.2 + 200 * harmonic), }; } gravityGun(baseColor, charge = 0.5) { this.updateTime(); const field = Math.sin(this.time * 6) * charge * 0.5 + charge; const distortion = Math.cos(this.time * 8) * 0.2 + 0.8; return { r: ColorMath.clamp(baseColor.r * (1 - field * 0.5) + field * 50), g: ColorMath.clamp(baseColor.g * distortion + field * 100), b: ColorMath.clamp(baseColor.b * (1 + field * 0.8) + field * 150), }; } combine(baseColor, suppression = 0.7) { const dystopian = 1 - suppression; const orangeOverlay = suppression * 0.6; return { r: ColorMath.clamp(baseColor.r * dystopian + 255 * orangeOverlay), g: ColorMath.clamp(baseColor.g * dystopian + 120 * orangeOverlay), b: ColorMath.clamp(baseColor.b * dystopian + 0 * orangeOverlay), }; } headcrabInfestation(baseColor, corruption = 0.4) { this.updateTime(); const pulse = Math.sin(this.time * 3) * corruption * 0.3 + corruption; const decay = 1 - corruption * 0.5; return { r: ColorMath.clamp(baseColor.r * decay + pulse * 150), g: ColorMath.clamp(baseColor.g * decay + pulse * 80), b: ColorMath.clamp(baseColor.b * decay + pulse * 40), }; } lambdaCore(baseColor, stability = 0.8) { this.updateTime(); const quantum = Math.sin(this.time * 5) * (1 - stability) * 0.5 + stability; const resonance = Math.cos(this.time * 3.7) * 0.3 + 0.7; return { r: ColorMath.clamp(baseColor.r * quantum + 255 * (1 - stability) * resonance), g: ColorMath.clamp(baseColor.g * quantum + 180 * (1 - stability) * resonance), b: ColorMath.clamp(baseColor.b * quantum + 50 * (1 - stability) * resonance), }; } // ====================== // ADDITIONAL SCI-FI EFFECTS // ====================== portalEnergy(baseColor, instability = 0.3) { this.updateTime(); const vortex = Math.sin(this.time * 8 + instability * 10) * instability + (1 - instability); const dimensional = Math.cos(this.time * 6) * 0.4 + 0.6; return { r: ColorMath.clamp(baseColor.r * vortex * 0.3 + 255 * dimensional * instability), g: ColorMath.clamp(baseColor.g * vortex * 0.1 + 100 * dimensional * instability), b: ColorMath.clamp(baseColor.b * vortex + 255 * dimensional), }; } radioactive(baseColor, decay = 0.5) { this.updateTime(); const geiger = Math.random() < 0.1 ? 1.5 : 1; const contamination = Math.sin(this.time * 2) * decay * 0.3 + decay; return { r: ColorMath.clamp(baseColor.r * (1 - contamination * 0.5)), g: ColorMath.clamp(baseColor.g * (1 + contamination * 0.8) * geiger), b: ColorMath.clamp(baseColor.b * (1 - contamination * 0.7)), }; } } export class ColorGenerators { static goldenRatioTheme(baseHue, baseSaturation = 70, baseLightness = 50) { const colors = []; const lightnesses = [20, 35, 50, 65, 80]; for (let i = 0; i < 5; i++) { const hue = (baseHue + i * ColorMath.goldenAngle) % 360; const saturation = baseSaturation + ((i * 10) % 40); const lightness = lightnesses[i]; const rgb = ColorMath.hslToRgb(hue, saturation, lightness); colors.push(ColorMath.rgbToHex(rgb.r, rgb.g, rgb.b)); } return { name: `Golden φ ${Math.round(baseHue)}°`, colors: colors, primary: colors[2], secondary: colors[1], accent: colors[4], background: colors[0], surface: colors[3], }; } static triadicHarmony(baseHue, intensity = 0.7) { const hues = [baseHue, (baseHue + 120) % 360, (baseHue + 240) % 360]; const colors = []; hues.forEach((hue, i) => { const saturation = 60 + i * 15; const lightness = 30 + i * 25; const rgb = ColorMath.hslToRgb(hue, saturation, lightness); colors.push(ColorMath.rgbToHex(rgb.r, rgb.g, rgb.b)); }); // Add lighter and darker variants const mainHue = hues[0]; const lightRgb = ColorMath.hslToRgb(mainHue, 40, 85); const darkRgb = ColorMath.hslToRgb(mainHue, 80, 15); colors.push(ColorMath.rgbToHex(lightRgb.r, lightRgb.g, lightRgb.b)); colors.push(ColorMath.rgbToHex(darkRgb.r, darkRgb.g, darkRgb.b)); return { name: `Triadic ${Math.round(baseHue)}°`, colors: colors, primary: colors[0], secondary: colors[1], accent: colors[2], background: colors[4], surface: colors[3], }; } static analogousGradient(baseHue, spread = 30) { const colors = []; const hueRange = spread; for (let i = 0; i < 5; i++) { const hue = (baseHue + ((i - 2) * hueRange) / 4) % 360; const saturation = 50 + i * 10; const lightness = 25 + i * 15; const rgb = ColorMath.hslToRgb(hue, saturation, lightness); colors.push(ColorMath.rgbToHex(rgb.r, rgb.g, rgb.b)); } return { name: `Analogous ${Math.round(baseHue)}°`, colors: colors, primary: colors[2], secondary: colors[1], accent: colors[4], background: colors[0], surface: colors[3], }; } static splitComplementary(baseHue) { const complementary = (baseHue + 180) % 360; const hues = [baseHue, (complementary - 30 + 360) % 360, (complementary + 30) % 360]; const colors = []; hues.forEach((hue, i) => { const saturation = 60 + i * 10; const lightness = 35 + i * 20; const rgb = ColorMath.hslToRgb(hue, saturation, lightness); colors.push(ColorMath.rgbToHex(rgb.r, rgb.g, rgb.b)); }); // Add neutral variants const neutralRgb1 = ColorMath.hslToRgb(baseHue, 20, 90); const neutralRgb2 = ColorMath.hslToRgb(baseHue, 40, 10); colors.push(ColorMath.rgbToHex(neutralRgb1.r, neutralRgb1.g, neutralRgb1.b)); colors.push(ColorMath.rgbToHex(neutralRgb2.r, neutralRgb2.g, neutralRgb2.b)); return { name: `Split Comp ${Math.round(baseHue)}°`, colors: colors, primary: colors[0], secondary: colors[1], accent: colors[2], background: colors[4], surface: colors[3], }; } static luminanceScaling(baseHue, baseSaturation = 70) { const colors = []; const luminanceSteps = [0.05, 0.2, 0.4, 0.7, 0.9]; luminanceSteps.forEach((luminance, i) => { // Convert luminance to perceived lightness const lightness = Math.sqrt(luminance) * 100; const saturation = baseSaturation * (1 - luminance * 0.3); const rgb = ColorMath.hslToRgb(baseHue, saturation, lightness); colors.push(ColorMath.rgbToHex(rgb.r, rgb.g, rgb.b)); }); return { name: `Luminance ${Math.round(baseHue)}°`, colors: colors, primary: colors[2], secondary: colors[1], accent: colors[3], background: colors[0], surface: colors[4], }; } static fibonacciSpiral(baseHue, turns = 3) { const colors = []; for (let i = 0; i < 5; i++) { const fibRatio = ColorMath.fibonacci(i + 3) / ColorMath.fibonacci(i + 4); const hue = (baseHue + i * 360 * fibRatio * turns) % 360; const saturation = 50 + fibRatio * 40; const lightness = 30 + i * 15; const rgb = ColorMath.hslToRgb(hue, saturation, lightness); colors.push(ColorMath.rgbToHex(rgb.r, rgb.g, rgb.b)); } return { name: `Fibonacci ${Math.round(baseHue)}°`, colors: colors, primary: colors[2], secondary: colors[1], accent: colors[4], background: colors[0], surface: colors[3], }; } }