peepee
Version:
Visual Programming Language Where You Connect Ports Of One EventEmitter to Ports Of Another EventEmitter
748 lines (645 loc) • 27.3 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Color Functions Demo</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #0c0c0c 0%, #1a1a2e 50%, #16213e 100%);
color: #ffffff;
min-height: 100vh;
overflow-x: hidden;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 40px;
padding: 30px;
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.header h1 {
font-size: 3em;
background: linear-gradient(45deg, #ff6b6b, #4ecdc4, #45b7d1, #f9ca24);
background-size: 400% 400%;
animation: gradientShift 3s ease infinite;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 15px;
}
@keyframes gradientShift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
.controls {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 40px;
}
.control-group {
background: rgba(255, 255, 255, 0.08);
padding: 25px;
border-radius: 15px;
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(5px);
}
.control-group h3 {
color: #4ecdc4;
margin-bottom: 15px;
font-size: 1.2em;
}
.color-input-container {
position: relative;
margin-bottom: 15px;
}
.color-input {
width: 100%;
height: 50px;
border: none;
border-radius: 10px;
cursor: pointer;
outline: none;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
transition: transform 0.2s ease;
}
.color-input:hover {
transform: scale(1.05);
}
.color-value {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-weight: bold;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
pointer-events: none;
font-size: 14px;
}
select, input[type="range"] {
width: 100%;
padding: 12px;
margin: 5px 0;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
background: rgba(255, 255, 255, 0.1);
color: white;
font-size: 14px;
}
select option {
background: #1a1a2e;
color: white;
}
input[type="range"] {
height: 8px;
background: rgba(255, 255, 255, 0.2);
outline: none;
padding: 0;
}
input[type="range"]::-webkit-slider-thumb {
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #4ecdc4;
cursor: pointer;
box-shadow: 0 2px 10px rgba(78, 205, 196, 0.3);
}
.swatch-container {
background: rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 30px;
margin-bottom: 30px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.swatch-title {
text-align: center;
font-size: 1.5em;
margin-bottom: 25px;
color: #4ecdc4;
}
.swatch-strip {
display: flex;
height: 120px;
border-radius: 15px;
overflow: hidden;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
margin-bottom: 20px;
transition: transform 0.3s ease;
}
.swatch-strip:hover {
transform: translateY(-5px);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
}
.swatch-color {
flex: 1;
cursor: pointer;
position: relative;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.swatch-color:hover {
transform: scaleY(1.1);
z-index: 10;
box-shadow: 0 0 20px rgba(255, 255, 255, 0.3);
}
.color-hex {
font-size: 11px;
font-weight: bold;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
opacity: 0;
transition: opacity 0.3s ease;
background: rgba(0, 0, 0, 0.5);
padding: 4px 8px;
border-radius: 4px;
}
.swatch-color:hover .color-hex {
opacity: 1;
}
.effect-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 25px;
margin-top: 30px;
}
.effect-demo {
background: rgba(255, 255, 255, 0.08);
border-radius: 15px;
padding: 20px;
border: 1px solid rgba(255, 255, 255, 0.1);
transition: transform 0.3s ease;
}
.effect-demo:hover {
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.effect-title {
color: #f9ca24;
margin-bottom: 15px;
font-size: 1.1em;
}
.mini-swatch {
height: 60px;
border-radius: 8px;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
cursor: pointer;
transition: transform 0.2s ease;
}
.mini-swatch:hover {
transform: scale(1.05);
}
.toast {
position: fixed;
top: 20px;
right: 20px;
background: rgba(78, 205, 196, 0.9);
color: white;
padding: 12px 20px;
border-radius: 8px;
font-weight: bold;
transform: translateX(300px);
transition: transform 0.3s ease;
z-index: 1000;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
.toast.show {
transform: translateX(0);
}
.animate-btn {
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
color: white;
border: none;
padding: 12px 24px;
border-radius: 25px;
cursor: pointer;
font-weight: bold;
transition: all 0.3s ease;
margin: 10px 5px;
}
.animate-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(255, 107, 107, 0.3);
}
.param-display {
font-size: 12px;
color: #cccccc;
margin-top: 5px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Color Functions Demo</h1>
<p>Explore nature-inspired and sci-fi color transformations</p>
</div>
<div class="controls">
<div class="control-group">
<h3>Base Color</h3>
<div class="color-input-container">
<input type="color" id="baseColor" class="color-input" value="#ff6b6b">
<div class="color-value" id="baseColorValue">#ff6b6b</div>
</div>
<button class="animate-btn" onclick="randomizeColor()">Random Base</button>
</div>
<div class="control-group">
<h3>Primary Effect</h3>
<select id="primaryEffect">
<option value="iridescence">Iridescence</option>
<option value="atmosphericScatter">Atmospheric Scatter</option>
<option value="sunsetGradient">Sunset Gradient</option>
<option value="bioluminescence">Bioluminescence</option>
<option value="underwaterCaustics">Underwater Caustics</option>
<option value="auroraTransform">Aurora Transform</option>
<option value="xenCrystal">Xen Crystal</option>
<option value="gravityGun">Gravity Gun</option>
<option value="oilSlick">Oil Slick</option>
<option value="chromaticAberration">Chromatic Aberration</option>
</select>
</div>
<div class="control-group">
<h3>Intensity</h3>
<input type="range" id="intensity" min="0" max="1" step="0.1" value="0.5">
<div class="param-display">Value: <span id="intensityValue">0.5</span></div>
</div>
<div class="control-group">
<h3>Animation</h3>
<button class="animate-btn" id="animateBtn" onclick="toggleAnimation()">Start Animation</button>
<button class="animate-btn" onclick="generateNewSwatch()">New Swatch</button>
</div>
</div>
<div class="swatch-container">
<div class="swatch-title">Color Transformation Strip</div>
<div class="swatch-strip" id="mainSwatch"></div>
<p style="text-align: center; color: #cccccc; font-size: 14px; margin-top: 15px;">
Click any color to copy its hex value to clipboard
</p>
</div>
<div class="effect-grid" id="effectGrid"></div>
</div>
<div class="toast" id="toast">Color copied to clipboard!</div>
<script>
// Include the ColorFunctions class
class ColorFunctions {
constructor() {
this.time = 0;
this.animationId = null;
this.startTime = Date.now();
}
static clamp(value, min = 0, max = 255) {
return Math.max(min, Math.min(max, value));
}
static hslToRgb(h, s, l) {
h /= 360;
s /= 100;
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;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
return {
r: Math.round(hue2rgb(p, q, h + 1/3) * 255),
g: Math.round(hue2rgb(p, q, h) * 255),
b: Math.round(hue2rgb(p, q, h - 1/3) * 255)
};
}
updateTime() {
this.time = (Date.now() - this.startTime) * 0.001;
}
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: ColorFunctions.clamp(r),
g: ColorFunctions.clamp(g),
b: ColorFunctions.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: ColorFunctions.clamp(baseColor.r + orangeShift * 100 + redShift * 50),
g: ColorFunctions.clamp(baseColor.g + orangeShift * 60 - redShift * 30),
b: ColorFunctions.clamp(baseColor.b - orangeShift * 80 - redShift * 100)
};
}
iridescence(baseColor, angle = 0, intensity = 0.8) {
this.updateTime();
const shimmer = Math.sin(angle + this.time * 2) * intensity;
const hueShift = shimmer * 60;
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 ColorFunctions.hslToRgb(h, s * 100, l * 100);
}
bioluminescence(baseColor, glowIntensity = 0.5) {
this.updateTime();
const pulse = Math.sin(this.time * 2) * glowIntensity + 1;
return {
r: ColorFunctions.clamp(baseColor.r * pulse * 0.3),
g: ColorFunctions.clamp(baseColor.g * pulse * 1.2),
b: ColorFunctions.clamp(baseColor.b * pulse * 0.8)
};
}
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: ColorFunctions.clamp(baseColor.r * (1 - depth * 0.7)),
g: ColorFunctions.clamp(baseColor.g * (1 - depth * 0.4) * causticEffect),
b: ColorFunctions.clamp(baseColor.b * causticEffect)
};
}
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: ColorFunctions.clamp(baseColor.r + purpleIntensity * 100),
g: ColorFunctions.clamp(baseColor.g + greenIntensity * 150),
b: ColorFunctions.clamp(baseColor.b + (greenIntensity + purpleIntensity) * 80)
};
}
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: ColorFunctions.clamp(baseColor.r * 0.2 + 100 * harmonic),
g: ColorFunctions.clamp(baseColor.g * 0.8 + 150 * energy),
b: ColorFunctions.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: ColorFunctions.clamp(baseColor.r * (1 - field * 0.5) + field * 50),
g: ColorFunctions.clamp(baseColor.g * distortion + field * 100),
b: ColorFunctions.clamp(baseColor.b * (1 + field * 0.8) + field * 150)
};
}
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 ColorFunctions.hslToRgb(
hueShift,
80 + interference * 20,
30 + interference * 40
);
}
chromaticAberration(baseColor, intensity = 0.1) {
return {
r: ColorFunctions.clamp(baseColor.r + intensity * 50),
g: baseColor.g,
b: ColorFunctions.clamp(baseColor.b - intensity * 30)
};
}
static rgbToHex(r, g, b) {
return '#' + [r, g, b].map(x => {
const hex = Math.round(x).toString(16);
return hex.length === 1 ? '0' + hex : hex;
}).join('');
}
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;
}
}
// App logic
const colorFunctions = new ColorFunctions();
let isAnimating = false;
let animationFrame;
const baseColorInput = document.getElementById('baseColor');
const baseColorValue = document.getElementById('baseColorValue');
const primaryEffectSelect = document.getElementById('primaryEffect');
const intensitySlider = document.getElementById('intensity');
const intensityValue = document.getElementById('intensityValue');
const mainSwatch = document.getElementById('mainSwatch');
const effectGrid = document.getElementById('effectGrid');
const toast = document.getElementById('toast');
// Update base color display
baseColorInput.addEventListener('input', () => {
baseColorValue.textContent = baseColorInput.value;
generateSwatch();
});
// Update intensity display
intensitySlider.addEventListener('input', () => {
intensityValue.textContent = intensitySlider.value;
generateSwatch();
});
// Update on effect change
primaryEffectSelect.addEventListener('change', generateSwatch);
function generateSwatch() {
const baseColor = ColorFunctions.hexToRgb(baseColorInput.value);
const effect = primaryEffectSelect.value;
const intensity = parseFloat(intensitySlider.value);
mainSwatch.innerHTML = '';
// Generate 12 color variations
for (let i = 0; i < 12; i++) {
const variation = i / 11;
let transformedColor;
switch (effect) {
case 'iridescence':
transformedColor = colorFunctions.iridescence(baseColor, variation * Math.PI * 2, intensity);
break;
case 'atmosphericScatter':
transformedColor = colorFunctions.atmosphericScatter(baseColor, variation * intensity);
break;
case 'sunsetGradient':
transformedColor = colorFunctions.sunsetGradient(baseColor, variation);
break;
case 'bioluminescence':
transformedColor = colorFunctions.bioluminescence(baseColor, intensity);
break;
case 'underwaterCaustics':
transformedColor = colorFunctions.underwaterCaustics(baseColor, variation);
break;
case 'auroraTransform':
transformedColor = colorFunctions.auroraTransform(baseColor, variation);
break;
case 'xenCrystal':
transformedColor = colorFunctions.xenCrystal(baseColor, variation * intensity);
break;
case 'gravityGun':
transformedColor = colorFunctions.gravityGun(baseColor, variation * intensity);
break;
case 'oilSlick':
transformedColor = colorFunctions.oilSlick(baseColor, variation * intensity);
break;
case 'chromaticAberration':
transformedColor = colorFunctions.chromaticAberration(baseColor, variation * intensity);
break;
default:
transformedColor = baseColor;
}
const hex = ColorFunctions.rgbToHex(transformedColor.r, transformedColor.g, transformedColor.b);
const colorDiv = document.createElement('div');
colorDiv.className = 'swatch-color';
colorDiv.style.backgroundColor = hex;
colorDiv.innerHTML = `<div class="color-hex">${hex}</div>`;
colorDiv.addEventListener('click', () => copyToClipboard(hex));
mainSwatch.appendChild(colorDiv);
}
}
function generateEffectGrid() {
const effects = [
{ name: 'atmosphericScatter', title: 'Atmospheric Scatter' },
{ name: 'iridescence', title: 'Iridescence' },
{ name: 'bioluminescence', title: 'Bioluminescence' },
{ name: 'underwaterCaustics', title: 'Underwater Caustics' },
{ name: 'auroraTransform', title: 'Aurora Transform' },
{ name: 'xenCrystal', title: 'Xen Crystal' },
{ name: 'gravityGun', title: 'Gravity Gun' },
{ name: 'oilSlick', title: 'Oil Slick' }
];
effectGrid.innerHTML = '';
const baseColor = ColorFunctions.hexToRgb(baseColorInput.value);
effects.forEach(effect => {
const demo = document.createElement('div');
demo.className = 'effect-demo';
const title = document.createElement('div');
title.className = 'effect-title';
title.textContent = effect.title;
demo.appendChild(title);
// Generate 3 variations for each effect
for (let i = 0; i < 3; i++) {
const variation = (i + 1) * 0.33;
let transformedColor;
switch (effect.name) {
case 'iridescence':
transformedColor = colorFunctions.iridescence(baseColor, variation * Math.PI * 2, 0.8);
break;
case 'atmosphericScatter':
transformedColor = colorFunctions.atmosphericScatter(baseColor, variation);
break;
case 'bioluminescence':
transformedColor = colorFunctions.bioluminescence(baseColor, variation);
break;
case 'underwaterCaustics':
transformedColor = colorFunctions.underwaterCaustics(baseColor, variation);
break;
case 'auroraTransform':
transformedColor = colorFunctions.auroraTransform(baseColor, variation);
break;
case 'xenCrystal':
transformedColor = colorFunctions.xenCrystal(baseColor, variation);
break;
case 'gravityGun':
transformedColor = colorFunctions.gravityGun(baseColor, variation);
break;
case 'oilSlick':
transformedColor = colorFunctions.oilSlick(baseColor, variation);
break;
}
const hex = ColorFunctions.rgbToHex(transformedColor.r, transformedColor.g, transformedColor.b);
const miniSwatch = document.createElement('div');
miniSwatch.className = 'mini-swatch';
miniSwatch.style.backgroundColor = hex;
miniSwatch.textContent = hex;
miniSwatch.addEventListener('click', () => copyToClipboard(hex));
demo.appendChild(miniSwatch);
}
effectGrid.appendChild(demo);
});
}
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
showToast();
}).catch(err => {
console.error('Failed to copy: ', err);
});
}
function showToast() {
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 2000);
}
function randomizeColor() {
const randomHex = '#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0');
baseColorInput.value = randomHex;
baseColorValue.textContent = randomHex;
generateSwatch();
generateEffectGrid();
}
function generateNewSwatch() {
generateSwatch();
generateEffectGrid();
}
function toggleAnimation() {
const btn = document.getElementById('animateBtn');
if (isAnimating) {
cancelAnimationFrame(animationFrame);
isAnimating = false;
btn.textContent = 'Start Animation';
btn.style.background = 'linear-gradient(45deg, #ff6b6b, #4ecdc4)';
} else {
isAnimating = true;
btn.textContent = 'Stop Animation';
btn.style.background = 'linear-gradient(45deg, #ff9ff3, #54a0ff)';
animate();
}
}
function animate() {
if (isAnimating) {
colorFunctions.updateTime();
generateSwatch();
animationFrame = requestAnimationFrame(animate);
}
}
// Initialize
generateSwatch();
generateEffectGrid();
</script>
</body>
</html>