Skip to content

Instantly share code, notes, and snippets.

@jaykdoe
Created June 19, 2025 20:01
Show Gist options
  • Save jaykdoe/3750fc082f3c93f08b814141feba98df to your computer and use it in GitHub Desktop.
Save jaykdoe/3750fc082f3c93f08b814141feba98df to your computer and use it in GitHub Desktop.
Morphing Superformula Explorer
{"config":{"codeTheme":"OneDarkPro","pageThemeSyncCodeTheme":true,"openAlmightyConsole":false,"autoRun":false,"layout":"default","keepPreviousLogs":true,"codeFontSize":16},"title":"Morphing Superformula Explorer","code":{"HTML":{"language":"html","content":"<div id=\"scene-container\"></div>"},"CSS":{"language":"css","content":"* { margin: 0; padding: 0; box-sizing: border-box; }\r\nhtml, body { width: 100%; height: 100%; }\r\nbody { \r\n overflow: hidden; \r\n background: #050508; \r\n color: #fff; \r\n font-family: 'Inter', sans-serif;\r\n}\r\n#scene-container { position: fixed; top: 0; left: 0; width: 100%; height: 100%; }\r\n\r\n:root {\r\n --background-color: rgba(22, 22, 30, 0.5);\r\n --text-color: rgba(255, 255, 255, 0.95);\r\n --title-background-color: rgba(40, 40, 60, 0.6);\r\n --title-text-color: rgba(255, 255, 255, 0.95);\r\n --widget-color: rgba(60, 60, 80, 0.5);\r\n --hover-color: rgba(80, 80, 120, 0.6);\r\n --focus-color: rgba(100, 100, 150, 0.7);\r\n --number-color: #ff55aa;\r\n --string-color: #55aaff;\r\n}\r\n\r\n.energy-button {\r\n border-radius: 6px !important;\r\n background: linear-gradient(to right, rgba(255, 65, 108, 0.9), rgba(255, 75, 43, 0.9)) !important;\r\n color: white !important;\r\n font-weight: bold !important;\r\n font-size: 14px !important;\r\n box-shadow: 0 4px 15px rgba(255, 85, 170, 0.4) !important;\r\n transition: all 0.3s !important;\r\n width: 100% !important;\r\n height: 40px !important;\r\n margin-top: 15px !important;\r\n margin-bottom: 8px !important;\r\n cursor: pointer !important;\r\n border: 1px solid rgba(255, 255, 255, 0.2) !important;\r\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3) !important;\r\n position: relative !important;\r\n overflow: hidden !important;\r\n backdrop-filter: blur(4px) !important;\r\n}\r\n.energy-button:before {\r\n content: '';\r\n position: absolute;\r\n top: -10px;\r\n left: -10px;\r\n right: -10px;\r\n bottom: -10px;\r\n background: linear-gradient(45deg, \r\n rgba(255,255,255,0.1) 0%, \r\n rgba(255,255,255,0.5) 25%, \r\n rgba(255,255,255,0.1) 50%, \r\n rgba(255,255,255,0) 50%);\r\n z-index: 1;\r\n transform: translateX(-100%);\r\n transition: transform 0.6s;\r\n}\r\n.energy-button:hover {\r\n transform: translateY(-2px) !important;\r\n box-shadow: 0 8px 20px rgba(255, 75, 43, 0.6) !important;\r\n}\r\n.energy-button:hover:before {\r\n transform: translateX(100%);\r\n}\r\n.energy-button:active {\r\n transform: translateY(1px) !important;\r\n}\r\n\r\n@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap');","resources":[]},"JS":{"language":"javascript","content":"import * as THREE from \"https://esm.sh/three\";\r\nimport { OrbitControls } from \"https://esm.sh/three/examples/jsm/controls/OrbitControls.js\";\r\nimport { EffectComposer } from \"https://esm.sh/three/examples/jsm/postprocessing/EffectComposer.js\";\r\nimport { RenderPass } from \"https://esm.sh/three/examples/jsm/postprocessing/RenderPass.js\";\r\nimport { UnrealBloomPass } from \"https://esm.sh/three/examples/jsm/postprocessing/UnrealBloomPass.js\";\r\nimport GUI from \"https://esm.sh/lil-gui\"; \r\n\r\nclass SuperformulaWireframe {\r\n constructor() {\r\n this.presets = [\r\n { m1: 5, n11: 10, n12: 2, n13: 7, m2: 5, n21: 10, n22: 10, n23: 10 },\r\n { m1: 2, n11: 1, n12: 4, n13: 8, m2: 8, n21: 1, n22: 1, n23: 4 },\r\n { m1: 6, n11: 1, n12: 1, n13: 1, m2: 3, n21: 1, n22: 5, n23: 1 },\r\n { m1: 12, n11: 15, n12: 8, n13: 8, m2: 12, n21: 8, n22: 4, n23: 15 }\r\n ];\r\n \r\n this.presetOptions = {\r\n \"Star Crystal\": 0, \r\n \"Ocean Creature\": 1, \r\n \"Spiral Galaxy\": 2, \r\n \"Quantum Form\": 3\r\n };\r\n\r\n this.themes = {\r\n \"Synthwave\": { \r\n colors: [\"#ff1f5a\", \"#ff758a\", \"#1e3799\", \"#0984e3\"], \r\n burstColor: \"#ffffff\"\r\n },\r\n \"Forest\": { \r\n colors: [\"#38ef7d\", \"#11998e\", \"#ffe259\", \"#ffa751\"],\r\n burstColor: \"#ffff99\"\r\n },\r\n \"Ocean\": { \r\n colors: [\"#2193b0\", \"#38ef7d\", \"#00b4db\", \"#0083B0\"],\r\n burstColor: \"#8cffff\"\r\n },\r\n \"Sunset\": { \r\n colors: [\"#FF416C\", \"#FF4B2B\", \"#f5af19\", \"#f12711\"],\r\n burstColor: \"#ffffa8\"\r\n }\r\n };\r\n this.themeNames = Object.keys(this.themes);\r\n\r\n this.params = {\r\n preset: 0,\r\n morphDuration: 2.0,\r\n \r\n pulseSpeed: 1.0,\r\n pulseIntensity: 0.2,\r\n microAnimationIntensity: 0.15,\r\n colorTheme: \"Sunset\", \r\n \r\n burstSpeed: 0.8,\r\n burstDuration: 6.0,\r\n multiWave: true,\r\n \r\n bloomStrength: 1.4,\r\n bloomRadius: 0.5,\r\n bloomThreshold: 0.18\r\n };\r\n \r\n this.resolutionTheta = 100;\r\n this.resolutionPhi = 100;\r\n\r\n this.currentPresetParams = { ...this.presets[this.params.preset] };\r\n this.targetPresetParams = { ...this.presets[this.params.preset] };\r\n this.isMorphing = false;\r\n this.morphStartTime = 0;\r\n\r\n this.burstActive = 0.0;\r\n this.burstStartTime = -1.0;\r\n this.lastBurstTime = 0;\r\n \r\n this.superformula = (angle, m, n1, n2, n3, a = 1, b = 1) => {\r\n const term1 = Math.pow(Math.abs(Math.cos(m * angle / 4) / a), n2);\r\n const term2 = Math.pow(Math.abs(Math.sin(m * angle / 4) / b), n3);\r\n const sum = term1 + term2;\r\n if (sum === 0) return 0;\r\n return Math.pow(sum, -1 / n1);\r\n };\r\n\r\n this.init();\r\n this.setupGUI();\r\n this.animate();\r\n }\r\n\r\n init() {\r\n this.scene = new THREE.Scene();\r\n this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);\r\n this.camera.position.set(0, 0, 2.5);\r\n this.camera.lookAt(0, 0, 0);\r\n\r\n this.renderer = new THREE.WebGLRenderer({ antialias: true });\r\n this.renderer.setSize(window.innerWidth, window.innerHeight);\r\n this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\r\n this.renderer.toneMapping = THREE.ACESFilmicToneMapping;\r\n this.renderer.toneMappingExposure = 1.0;\r\n this.container = document.getElementById(\"scene-container\");\r\n this.container.appendChild(this.renderer.domElement);\r\n\r\n this.composer = new EffectComposer(this.renderer);\r\n this.composer.addPass(new RenderPass(this.scene, this.camera));\r\n \r\n this.bloomPass = new UnrealBloomPass(\r\n new THREE.Vector2(window.innerWidth, window.innerHeight),\r\n this.params.bloomStrength,\r\n this.params.bloomRadius,\r\n this.params.bloomThreshold\r\n );\r\n this.composer.addPass(this.bloomPass);\r\n\r\n this.controls = new OrbitControls(this.camera, this.renderer.domElement);\r\n this.controls.enableDamping = true;\r\n this.controls.dampingFactor = 0.05;\r\n this.controls.autoRotate = false;\r\n this.controls.minDistance = 1;\r\n\r\n this.clock = new THREE.Clock();\r\n \r\n this.createWireframe();\r\n this.updateWireframeGeometry();\r\n \r\n window.addEventListener(\"resize\", () => this.onResize());\r\n \r\n this.container.addEventListener('click', (event) => {\r\n if (!this.isDragging) {\r\n this.triggerBurst();\r\n }\r\n });\r\n \r\n this.container.addEventListener('mousedown', () => { this.isDragging = false; });\r\n this.container.addEventListener('mousemove', () => { if (this.isMouseDown) this.isDragging = true; });\r\n this.container.addEventListener('mouseup', () => { this.isMouseDown = false; });\r\n this.container.addEventListener('mousedown', () => { this.isMouseDown = true; });\r\n }\r\n\r\n triggerBurst() {\r\n const currentTime = this.clock.getElapsedTime();\r\n \r\n if (currentTime - this.lastBurstTime > 0.3) {\r\n this.burstActive = 1.0;\r\n this.burstStartTime = currentTime;\r\n this.lastBurstTime = currentTime;\r\n \r\n const burstButton = document.querySelector('.energy-button');\r\n if (burstButton) {\r\n burstButton.style.boxShadow = '0 0 40px rgba(255, 175, 25, 0.9)';\r\n burstButton.style.transform = 'scale(1.15)';\r\n \r\n const theme = this.themes[this.params.colorTheme];\r\n const colors = theme.colors;\r\n burstButton.style.background = `linear-gradient(to right, ${colors[0]}, ${colors[2]})`;\r\n \r\n setTimeout(() => {\r\n burstButton.style.boxShadow = '0 0 30px rgba(255, 100, 75, 0.8)';\r\n burstButton.style.transform = 'scale(1.1)';\r\n }, 150);\r\n \r\n setTimeout(() => {\r\n burstButton.style.boxShadow = '0 0 20px rgba(245, 175, 25, 0.6)';\r\n burstButton.style.transform = 'scale(1.05)';\r\n burstButton.style.background = `linear-gradient(to right, ${colors[1]}, ${colors[3]})`;\r\n }, 300);\r\n \r\n setTimeout(() => {\r\n burstButton.style.boxShadow = '0 8px 20px rgba(255, 75, 43, 0.6)';\r\n burstButton.style.transform = '';\r\n burstButton.style.background = 'linear-gradient(to right, rgba(255, 65, 108, 0.9), rgba(255, 75, 43, 0.9))';\r\n }, 500);\r\n }\r\n \r\n if (this.wireframeMesh) {\r\n const theme = this.themes[this.params.colorTheme];\r\n const burstColor = theme.burstColor || \"#ffffff\";\r\n \r\n this.wireframeMesh.material.uniforms.burstActive.value = this.burstActive;\r\n this.wireframeMesh.material.uniforms.burstStartTime.value = this.burstStartTime;\r\n this.wireframeMesh.material.uniforms.burstColor.value.set(burstColor);\r\n }\r\n }\r\n }\r\n\r\n createWireframe() {\r\n const geometry = new THREE.BufferGeometry();\r\n const resTheta = this.resolutionTheta;\r\n const resPhi = this.resolutionPhi;\r\n const vertexCount = (resTheta + 1) * (resPhi + 1);\r\n\r\n const positions = new Float32Array(vertexCount * 3);\r\n const colors = new Float32Array(vertexCount * 3);\r\n const indices = [];\r\n\r\n for (let i = 0; i < resTheta; i++) {\r\n for (let j = 0; j < resPhi; j++) {\r\n const current = i * (resPhi + 1) + j;\r\n const nextTheta = (i + 1) * (resPhi + 1) + j;\r\n const nextPhi = current + 1;\r\n indices.push(current, nextTheta);\r\n indices.push(current, nextPhi);\r\n }\r\n indices.push(i * (resPhi + 1) + resPhi, (i + 1) * (resPhi + 1) + resPhi);\r\n }\r\n const lastRowStart = resTheta * (resPhi + 1);\r\n for (let j = 0; j < resPhi; j++) {\r\n indices.push(lastRowStart + j, lastRowStart + j + 1);\r\n }\r\n\r\n geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));\r\n geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));\r\n geometry.setIndex(new THREE.Uint32BufferAttribute(indices, 1));\r\n\r\n const material = new THREE.ShaderMaterial({\r\n uniforms: {\r\n time: { value: 0.0 },\r\n pulseSpeed: { value: this.params.pulseSpeed },\r\n pulseIntensity: { value: this.params.pulseIntensity },\r\n microAnimationIntensity: { value: this.params.microAnimationIntensity },\r\n dashSize: { value: 0.1 },\r\n dashRatio: { value: 0.5 },\r\n burstActive: { value: this.burstActive },\r\n burstStartTime: { value: this.burstStartTime },\r\n burstSpeed: { value: this.params.burstSpeed },\r\n burstDuration: { value: this.params.burstDuration },\r\n burstColor: { value: new THREE.Color(this.themes[this.params.colorTheme].burstColor) },\r\n multiWave: { value: this.params.multiWave ? 1.0 : 0.0 },\r\n morphProgress: { value: 0.0 }\r\n },\r\n vertexShader: `\r\n uniform float time;\r\n uniform float pulseSpeed;\r\n uniform float pulseIntensity;\r\n uniform float microAnimationIntensity;\r\n uniform float morphProgress;\r\n\r\n varying vec3 vColor;\r\n varying vec3 vPos;\r\n varying float vLineDistance;\r\n\r\n void main() {\r\n vColor = color;\r\n vPos = position;\r\n \r\n vLineDistance = length(position);\r\n \r\n float pulse = sin(length(position) * 2.0 - time * pulseSpeed) * pulseIntensity;\r\n \r\n float microAnim1 = sin(position.x * 8.0 + time * 3.0) * microAnimationIntensity;\r\n float microAnim2 = cos(position.y * 9.0 + time * 2.7) * microAnimationIntensity;\r\n float microAnim3 = sin(position.z * 7.0 + time * 3.3) * microAnimationIntensity;\r\n \r\n vec3 microOffset = vec3(microAnim1, microAnim2, microAnim3);\r\n vec3 pulseOffset = normalize(position) * pulse;\r\n \r\n microOffset *= (1.0 + morphProgress * 3.0);\r\n \r\n vec3 animatedPos = position + pulseOffset + microOffset;\r\n gl_Position = projectionMatrix * modelViewMatrix * vec4(animatedPos, 1.0);\r\n }\r\n `,\r\n fragmentShader: `\r\n uniform float time;\r\n uniform float dashSize;\r\n uniform float dashRatio;\r\n uniform float burstActive;\r\n uniform float burstStartTime;\r\n uniform float burstSpeed;\r\n uniform float burstDuration;\r\n uniform vec3 burstColor;\r\n uniform float multiWave;\r\n\r\n varying vec3 vColor;\r\n varying vec3 vPos;\r\n varying float vLineDistance;\r\n\r\n void main() {\r\n vec3 finalColor = vColor;\r\n float finalIntensity = 1.0;\r\n \r\n float totalSize = dashSize * (1.0 + dashRatio);\r\n float patternPos = mod(vLineDistance + time * 0.2, totalSize);\r\n float dashPart = step(patternPos, dashSize);\r\n \r\n if (dashPart < 0.1) {\r\n discard;\r\n }\r\n \r\n finalIntensity *= (1.0 + dashPart * 0.5);\r\n \r\n if (burstActive > 0.5) {\r\n float burstElapsed = max(0.0, time - burstStartTime);\r\n if (burstElapsed < burstDuration) {\r\n float distOrigin = length(vPos);\r\n float progress = burstElapsed / burstDuration;\r\n \r\n float baseSpeed = burstSpeed;\r\n float mainRadius = burstElapsed * baseSpeed;\r\n float mainThickness = 0.4 * (1.0 - 0.5 * progress);\r\n \r\n float mainDist = abs(distOrigin - mainRadius);\r\n float mainWave = 1.0 - smoothstep(0.0, mainThickness, mainDist);\r\n \r\n float trailFactor = smoothstep(0.0, mainRadius, distOrigin) * (1.0 - smoothstep(mainRadius * 0.5, mainRadius, distOrigin));\r\n \r\n float secondaryWave = 0.0;\r\n float tertiaryWave = 0.0;\r\n \r\n if (multiWave > 0.5) {\r\n float secondaryRadius = burstElapsed * (baseSpeed * 1.5);\r\n float secondaryThickness = 0.3 * (1.0 - 0.6 * progress);\r\n float secondaryDist = abs(distOrigin - secondaryRadius);\r\n secondaryWave = 1.0 - smoothstep(0.0, secondaryThickness, secondaryDist);\r\n secondaryWave *= 0.7 * (1.0 - progress * 0.7); \r\n \r\n float tertiaryRadius = burstElapsed * (baseSpeed * 0.7);\r\n float tertiaryThickness = 0.25 * (1.0 - 0.4 * progress);\r\n float tertiaryDist = abs(distOrigin - tertiaryRadius);\r\n tertiaryWave = 1.0 - smoothstep(0.0, tertiaryThickness, tertiaryDist);\r\n tertiaryWave *= 0.5 * (1.0 - progress * 0.5); \r\n }\r\n \r\n vec3 waveColorShift = burstColor;\r\n if (secondaryWave > 0.01) {\r\n waveColorShift = mix(burstColor, vec3(0.5, 0.8, 1.0), 0.3);\r\n }\r\n if (tertiaryWave > 0.01) {\r\n waveColorShift = mix(burstColor, vec3(0.8, 0.5, 1.0), 0.3);\r\n }\r\n \r\n float combinedWave = max(max(mainWave, secondaryWave * 0.8), tertiaryWave * 0.6);\r\n combinedWave = max(combinedWave, trailFactor * 0.4);\r\n \r\n float timeFade = 1.0 - smoothstep(burstDuration * 0.6, burstDuration, burstElapsed);\r\n combinedWave *= timeFade;\r\n \r\n finalColor = mix(finalColor, waveColorShift, combinedWave * 0.8);\r\n finalIntensity += combinedWave * 3.0;\r\n \r\n float rippleFactor = sin(distOrigin * 10.0 - burstElapsed * 5.0) * 0.5 + 0.5;\r\n rippleFactor *= smoothstep(0.0, mainRadius * 0.8, distOrigin) * (1.0 - smoothstep(mainRadius * 0.8, mainRadius, distOrigin));\r\n rippleFactor *= 0.15 * timeFade; \r\n \r\n finalIntensity += rippleFactor;\r\n }\r\n }\r\n\r\n gl_FragColor = vec4(finalColor * finalIntensity, 1.0);\r\n }\r\n `,\r\n vertexColors: true\r\n });\r\n\r\n this.wireframeMesh = new THREE.LineSegments(geometry, material);\r\n this.scene.add(this.wireframeMesh);\r\n }\r\n\r\n updateWireframeGeometry() {\r\n if (!this.wireframeMesh) return;\r\n\r\n const geometry = this.wireframeMesh.geometry;\r\n const positions = geometry.attributes.position.array;\r\n const colors = geometry.attributes.color.array;\r\n \r\n const sfParams = this.isMorphing ? this.getInterpolatedParams() : this.currentPresetParams;\r\n const resTheta = this.resolutionTheta;\r\n const resPhi = this.resolutionPhi;\r\n \r\n const theme = this.themes[this.params.colorTheme];\r\n const themeColors = theme.colors.map(color => new THREE.Color(color));\r\n \r\n let vertexIndex = 0;\r\n for (let i = 0; i <= resTheta; i++) {\r\n const theta = THREE.MathUtils.mapLinear(i, 0, resTheta, -Math.PI / 2, Math.PI / 2);\r\n const r1 = this.superformula(theta, sfParams.m1, sfParams.n11, sfParams.n12, sfParams.n13);\r\n \r\n for (let j = 0; j <= resPhi; j++) {\r\n const phi = THREE.MathUtils.mapLinear(j, 0, resPhi, -Math.PI, Math.PI);\r\n const r2 = this.superformula(phi, sfParams.m2, sfParams.n21, sfParams.n22, sfParams.n23);\r\n \r\n const x = r1 * Math.cos(theta) * r2 * Math.cos(phi);\r\n const y = r1 * Math.sin(theta);\r\n const z = r1 * Math.cos(theta) * r2 * Math.sin(phi);\r\n \r\n positions[vertexIndex * 3 + 0] = x;\r\n positions[vertexIndex * 3 + 1] = y;\r\n positions[vertexIndex * 3 + 2] = z;\r\n \r\n const colorMix = THREE.MathUtils.smoothstep(y, -1.5, 1.5);\r\n \r\n const colorIndex1 = Math.floor(colorMix * (themeColors.length - 1));\r\n const colorIndex2 = Math.min(colorIndex1 + 1, themeColors.length - 1);\r\n const colorFraction = (colorMix * (themeColors.length - 1)) - colorIndex1;\r\n \r\n const vertexColor = themeColors[colorIndex1].clone().lerp(\r\n themeColors[colorIndex2], colorFraction\r\n );\r\n \r\n colors[vertexIndex * 3 + 0] = vertexColor.r;\r\n colors[vertexIndex * 3 + 1] = vertexColor.g;\r\n colors[vertexIndex * 3 + 2] = vertexColor.b;\r\n \r\n vertexIndex++;\r\n }\r\n }\r\n \r\n geometry.attributes.position.needsUpdate = true;\r\n geometry.attributes.color.needsUpdate = true;\r\n geometry.computeBoundingSphere();\r\n }\r\n\r\n getInterpolatedParams() {\r\n const duration = Math.max(0.001, this.params.morphDuration);\r\n const elapsedTime = this.clock.getElapsedTime() - this.morphStartTime;\r\n const totalProgress = Math.min(1.0, elapsedTime / duration);\r\n \r\n if (this.wireframeMesh && this.wireframeMesh.material.uniforms.morphProgress) {\r\n const morphEffect = Math.sin(totalProgress * Math.PI);\r\n this.wireframeMesh.material.uniforms.morphProgress.value = morphEffect;\r\n }\r\n \r\n const interpolated = {};\r\n for (const key in this.currentPresetParams) {\r\n const factor = Math.sin(totalProgress * Math.PI / 2);\r\n interpolated[key] = THREE.MathUtils.lerp(\r\n this.currentPresetParams[key], \r\n this.targetPresetParams[key], \r\n factor\r\n );\r\n }\r\n return interpolated;\r\n }\r\n\r\n startMorphing(targetPresetIndex) {\r\n const targetIndex = Number(targetPresetIndex);\r\n if (isNaN(targetIndex) || targetIndex < 0 || targetIndex >= this.presets.length) {\r\n console.error(\"Invalid target preset index:\", targetPresetIndex);\r\n return;\r\n }\r\n\r\n if (!this.isMorphing) {\r\n this.currentPresetParams = { ...this.presets[this.params.preset] };\r\n } else {\r\n this.currentPresetParams = this.getInterpolatedParams();\r\n }\r\n\r\n this.params.preset = targetIndex; \r\n this.targetPresetParams = { ...this.presets[targetIndex] };\r\n \r\n this.isMorphing = true;\r\n this.morphStartTime = this.clock.getElapsedTime();\r\n }\r\n\r\n setupGUI() {\r\n const customStyles = document.createElement('style');\r\n customStyles.textContent = `\r\n .lil-gui {\r\n border-radius: 12px;\r\n width: 280px;\r\n font-family: 'Inter', sans-serif;\r\n backdrop-filter: blur(12px);\r\n border: 1px solid rgba(255, 255, 255, 0.15);\r\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\r\n overflow: hidden;\r\n opacity: 0.92;\r\n transition: opacity 0.3s;\r\n background: rgba(20, 20, 28, 0.5);\r\n }\r\n \r\n .lil-gui:hover {\r\n opacity: 0.98;\r\n }\r\n \r\n .lil-gui .title {\r\n border-radius: 10px 10px 0 0;\r\n font-weight: bold;\r\n letter-spacing: 0.5px;\r\n text-transform: uppercase;\r\n font-size: 11px;\r\n padding: 10px 12px;\r\n transition: background-color 0.3s;\r\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\r\n }\r\n \r\n .lil-gui .controller {\r\n border-radius: 6px;\r\n margin: 4px 6px;\r\n padding: 6px 10px;\r\n background: rgba(60, 60, 80, 0.25);\r\n border: 1px solid rgba(255, 255, 255, 0.05);\r\n transition: all 0.2s;\r\n }\r\n \r\n .lil-gui .controller:hover {\r\n background-color: rgba(80, 80, 120, 0.35);\r\n border-color: rgba(255, 255, 255, 0.1);\r\n }\r\n \r\n .lil-gui .controller.color input[type=\"color\"] {\r\n border-radius: 6px;\r\n border: 2px solid rgba(255, 255, 255, 0.2);\r\n width: 16px;\r\n height: 16px;\r\n }\r\n \r\n .lil-gui button {\r\n border-radius: 6px;\r\n background: linear-gradient(to right, rgba(60, 60, 100, 0.8), rgba(80, 80, 150, 0.8));\r\n border: 1px solid rgba(255, 255, 255, 0.1);\r\n transition: all 0.2s;\r\n box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);\r\n }\r\n \r\n .lil-gui button:hover {\r\n background: linear-gradient(to right, rgba(80, 80, 150, 0.9), rgba(100, 100, 200, 0.9));\r\n transform: translateY(-1px);\r\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);\r\n }\r\n \r\n .lil-gui .controller select {\r\n background: rgba(40, 40, 60, 0.7);\r\n border: 1px solid rgba(255, 255, 255, 0.1);\r\n border-radius: 4px;\r\n padding: 3px 6px;\r\n color: white;\r\n }\r\n \r\n .lil-gui .folder > .title {\r\n background: rgba(60, 60, 100, 0.4);\r\n }\r\n \r\n .lil-gui.root {\r\n margin-top: 10px;\r\n margin-right: 10px;\r\n }\r\n `;\r\n document.head.appendChild(customStyles);\r\n\r\n this.gui = new GUI();\r\n\r\n const shapeFolder = this.gui.addFolder('Shape & Morphing');\r\n this.guiPresetController = shapeFolder.add(this.params, 'preset', this.presetOptions).name('Shape Preset').listen().onChange((value) => {\r\n this.startMorphing(value);\r\n });\r\n shapeFolder.add(this.params, 'morphDuration', 0.5, 5.0, 0.1).name('Morph Duration');\r\n \r\n const animFolder = this.gui.addFolder('Animation & Color');\r\n animFolder.add(this.params, 'pulseSpeed', 0, 2, 0.05).name('Pulse Speed').onChange((value) => { \r\n if(this.wireframeMesh) this.wireframeMesh.material.uniforms.pulseSpeed.value = value; \r\n });\r\n animFolder.add(this.params, 'pulseIntensity', 0, 0.5, 0.01).name('Pulse Intensity').onChange((value) => { \r\n if(this.wireframeMesh) this.wireframeMesh.material.uniforms.pulseIntensity.value = value; \r\n });\r\n animFolder.add(this.params, 'microAnimationIntensity', 0, 0.3, 0.01).name('Micro-Animations').onChange(value => {\r\n if (this.wireframeMesh) this.wireframeMesh.material.uniforms.microAnimationIntensity.value = value;\r\n });\r\n animFolder.add(this.params, 'colorTheme', this.themeNames).name('Color Theme').onChange(() => {\r\n this.updateWireframeGeometry();\r\n if (this.wireframeMesh && this.wireframeMesh.material.uniforms.burstColor) {\r\n const themeColor = this.themes[this.params.colorTheme].burstColor || \"#ffffff\";\r\n this.wireframeMesh.material.uniforms.burstColor.value.set(themeColor);\r\n }\r\n });\r\n\r\n const bloomFolder = this.gui.addFolder('Bloom Effect');\r\n bloomFolder.add(this.params, 'bloomStrength', 0, 3).name('Strength').onChange((value) => { \r\n this.bloomPass.strength = value; \r\n });\r\n bloomFolder.add(this.params, 'bloomRadius', 0, 2).name('Radius').onChange((value) => { \r\n this.bloomPass.radius = value; \r\n });\r\n bloomFolder.add(this.params, 'bloomThreshold', 0, 1).name('Threshold').onChange((value) => { \r\n this.bloomPass.threshold = value; \r\n });\r\n \r\n const burstFolder = this.gui.addFolder('Energy Burst');\r\n burstFolder.add(this.params, 'multiWave').name('Multi-Wave Effect').onChange(value => {\r\n if (this.wireframeMesh) {\r\n this.wireframeMesh.material.uniforms.multiWave.value = value ? 1.0 : 0.0;\r\n }\r\n });\r\n \r\n const burstButton = document.createElement('button');\r\n burstButton.textContent = '✨ ENERGY BURST ✨';\r\n burstButton.className = 'energy-button';\r\n burstButton.onclick = () => this.triggerBurst();\r\n this.gui.domElement.appendChild(burstButton);\r\n \r\n shapeFolder.open();\r\n animFolder.open();\r\n burstFolder.open();\r\n }\r\n\r\n animate() {\r\n requestAnimationFrame(() => this.animate());\r\n const elapsedTime = this.clock.getElapsedTime();\r\n const delta = this.clock.getDelta();\r\n\r\n if (this.isMorphing) {\r\n const morphProgress = (elapsedTime - this.morphStartTime) / Math.max(0.001, this.params.morphDuration);\r\n if (morphProgress >= 1.0) {\r\n this.isMorphing = false;\r\n this.currentPresetParams = { ...this.targetPresetParams };\r\n this.updateWireframeGeometry(); \r\n if(this.guiPresetController) this.guiPresetController.updateDisplay();\r\n } else {\r\n this.updateWireframeGeometry(); \r\n }\r\n }\r\n\r\n if (this.wireframeMesh && this.wireframeMesh.material.uniforms) {\r\n this.wireframeMesh.material.uniforms.time.value = elapsedTime;\r\n this.wireframeMesh.material.uniforms.burstActive.value = this.burstActive;\r\n this.wireframeMesh.material.uniforms.burstStartTime.value = this.burstStartTime;\r\n\r\n if (this.burstActive > 0.5 && (elapsedTime - this.burstStartTime >= this.params.burstDuration)) {\r\n this.burstActive = 0.0;\r\n this.burstStartTime = -1.0;\r\n }\r\n }\r\n\r\n this.controls.update(delta);\r\n this.composer.render();\r\n }\r\n\r\n onResize() {\r\n this.camera.aspect = window.innerWidth / window.innerHeight;\r\n this.camera.updateProjectionMatrix();\r\n this.renderer.setSize(window.innerWidth, window.innerHeight);\r\n this.composer.setSize(window.innerWidth, window.innerHeight);\r\n }\r\n}\r\n\r\nwindow.onload = () => {\r\n new SuperformulaWireframe();\r\n}","resources":[]},"VUE":{"language":"vue2","content":"","resources":[]}}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment