let scene, camera, renderer, uniforms, clock; let musicStarted = false; let synthLead, synthPad, synthBass, reverb, delay, filter; init(); animate(); function init() { document.body.style.margin = '0'; document.body.style.padding = '0'; document.body.style.overflow = 'hidden'; document.body.style.backgroundColor = '#000000'; scene = new THREE.Scene(); camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); clock = new THREE.Clock(); initAudio(); document.addEventListener('click', startMusic); uniforms = { iTime: { value: 0.0 }, iResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) } }; const vertexShader = ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `; const fragmentShader = ` precision highp float; uniform float iTime; uniform vec2 iResolution; varying vec2 vUv; #define TWOPI 6.28318530718 #define D2R 0.01745329251 #define OC 12.0 #define volsteps 15 #define iterations 14 #define formuparam 0.55 #define darkmatter 0.4 #define brightness 0.002 #define distfading 0.7 #define saturation 0.9 #define zoom 1.2 #define tile 0.9 mat2 rotMat(float r) { float c = cos(r); float s = sin(r); return mat2(c, -s, s, c); } float abs1d(float x) { return abs(fract(x) - 0.5); } vec2 abs2d(vec2 v) { return abs(fract(v) - 0.5); } float cos1d(float p) { return cos(p * TWOPI) * 0.3 + 0.3; } float sin1d(float p) { return sin(p * TWOPI) * 0.3 + 0.3; } float pseudoRandom(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } vec3 OilEffect(vec2 pos, vec3 RGB) { vec2 q = vec2(0.0); float result = 0.0; float s = 1.8; float gain = 0.5; vec2 aPos = abs2d(pos) * 0.7; for(float i = 0.0; i < OC; i++) { pos *= rotMat(D2R * 45.0 + sin(iTime * 0.2)); float time = (sin(iTime * 0.3) * 0.5 + 0.5) * 0.3 + iTime * 0.6; q = pos * s + aPos + time; q = vec2(cos(q.x), cos(q.y)); result += sin1d(dot(q, vec2(0.4))) * gain; s *= 1.1; aPos += cos(smoothstep(0.0, 0.2, q)); aPos *= rotMat(D2R * 10.0); aPos *= 1.3; } result = pow(result, 4.0); vec3 dynamicRGB = RGB * (0.5 + 0.5 * sin(iTime + vec3(0.0, 2.0, 4.0))); return clamp(dynamicRGB / abs1d(dot(q, vec2(-0.3, 0.1))) * 0.7 / result, vec3(0.0), vec3(1.0)); } float easeFade(float x) { return 1.0 - pow(2.0 * x - 1.0, 3.0); } float holeFade(float t, float life, float lo) { return easeFade(mod(t - lo, life) / life); } vec2 getPos(float t, float life, float offset, float lo) { float spiral = t * 0.5 + offset; float radius = 0.3 + 0.2 * sin(iTime + float(offset)); return vec2( cos(spiral) * radius * iResolution.x, sin(spiral) * radius * iResolution.y ); } vec4 mainVR(vec2 fragCoord, vec3 ro, vec3 rd) { vec3 dir = rd; vec3 from = ro; float s = 0.1, fade = 1.0; vec3 v = vec3(0.0); for (int r = 0; r < volsteps; r++) { vec3 p = from + s * dir * 0.5; p = abs(vec3(tile) - mod(p, vec3(tile * 2.0))); float pa = 0.0, a = 0.0; for (int i = 0; i < iterations; i++) { p = abs(p) / dot(p, p) - formuparam; p.xy *= rotMat(iTime * 0.02); a += abs(length(p) - pa); pa = length(p); } float dm = max(0.0, darkmatter - a * a * 0.002); a *= a * a; if (r > 6) fade *= 1.4 - dm; v += fade; v += vec3(s, s * s, s * s * s * s) * a * brightness * fade; fade *= distfading; s += 0.12; } v = mix(vec3(length(v)), v, saturation); return vec4(v * 0.015, 1.0); } float vignette(vec2 uv) { uv = uv * 2.0 - 1.0; return 1.0 - dot(uv, uv) * 0.3; } void main() { vec2 uv = vUv - 0.5; uv.y *= iResolution.y / iResolution.x; vec2 st = (vUv - 0.5) * 4.0; vec2 cPos = -1.0 + 2.0 * vUv; float cLength = length(cPos); vec3 rgb = vec3(0.6, 0.3, 0.9); // Purple-green-pink base vec3 col = OilEffect(st, rgb); vec4 o = vec4(1.0, 1.5, 2.0, 0.0); vec2 u = (vUv * 2.0 - 1.0) * iResolution / iResolution.y; vec2 w = u; float t = iTime * 0.3; for (float i = 0.0; i < 15.0; i++) { vec2 v = cos(t - 5.0 * u * pow(0.6 + 0.04 * i, i)) - 4.0 * u; u *= rotMat(i + t * 0.03); u += 0.008 * tanh(30.0 * dot(u, u) * cos(80.0 * vec2(u.y, u.x) + t)) + 0.25 * (0.6 + 0.04 * i) * u + 0.004 * cos(t + 3.0 * exp(-0.015 * dot(o, o))); w = u / (1.0 - 1.5 * dot(u, u)); o += (1.0 + cos(vec4(0.5, 1.5, 2.5, 0.0) + t)) / length((1.0 + i * dot(v, v)) * sin(w * 4.0 - 7.0 * vec2(u.y, u.x) + t)); } o = pow(1.0 - sqrt(exp(-o * o * o / 150.0)), 0.4 * o / o) - dot(u, u) / 200.0; vec3 final = vec3(0.0); float holeSize = iResolution.y / 8.0; float holeLife = 1.5; vec2 coord = vUv * iResolution - iResolution * 0.5; for (int i = 0; i < 30; i++) { vec3 col2 = 0.5 + 0.5 * cos(iTime + uv.xyx + vec3(float(i) * 0.5, float(i) + 2.0, float(i) * 2.0 + 8.0)); float lifeOffset = float(i) / 3.0; vec2 pos = getPos(iTime, holeLife, float(i) * 3.0, lifeOffset); float d = distance(coord, pos) / (holeSize * (0.5 + 0.5 * sin(float(i)))); d = 1.0 / d - 0.15; final += mix(vec3(0.0), col2, d) * holeFade(iTime, holeLife, lifeOffset); } vec2 pos = 0.5 - uv; pos.y /= iResolution.x / iResolution.y; float dist = 1.0 / length(pos); dist *= 0.15 + 0.05 * sin(iTime * 0.5); // Pulsing glow dist = pow(dist, 0.9); vec3 col2 = dist * vec3(0.8, 1.2, 1.8); vec3 from = vec3(1.0, 0.5, 0.5); float angle = atan(from.y, from.x) * 4.0; from.xy *= 2.0 * rotMat(floor(angle + 0.5) / 4.0); from.xy += (cPos / cLength) * cos(cLength * 10.0 - iTime * 1.5) * 0.04; vec4 vrColor = mainVR(vUv * iResolution, from, vec3(uv * zoom, 1.0)); vec3 bg = vec3(pseudoRandom(vUv * 10.0)) * 0.1 * vec3(0.2, 0.3, 0.5); vec4 fragColor = vrColor; fragColor *= vec4(final * vec3(0.5, 0.9, 1.2) + o.xyz, 1.0); fragColor += vec4(col2 + bg, 1.0); fragColor *= vignette(vUv); gl_FragColor = fragColor; } `; const material = new THREE.ShaderMaterial({ uniforms: uniforms, vertexShader: vertexShader, fragmentShader: fragmentShader }); const geometry = new THREE.PlaneGeometry(2, 2); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); window.addEventListener('resize', onWindowResize); } function onWindowResize() { renderer.setSize(window.innerWidth, window.innerHeight); uniforms.iResolution.value.set(window.innerWidth, window.innerHeight); } function animate() { requestAnimationFrame(animate); const time = clock.getElapsedTime(); uniforms.iTime.value = time; renderer.render(scene, camera); } function initAudio() { reverb = new Tone.Reverb({ decay: 4, preDelay: 0.1, wet: 0.4 }).toDestination(); delay = new Tone.PingPongDelay({ delayTime: "8n", feedback: 0.3, wet: 0.2 }).connect(reverb); filter = new Tone.Filter({ frequency: 800, type: "lowpass", rolloff: -12 }).connect(delay); synthLead = new Tone.PolySynth(Tone.Synth, { oscillator: { type: "triangle", }, envelope: { attack: 0.3, decay: 0.4, sustain: 0.3, release: 1.2 }, volume: -12 }).connect(filter); synthPad = new Tone.PolySynth(Tone.Synth, { oscillator: { type: "sawtooth", }, envelope: { attack: 1.5, decay: 0.5, sustain: 0.8, release: 2.0 }, volume: -16 }).connect(reverb); synthBass = new Tone.MonoSynth({ oscillator: { type: "sine", }, envelope: { attack: 0.1, decay: 0.3, sustain: 0.4, release: 0.8 }, volume: -14 }).connect(reverb); } function startMusic() { if (musicStarted) return; musicStarted = true; Tone.start().then(() => { Tone.Transport.bpm.value = 72; const fountainMelody = [ { note: "C5", time: "0:0:0", duration: "2n" }, { note: "E5", time: "0:2:0", duration: "4n" }, { note: "G5", time: "0:3:0", duration: "4n" }, { note: "A5", time: "1:0:0", duration: "2n" }, { note: "G5", time: "1:2:0", duration: "4n" }, { note: "F5", time: "1:3:0", duration: "4n" }, { note: "E5", time: "2:0:0", duration: "1n" }, { note: "D5", time: "3:0:0", duration: "4n" }, { note: "C5", time: "3:1:0", duration: "4n" }, { note: "G4", time: "3:2:0", duration: "2n" } ]; const padChords = [ { chord: ["C4", "E4", "G4"], time: "0:0:0", duration: "1n" }, { chord: ["F4", "A4", "C5"], time: "1:0:0", duration: "1n" }, { chord: ["G4", "B4", "D5"], time: "2:0:0", duration: "1n" }, { chord: ["C4", "E4", "G4"], time: "3:0:0", duration: "1n" } ]; const bassLine = [ { note: "C3", time: "0:0:0", duration: "2n" }, { note: "F3", time: "1:0:0", duration: "2n" }, { note: "G3", time: "2:0:0", duration: "2n" }, { note: "C3", time: "3:0:0", duration: "2n" } ]; fountainMelody.forEach(({ note, time, duration }) => { Tone.Transport.schedule((scheduleTime) => { synthLead.triggerAttackRelease(note, duration, scheduleTime); }, time); }); padChords.forEach(({ chord, time, duration }) => { Tone.Transport.schedule((scheduleTime) => { synthPad.triggerAttackRelease(chord, duration, scheduleTime); }, time); }); bassLine.forEach(({ note, time, duration }) => { Tone.Transport.schedule((scheduleTime) => { synthBass.triggerAttackRelease(note, duration, scheduleTime); }, time); }); Tone.Transport.loop = true; Tone.Transport.loopStart = "0:0:0"; Tone.Transport.loopEnd = "4:0:0"; Tone.Transport.start(); const volumePattern = new Tone.Pattern((time, volume) => { synthLead.volume.setValueAtTime(volume - 12, time); synthPad.volume.setValueAtTime(volume - 16, time); }, [-2, -1, -2, -3], "up").start(0); volumePattern.interval = "2n"; }); document.removeEventListener('click', startMusic); } const titleTag = document.createElement('div'); titleTag.textContent = 'Fountain of Youth'; titleTag.style.position = 'fixed'; titleTag.style.right = '12px'; titleTag.style.bottom = '28px'; titleTag.style.fontFamily = 'monospace, sans-serif'; titleTag.style.fontSize = '14px'; titleTag.style.color = '#ffffff'; titleTag.style.opacity = '0.85'; titleTag.style.pointerEvents = 'none'; titleTag.style.fontWeight = 'bold'; document.body.appendChild(titleTag); const artistTag = document.createElement('div'); artistTag.textContent = '口乃工-山卂𠘨 丂卂丅口丂卄工'; artistTag.style.position = 'fixed'; artistTag.style.right = '12px'; artistTag.style.bottom = '8px'; artistTag.style.fontFamily = 'monospace, sans-serif'; artistTag.style.fontSize = '12px'; artistTag.style.color = '#bbbbbb'; artistTag.style.opacity = '0.75'; artistTag.style.pointerEvents = 'none'; document.body.appendChild(artistTag); const musicIndicator = document.createElement('div'); musicIndicator.textContent = '🎵 Click to Play Music 🎵'; musicIndicator.style.position = 'fixed'; musicIndicator.style.left = '50%'; musicIndicator.style.top = '20px'; musicIndicator.style.transform = 'translateX(-50%)'; musicIndicator.style.fontFamily = 'monospace, sans-serif'; musicIndicator.style.fontSize = '16px'; musicIndicator.style.color = '#ffffff'; musicIndicator.style.opacity = '0.9'; musicIndicator.style.pointerEvents = 'none'; musicIndicator.style.textAlign = 'center'; musicIndicator.style.background = 'rgba(0, 0, 0, 0.7)'; musicIndicator.style.padding = '8px 16px'; musicIndicator.style.borderRadius = '8px'; musicIndicator.style.transition = 'opacity 2s ease-out'; document.body.appendChild(musicIndicator); document.addEventListener('click', () => { setTimeout(() => { musicIndicator.style.opacity = '0'; setTimeout(() => { document.body.removeChild(musicIndicator); }, 2000); }, 1000); }, { once: true });