feat: Add 14 genre-focused visualizer shapes and presets

This commit is contained in:
Jannik committed 2026-09-07 18:01:55 +02:00
1 parent 771dc7914c
commit 6a1801ed92
4 files changed
+416

No files matched your search

+156
View File
@@ -651,6 +651,162 @@
const r = 0.76 + 0.24 * Math.cos(6 * (i / count) * Math.PI * 2);
verts.push({ x: Math.cos(theta) * r, y: Math.sin(theta) * r });
}
} else if (shape === 'lightning') {
verts = [
{ x: 0.18, y: -1.0 },
{ x: -0.42, y: -0.08 },
{ x: -0.06, y: -0.08 },
{ x: -0.32, y: 1.0 },
{ x: 0.42, y: 0.08 },
{ x: 0.06, y: 0.08 }
];
} else if (shape === 'flame') {
const count = 64;
for (let i = 0; i < count; i++) {
const u = (i / count) * Math.PI * 2;
const px = 0.78 * Math.sin(u) * Math.pow(Math.sin(u / 2), 0.75);
const py = -Math.cos(u) * 0.95 - 0.05 * Math.cos(2 * u);
verts.push({ x: px, y: py });
}
} else if (shape === 'skull') {
verts = [
{ x: 0, y: -0.95 },
{ x: 0.38, y: -0.90 },
{ x: 0.70, y: -0.68 },
{ x: 0.84, y: -0.32 },
{ x: 0.82, y: 0.0 },
{ x: 0.62, y: 0.18 },
{ x: 0.75, y: 0.38 },
{ x: 0.48, y: 0.52 },
{ x: 0.42, y: 0.88 },
{ x: 0.20, y: 0.94 },
{ x: 0, y: 0.95 },
{ x: -0.20, y: 0.94 },
{ x: -0.42, y: 0.88 },
{ x: -0.48, y: 0.52 },
{ x: -0.75, y: 0.38 },
{ x: -0.62, y: 0.18 },
{ x: -0.82, y: 0.0 },
{ x: -0.84, y: -0.32 },
{ x: -0.70, y: -0.68 },
{ x: -0.38, y: -0.90 }
];
} else if (shape === 'crown') {
verts = [
{ x: 0, y: -0.95 },
{ x: 0.24, y: -0.32 },
{ x: 0.50, y: -0.80 },
{ x: 0.66, y: -0.25 },
{ x: 0.88, y: -0.65 },
{ x: 0.82, y: 0.70 },
{ x: 0.40, y: 0.75 },
{ x: 0, y: 0.76 },
{ x: -0.40, y: 0.75 },
{ x: -0.82, y: 0.70 },
{ x: -0.88, y: -0.65 },
{ x: -0.66, y: -0.25 },
{ x: -0.50, y: -0.80 },
{ x: -0.24, y: -0.32 }
];
} else if (shape === 'crescent') {
const count = 36;
for (let i = 0; i <= count; i++) {
const a = Math.PI * 1.5 - (i / count) * Math.PI;
verts.push({ x: 0.92 * Math.cos(a) + 0.42, y: 0.92 * Math.sin(a) });
}
for (let i = 1; i < count; i++) {
const phi = Math.PI * 0.5 - (i / count) * Math.PI;
verts.push({ x: -0.36 * Math.cos(phi) + 0.42, y: 0.92 * Math.sin(phi) });
}
} else if (shape === 'infinity') {
const count = 64;
for (let i = 0; i < count; i++) {
const theta = (i / count) * Math.PI * 2;
const r = 0.32 + 0.64 * Math.pow(Math.cos(theta), 2);
verts.push({ x: Math.cos(theta) * r, y: Math.sin(theta) * r });
}
} else if (shape === 'eye') {
const count = 32;
for (let i = 0; i <= count; i++) {
const t = i / count;
verts.push({ x: -0.95 + 1.90 * t, y: -Math.sin(t * Math.PI) * 0.62 });
}
for (let i = count - 1; i > 0; i--) {
const t = i / count;
verts.push({ x: -0.95 + 1.90 * t, y: Math.sin(t * Math.PI) * 0.62 });
}
} else if (shape === 'gem') {
verts = [
{ x: -0.52, y: -0.85 },
{ x: 0.52, y: -0.85 },
{ x: 0.92, y: -0.32 },
{ x: 0, y: 0.98 },
{ x: -0.92, y: -0.32 }
];
} else if (shape === 'pick') {
const count = 48;
for (let i = 0; i < count; i++) {
const a = (i / count) * Math.PI * 2;
const px = 0.80 * Math.cos(a) * (1.0 - 0.45 * Math.sin(a));
const py = 0.92 * Math.sin(a);
verts.push({ x: px, y: py });
}
} else if (shape === 'sun') {
const n = 16;
const step = Math.PI / n;
const start = -Math.PI / 2;
for (let i = 0; i < n * 2; i++) {
const r = (i % 2 === 0) ? 1.0 : 0.72;
const a = start + i * step;
verts.push({ x: Math.cos(a) * r, y: Math.sin(a) * r });
}
} else if (shape === 'droplet') {
const count = 48;
for (let i = 0; i < count; i++) {
const u = (i / count) * Math.PI * 2;
const px = 0.82 * Math.sin(u) * Math.sin(u / 2);
const py = -Math.cos(u) * 0.90 - 0.05 * Math.cos(2 * u) + 0.05;
verts.push({ x: px, y: py });
}
} else if (shape === 'hourglass') {
verts = [
{ x: -0.80, y: -0.92 },
{ x: 0.80, y: -0.92 },
{ x: 0.18, y: 0.0 },
{ x: 0.80, y: 0.92 },
{ x: -0.80, y: 0.92 },
{ x: -0.18, y: 0.0 }
];
} else if (shape === 'butterfly') {
verts = [
{ x: 0, y: -0.75 },
{ x: 0.22, y: -0.68 },
{ x: 0.62, y: -0.88 },
{ x: 0.95, y: -0.48 },
{ x: 0.72, y: -0.10 },
{ x: 0.38, y: -0.02 },
{ x: 0.78, y: 0.35 },
{ x: 0.55, y: 0.72 },
{ x: 0.20, y: 0.62 },
{ x: 0, y: 0.82 },
{ x: -0.20, y: 0.62 },
{ x: -0.55, y: 0.72 },
{ x: -0.78, y: 0.35 },
{ x: -0.38, y: -0.02 },
{ x: -0.72, y: -0.10 },
{ x: -0.95, y: -0.48 },
{ x: -0.62, y: -0.88 },
{ x: -0.22, y: -0.68 }
];
} else if (shape === 'hexagram') {
const n = 6;
const step = Math.PI / n;
const start = -Math.PI / 2;
for (let i = 0; i < n * 2; i++) {
const r = (i % 2 === 0) ? 1.0 : 0.577;
const a = start + i * step;
verts.push({ x: Math.cos(a) * r, y: Math.sin(a) * r });
}
}
SHAPE_VERTICES_CACHE[shape] = verts;
+98
View File
@@ -371,6 +371,104 @@
<span class="shape-label">Blume</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="lightning">
<div class="shape-card-inner">
<span class="shape-icon">⚡</span>
<span class="shape-label">Blitz</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="flame">
<div class="shape-card-inner">
<span class="shape-icon">🔥</span>
<span class="shape-label">Flamme</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="skull">
<div class="shape-card-inner">
<span class="shape-icon">💀</span>
<span class="shape-label">Totenkopf</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="crown">
<div class="shape-card-inner">
<span class="shape-icon">👑</span>
<span class="shape-label">Krone</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="crescent">
<div class="shape-card-inner">
<span class="shape-icon">🌙</span>
<span class="shape-label">Halbmond</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="infinity">
<div class="shape-card-inner">
<span class="shape-icon">♾️</span>
<span class="shape-label">Infinity</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="eye">
<div class="shape-card-inner">
<span class="shape-icon">👁️</span>
<span class="shape-label">Cyber-Auge</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="gem">
<div class="shape-card-inner">
<span class="shape-icon">💎</span>
<span class="shape-label">Diamant</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="pick">
<div class="shape-card-inner">
<span class="shape-icon">🎸</span>
<span class="shape-label">Plektrum</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="sun">
<div class="shape-card-inner">
<span class="shape-icon">☀️</span>
<span class="shape-label">Sonne</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="droplet">
<div class="shape-card-inner">
<span class="shape-icon">💧</span>
<span class="shape-label">Tropfen</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="hourglass">
<div class="shape-card-inner">
<span class="shape-icon">⏳</span>
<span class="shape-label">Sanduhr</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="butterfly">
<div class="shape-card-inner">
<span class="shape-icon">🦋</span>
<span class="shape-label">Schmetterling</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="hexagram">
<div class="shape-card-inner">
<span class="shape-icon">✡️</span>
<span class="shape-label">Hexagramm</span>
</div>
</label>
</div>
</div>
+81
View File
@@ -0,0 +1,81 @@
{
"name": "Heavy Metal Lightning",
"icon": "⚡",
"shapeType": "lightning",
"showCover": true,
"coverShape": "circle",
"circleRadius": 160,
"pulseIntensity": 0.65,
"pulseDecay": 0.82,
"showBorder": true,
"borderWidth": 4,
"borderColor": "#ffcc00",
"showProgressRing": true,
"progressRingColor": "#ff2200",
"progressRingWidth": 4,
"rotationSpeed": 0.8,
"beatSpinImpulse": true,
"showSpectrum": true,
"spectrumMode": "radial_bars",
"linearAlignment": "center_mirrored",
"barCount": 96,
"spectrumMaxHeight": 150,
"barWidth": 4,
"barSpacing": 8,
"barCornerRadius": 1,
"freqSensitivity": 1.4,
"freqFocus": "bass_heavy",
"mirrorSpectrum": true,
"stereoSplit": true,
"stereoMode": "split_hemisphere",
"routePulse": "sub_bass",
"routeParticles": "treble",
"routeShake": "sub_bass",
"bpmSync": false,
"bpmValue": 140,
"bpmSyncRotation": false,
"bpmSyncGrid": false,
"enableParticles": true,
"particleType": "sparks",
"particleCount": 120,
"particleSpeed": 1.4,
"particleWarp": 2.0,
"enableGrid": true,
"colorGrid": "#ff3300",
"gridPerspective": 0.7,
"gridAudioReactive": 0.6,
"enableShake": true,
"camShakeIntensity": 0.45,
"enableGlitch": true,
"rgbSplitIntensity": 0.25,
"beatFlash": true,
"enableBassZoom": true,
"enableVhs": false,
"vhsScanlineIntensity": 0.2,
"vhsNoiseIntensity": 0.1,
"vhsCrtCurvature": false,
"enableAfterglow": true,
"afterglowDecay": 0.75,
"afterglowMode": "fire_amber",
"colorMode": "gradient_3",
"colorGrad1": "#ffcc00",
"colorGrad2": "#ff3300",
"colorGrad3": "#990000",
"colorParticle": "#ffea88",
"gradientStyle": "angular",
"enableGlow": true,
"glowIntensity": 0.9,
"glowBlur": 28,
"laserCore": true,
"backgroundMode": "transparent",
"showTextOverlay": true,
"fontFamily": "'Outfit', sans-serif",
"textPosition": "below_circle",
"titleFontSize": 40,
"artistFontSize": 24,
"textPulseWithBeat": true,
"textGlow": true,
"titleColor": "#ffffff",
"artistColor": "#ffcc00",
"timecodeColor": "#ff4422"
}
+81
View File
@@ -0,0 +1,81 @@
{
"name": "Midnight Crescent",
"icon": "🌙",
"shapeType": "crescent",
"showCover": true,
"coverShape": "circle",
"circleRadius": 155,
"pulseIntensity": 0.45,
"pulseDecay": 0.85,
"showBorder": true,
"borderWidth": 4,
"borderColor": "#00f0ff",
"showProgressRing": true,
"progressRingColor": "#bd00ff",
"progressRingWidth": 4,
"rotationSpeed": 0.6,
"beatSpinImpulse": false,
"showSpectrum": true,
"spectrumMode": "smooth_wave",
"linearAlignment": "center_mirrored",
"barCount": 120,
"spectrumMaxHeight": 120,
"barWidth": 3,
"barSpacing": 6,
"barCornerRadius": 2,
"freqSensitivity": 1.2,
"freqFocus": "flat",
"mirrorSpectrum": true,
"stereoSplit": true,
"stereoMode": "split_hemisphere",
"routePulse": "sub_bass",
"routeParticles": "treble",
"routeShake": "sub_bass",
"bpmSync": false,
"bpmValue": 90,
"bpmSyncRotation": false,
"bpmSyncGrid": false,
"enableParticles": true,
"particleType": "starfield",
"particleCount": 130,
"particleSpeed": 0.8,
"particleWarp": 1.8,
"enableGrid": true,
"colorGrid": "#6b11ff",
"gridPerspective": 0.6,
"gridAudioReactive": 0.4,
"enableShake": false,
"camShakeIntensity": 0.2,
"enableGlitch": false,
"rgbSplitIntensity": 0.15,
"beatFlash": false,
"enableBassZoom": true,
"enableVhs": false,
"vhsScanlineIntensity": 0.2,
"vhsNoiseIntensity": 0.1,
"vhsCrtCurvature": false,
"enableAfterglow": true,
"afterglowDecay": 0.82,
"afterglowMode": "magenta_dream",
"colorMode": "gradient_3",
"colorGrad1": "#00f0ff",
"colorGrad2": "#a020f0",
"colorGrad3": "#ff007f",
"colorParticle": "#aae7ff",
"gradientStyle": "angular",
"enableGlow": true,
"glowIntensity": 0.85,
"glowBlur": 26,
"laserCore": true,
"backgroundMode": "transparent",
"showTextOverlay": true,
"fontFamily": "'Outfit', sans-serif",
"textPosition": "below_circle",
"titleFontSize": 38,
"artistFontSize": 22,
"textPulseWithBeat": true,
"textGlow": true,
"titleColor": "#ffffff",
"artistColor": "#00f0ff",
"timecodeColor": "#ff007f"
}