\n
\n\n
\n

MELANCOLÍA DIGITAL

\n

fragmentos de una memoria que nunca existió

\n
mueve el cursor para agitar el vacío
\n
\n
\n
", "css": "body, html { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: #050505; font-family: 'Courier New', Courier, monospace; color: #a0a0a0; }\n#app { width: 100vw; height: 100vh; display: flex; justify-content: center; align-items: center; }\n#canvas-container { position: relative; width: 100%; height: 100%; }\n#glCanvas { width: 100%; height: 100%; display: block; }\n#overlay { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; pointer-events: none; z-index: 10; }\n.glitch { font-size: 3rem; font-weight: bold; position: relative; color: #fff; text-transform: uppercase; letter-spacing: 5px; animation: glitch-skew 1s infinite linear alternate-reverse; }\n.glitch::before, .glitch::after { content: attr(data-text); position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: transparent; }\n.glitch::before { left: 2px; text-shadow: -2px 0 #ff00c1; clip: rect(44px, 450px, 56px, 0); animation: glitch-anim 5s infinite linear alternate-reverse; }\n.glitch::after { left: -2px; text-shadow: -2px 0 #00fff9; clip: rect(44px, 450px, 56px, 0); animation: glitch-anim2 5s infinite linear alternate-reverse; }\n.subtitle { font-size: 1rem; margin-top: 10px; opacity: 0.6; letter-spacing: 2px; }\n.instructions { font-size: 0.7rem; margin-top: 30px; opacity: 0.3; }\n@keyframes glitch-anim { 0% { clip: rect(31px, 9999px, 94px, 0); } 20% { clip: rect(62px, 9999px, 42px, 0); } 40% { clip: rect(16px, 9999px, 78px, 0); } 60% { clip: rect(87px, 9999px, 11px, 0); } 80% { clip: rect(53px, 9999px, 49px, 0); } 100% { clip: rect(12px, 9999px, 65px, 0); } }\n@keyframes glitch-anim2 { 0% { clip: rect(65px, 9999px, 12px, 0); } 20% { clip: rect(42px, 9999px, 62px, 0); } 40% { clip: rect(78px, 9999px, 16px, 0); } 60% { clip: rect(11px, 9999px, 87px, 0); } 80% { clip: rect(49px, 9999px, 53px, 0); } 100% { clip: rect(0px, 9999px, 31px, 0); } }\n@keyframes glitch-skew { 0% { transform: skew(0deg); } 10% { transform: skew(0deg); } 20% { transform: skew(-1deg); } 30% { transform: skew(1deg); } 40% { transform: skew(0deg); } 100% { transform: skew(0deg); } }", "js": "const canvas = document.getElementById('glCanvas');\nconst gl = canvas.getContext('webgl');\n\nif (!gl) {\n alert('WebGL not supported');\n}\n\nconst vsSource = `\nattribute vec4 aVertexPosition;\nvoid main() {\n gl_Position = aVertexPosition;\n}\n`;\n\nconst fsSource = `\nprecision highp float;\nuniform float uTime;\nuniform vec2 uResolution;\nuniform vec2 uMouse;\n\nfloat hash(float n) {\n return fract(sin(n) * 43758.5453123);\n}\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / uResolution.xy;\n vec2 p = (gl_FragCoord.xy * 2.0 - uResolution.xy) / min(uResolution.y, uResolution.x);\n \n float t = uTime * 0.2;\n \n float noise = 0.0;\n vec2 noiseUV = p * 2.0;\n for(float i = 1.0; i < 4.0; i++) {\n noise += 0.5 / i * sin(noiseUV.x * i * 2.0 + t);\n noiseUV.x += sin(noiseUV.y + t) * 0.5;\n }\n \n float dist = length(p - uMouse);\n float glow = 0.05 / (dist + 0.1);\n \n float ripple = sin(dist * 10.0 - t * 3.0) * 0.01;\n vec2 distortedUV = uv + vec2(ripple);\n \n float line = abs(sin(distortedUV.y * 100.0 + t)) * 0.1;\n \n vec3 color = vec3(0.05, 0.05, 0.1) * (1.0 - length(uv - 0.5));\n color += vec3(0.1, 0.2, 0.3) * noise * 0.5;\n color += vec3(0.4, 0.6, 0.9) * glow * 0.3;\n color += vec3(0.5, 0.5, 0.5) * line * 0.2;\n \n gl_FragColor = vec4(color, 1.0);\n}\n`;\n\nfunction createShader(gl, type, source) {\n const shader = gl.createShader(type);\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n if (!gl.getShaderParameter(shader, COMPILATION_STATUS)) {\n console.error(gl.getShaderInfoLog(shader));\n gl.deleteShader(shader);\n return null;\n }\n return shader;\n}\n\nconst vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource);\nconst fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource);\n\nconst program = gl.createProgram();\ngl.attachShader(program, vertexShader);\ngl.attachShader(program, fragmentShader);\ngl.linkProgram(program);\n\nconst positionBuffer = gl.createBuffer();\ngl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);\nconst positions = new Float32Array([-1, 1, -1, -1, 1, 1, 1, -1]);\ngl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);\n\nconst positionLocation = gl.getAttribLocation(program, 'aVertexPosition');\nconst uTimeLocation = gl.getUniformLocation(program, 'uTime');\nconst uResolutionLocation = gl.getUniformLocation(program, 'uResolution');\nconst uMouseLocation = gl.getUniformLocation(program, 'uMouse');\n\nfunction render(time) {\n time *= 0.001;\n \n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n gl.viewport(0, 0, canvas.width, canvas.height);\n \n gl.useProgram(program);\n \n gl.enableVertexAttribArray(positionLocation);\n gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);\n gl.vertexAttribPointer(positionLocation, 0, 3, false, 0, 0);\n \n gl.uniform1f(uTimeLocation, time);\n gl.uniform1f(uResolutionLocation, canvas.width); // This is a simplification, but it's passed as a vec2 in shader\n gl.uniform2f(uResolutionLocation, canvas.width, canvas.height);\n \n const mouseX = (window.mouseX || 0) / canvas.width * 2 - 1;\n const mouseY = 1 - (window.mouseY || 0) / canvas.height * 2; // Error here in the original logic, fixing it below\n // Use screen space coordinates in JS for the shader\n const screenMouseX = (window.mouseX || 0) / canvas.width * 2 - 1; \n const screenMouseY = 1 - (window.mouseY || 0) / canvas.height * 2;\n \n // Let's just map to the coordinate system used in the shader (p = (gl_FragCoord.xy * 2.0 - uResolution.xy) / min(uResolution.y, uResolution.x))\n const finalMouseX = ((window.mouseX || 0) * 2.0 - canvas.width) / Math.min(canvas.width, canvas.height);\n const finalMouseY = ((window.innerHeight - (window.mouseY || 0)) * 2.0 - canvas.height) / Math.min(canvas.width, canvas.height);\n \n gl.uniform2f(uMouseLocation, finalMouseX, finalMouseY);\n \n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n requestAnimationFrame(render);\n}\n\nwindow.addEventListener('mousemove', (e) =>