From bc8a26c89e400f9c8e7495b9aa15abf1f0ea49ce Mon Sep 17 00:00:00 2001 From: iki Date: Sun, 31 May 2026 15:58:07 +0000 Subject: [PATCH] Upload files to "/" --- background.js | 469 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 469 insertions(+) create mode 100644 background.js diff --git a/background.js b/background.js new file mode 100644 index 0000000..d2cce59 --- /dev/null +++ b/background.js @@ -0,0 +1,469 @@ +(() => { + const vertexShaderSource = `#version 300 es + in vec2 a_position; + out vec2 v_position; + + void main() { + v_position = a_position; + gl_Position = vec4(a_position, 0.0, 1.0); + } + `; + + const fragmentShaderSource = `#version 300 es + precision highp float; + + uniform vec2 u_viewportSize; + uniform vec3 u_color1; + uniform vec3 u_color2; + uniform vec3 u_color3; + uniform vec3 u_color4; + uniform float u_colorSize; + uniform float u_colorSpacing; + uniform float u_colorRotation; + uniform float u_colorSpread; + uniform vec2 u_colorOffset; + uniform float u_displacement; + uniform float u_zoom; + uniform float u_spacing; + uniform float u_seed; + uniform vec2 u_transformPosition; + uniform float u_time; + + in vec2 v_position; + out vec4 outColor; + + vec4 permute(vec4 x) { + return mod(((x * 34.0) + 1.0) * x, 289.0); + } + + vec4 taylorInvSqrt(vec4 r) { + return 1.79284291400159 - 0.85373472095314 * r; + } + + float snoise(vec3 v) { + const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0); + const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); + + vec3 i = floor(v + dot(v, C.yyy)); + vec3 x0 = v - i + dot(i, C.xxx); + + vec3 g = step(x0.yzx, x0.xyz); + vec3 l = 1.0 - g; + vec3 i1 = min(g.xyz, l.zxy); + vec3 i2 = max(g.xyz, l.zxy); + + vec3 x1 = x0 - i1 + C.xxx; + vec3 x2 = x0 - i2 + C.yyy; + vec3 x3 = x0 - D.yyy; + + i = mod(i, 289.0); + vec4 p = permute(permute(permute( + i.z + vec4(0.0, i1.z, i2.z, 1.0)) + + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + + i.x + vec4(0.0, i1.x, i2.x, 1.0)); + + float n_ = 0.142857142857; + vec3 ns = n_ * D.wyz - D.xzx; + + vec4 j = p - 49.0 * floor(p * ns.z * ns.z); + vec4 x_ = floor(j * ns.z); + vec4 y_ = floor(j - 7.0 * x_); + + vec4 x = x_ * ns.x + ns.yyyy; + vec4 y = y_ * ns.x + ns.yyyy; + vec4 h = 1.0 - abs(x) - abs(y); + + vec4 b0 = vec4(x.xy, y.xy); + vec4 b1 = vec4(x.zw, y.zw); + + vec4 s0 = floor(b0) * 2.0 + 1.0; + vec4 s1 = floor(b1) * 2.0 + 1.0; + vec4 sh = -step(h, vec4(0.0)); + + vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; + vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; + + vec3 p0 = vec3(a0.xy, h.x); + vec3 p1 = vec3(a0.zw, h.y); + vec3 p2 = vec3(a1.xy, h.z); + vec3 p3 = vec3(a1.zw, h.w); + + vec4 norm = taylorInvSqrt(vec4( + dot(p0, p0), + dot(p1, p1), + dot(p2, p2), + dot(p3, p3) + )); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + + vec4 m = max(0.6 - vec4( + dot(x0, x0), + dot(x1, x1), + dot(x2, x2), + dot(x3, x3) + ), 0.0); + m = m * m; + return 42.0 * dot(m * m, vec4( + dot(p0, x0), + dot(p1, x1), + dot(p2, x2), + dot(p3, x3) + )); + } + + vec3 noiseDerivatives(vec3 p) { + float e = 0.035; + float n = snoise(p); + float dx = snoise(p + vec3(e, 0.0, 0.0)) - n; + float dy = snoise(p + vec3(0.0, e, 0.0)) - n; + float dz = snoise(p + vec3(0.0, 0.0, e)) - n; + return vec3(dx, dy, dz) / e; + } + + float grain(vec2 p) { + return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123); + } + + mat2 rotate2d(float angle) { + float s = sin(angle); + float c = cos(angle); + return mat2(c, -s, s, c); + } + + void main() { + vec2 position = v_position; + position.x *= min(1.0, u_viewportSize.x / u_viewportSize.y); + position.y *= min(1.0, u_viewportSize.y / u_viewportSize.x); + position /= u_zoom; + position += u_transformPosition; + + vec2 noisePosition = position * 0.5 + 0.5; + vec3 displacementNoise = noiseDerivatives(vec3(noisePosition, u_seed + u_time * 0.015)); + position += displacementNoise.xz * u_displacement * 0.12; + + vec2 offsetPosition = position; + offsetPosition -= u_colorOffset; + offsetPosition = mod(offsetPosition - u_spacing, vec2(u_spacing * 2.0)) - u_spacing; + offsetPosition = rotate2d(offsetPosition.x * 0.04 - u_colorRotation) * offsetPosition; + offsetPosition /= vec2(max(u_colorSize, 0.001)); + offsetPosition *= vec2(1.0 / max(u_colorSpread, 0.001), 1.0); + + vec3 color = vec3(0.0); + color = mix(u_color1, color, smoothstep(0.0, 1.0, distance(offsetPosition, vec2(0.0, u_colorSpacing * 1.5)))); + color = mix(u_color2, color, smoothstep(0.0, 1.0, distance(offsetPosition, vec2(0.0, u_colorSpacing * 0.5)))); + color = mix(u_color3, color, smoothstep(0.0, 1.0, distance(offsetPosition, vec2(0.0, -u_colorSpacing * 0.5)))); + color = mix(u_color4, color, smoothstep(0.0, 1.0, distance(offsetPosition, vec2(0.0, -u_colorSpacing * 1.5)))); + + float textureNoise = grain(v_position * u_viewportSize * 0.5 + u_time * 12.0); + float vignette = smoothstep(1.1, 0.16, length(v_position * vec2(0.82, 1.0))); + color += (textureNoise - 0.5) * 0.055; + color *= 0.74 + vignette * 0.42; + color = clamp(color, 0.0, 1.0); + + outColor = vec4(color, 1.0); + } + `; + + const defaultOptions = { + color1: "#16254b", + color2: "#23418a", + color3: "#aadfd9", + color4: "#e64f0f", + colorSize: 0.75, + colorSpacing: 0.52, + colorRotation: -0.381592653589793, + colorSpread: 4.52, + colorOffset: [-0.7741174697875977, -0.20644775390624992], + displacement: 5, + seed: 0.18, + position: [-0.2816110610961914, -0.43914794921875], + zoom: 0.72, + spacing: 4.27 + }; + + function compileShader(gl, type, source) { + const shader = gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + const message = gl.getShaderInfoLog(shader); + gl.deleteShader(shader); + throw new Error(message || "Shader compile failed"); + } + + return shader; + } + + function createProgram(gl) { + const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vertexShaderSource); + const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource); + const program = gl.createProgram(); + + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + gl.linkProgram(program); + + gl.deleteShader(vertexShader); + gl.deleteShader(fragmentShader); + + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + const message = gl.getProgramInfoLog(program); + gl.deleteProgram(program); + throw new Error(message || "Program link failed"); + } + + return program; + } + + function parseHexColor(value) { + const hex = String(value || "").trim().replace("#", ""); + const normalized = hex.length === 3 + ? hex.split("").map((char) => char + char).join("") + : hex.padEnd(6, "0").slice(0, 6); + const int = Number.parseInt(normalized, 16); + + if (!Number.isFinite(int)) return [0, 0, 0]; + + return [ + ((int >> 16) & 255) / 255, + ((int >> 8) & 255) / 255, + (int & 255) / 255 + ]; + } + + function parseNumber(value, fallback) { + const number = Number.parseFloat(value); + return Number.isFinite(number) ? number : fallback; + } + + function parseVector(value, fallback) { + if (!value) return fallback; + const parts = String(value).split(",").map((part) => Number.parseFloat(part.trim())); + return parts.length >= 2 && parts.every(Number.isFinite) ? [parts[0], parts[1]] : fallback; + } + + class FluidGradient extends HTMLElement { + static get observedAttributes() { + return [ + "color1", + "color2", + "color3", + "color4", + "colorsize", + "colorspacing", + "colorrotation", + "colorspread", + "coloroffset", + "displacement", + "seed", + "position", + "zoom", + "spacing" + ]; + } + + constructor() { + super(); + this.canvas = document.createElement("canvas"); + this.shadow = this.attachShadow({ mode: "open" }); + this.shadow.innerHTML = ` + + `; + this.shadow.append(this.canvas); + + this.options = structuredClone(defaultOptions); + this.pointer = { + x: 0.5, + y: 0.5, + targetX: 0.5, + targetY: 0.5, + displacement: defaultOptions.displacement, + seed: defaultOptions.seed + }; + this.bounds = { width: 1, height: 1, dpr: 1 }; + this.frame = 0; + this.start = performance.now(); + this.resizeObserver = new ResizeObserver(() => this.resize()); + this.handlePointerMove = this.handlePointerMove.bind(this); + this.render = this.render.bind(this); + } + + connectedCallback() { + this.gl = this.canvas.getContext("webgl2", { + alpha: false, + antialias: true, + depth: false, + preserveDrawingBuffer: false + }); + + if (!this.gl) { + this.style.background = "radial-gradient(circle at 30% 15%, #aadfd9, transparent 28%), radial-gradient(circle at 24% 78%, #e64f0f, transparent 38%), radial-gradient(circle at 70% 60%, #23418a, transparent 42%), #05090a"; + return; + } + + this.program = createProgram(this.gl); + this.createGeometry(); + this.collectUniforms(); + this.readAttributes(); + this.resizeObserver.observe(this); + this.addEventListener("pointermove", this.handlePointerMove, { passive: true }); + this.resize(); + this.play(); + } + + disconnectedCallback() { + cancelAnimationFrame(this.frame); + this.resizeObserver.disconnect(); + this.removeEventListener("pointermove", this.handlePointerMove); + } + + attributeChangedCallback() { + this.readAttributes(); + } + + createGeometry() { + const gl = this.gl; + const positions = new Float32Array([ + -1, -1, + 1, -1, + -1, 1, + -1, 1, + 1, -1, + 1, 1 + ]); + + this.buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + + const positionLocation = gl.getAttribLocation(this.program, "a_position"); + gl.enableVertexAttribArray(positionLocation); + gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); + } + + collectUniforms() { + const gl = this.gl; + this.uniforms = { + viewportSize: gl.getUniformLocation(this.program, "u_viewportSize"), + color1: gl.getUniformLocation(this.program, "u_color1"), + color2: gl.getUniformLocation(this.program, "u_color2"), + color3: gl.getUniformLocation(this.program, "u_color3"), + color4: gl.getUniformLocation(this.program, "u_color4"), + colorSize: gl.getUniformLocation(this.program, "u_colorSize"), + colorSpacing: gl.getUniformLocation(this.program, "u_colorSpacing"), + colorRotation: gl.getUniformLocation(this.program, "u_colorRotation"), + colorSpread: gl.getUniformLocation(this.program, "u_colorSpread"), + colorOffset: gl.getUniformLocation(this.program, "u_colorOffset"), + displacement: gl.getUniformLocation(this.program, "u_displacement"), + seed: gl.getUniformLocation(this.program, "u_seed"), + transformPosition: gl.getUniformLocation(this.program, "u_transformPosition"), + zoom: gl.getUniformLocation(this.program, "u_zoom"), + spacing: gl.getUniformLocation(this.program, "u_spacing"), + time: gl.getUniformLocation(this.program, "u_time") + }; + } + + readAttributes() { + this.options.color1 = parseHexColor(this.getAttribute("color1") || defaultOptions.color1); + this.options.color2 = parseHexColor(this.getAttribute("color2") || defaultOptions.color2); + this.options.color3 = parseHexColor(this.getAttribute("color3") || defaultOptions.color3); + this.options.color4 = parseHexColor(this.getAttribute("color4") || defaultOptions.color4); + this.options.colorSize = parseNumber(this.getAttribute("colorsize"), defaultOptions.colorSize); + this.options.colorSpacing = parseNumber(this.getAttribute("colorspacing"), defaultOptions.colorSpacing); + this.options.colorRotation = parseNumber(this.getAttribute("colorrotation"), defaultOptions.colorRotation); + this.options.colorSpread = parseNumber(this.getAttribute("colorspread"), defaultOptions.colorSpread); + this.options.colorOffset = parseVector(this.getAttribute("coloroffset"), defaultOptions.colorOffset); + this.options.displacement = parseNumber(this.getAttribute("displacement"), defaultOptions.displacement); + this.options.seed = parseNumber(this.getAttribute("seed"), defaultOptions.seed); + this.options.position = parseVector(this.getAttribute("position"), defaultOptions.position); + this.options.zoom = parseNumber(this.getAttribute("zoom"), defaultOptions.zoom); + this.options.spacing = parseNumber(this.getAttribute("spacing"), defaultOptions.spacing); + } + + resize() { + if (!this.gl) return; + const rect = this.getBoundingClientRect(); + const dpr = Math.min(window.devicePixelRatio || 1, 2); + const width = Math.max(1, Math.floor(rect.width * dpr)); + const height = Math.max(1, Math.floor(rect.height * dpr)); + + if (this.canvas.width !== width || this.canvas.height !== height) { + this.canvas.width = width; + this.canvas.height = height; + this.bounds = { width, height, dpr }; + this.gl.viewport(0, 0, width, height); + } + } + + handlePointerMove(event) { + const rect = this.getBoundingClientRect(); + const x = rect.width > 0 ? (event.clientX - rect.left) / rect.width : 0.5; + const y = rect.height > 0 ? (event.clientY - rect.top) / rect.height : 0.5; + + this.pointer.targetX = Math.min(1, Math.max(0, x)); + this.pointer.targetY = Math.min(1, Math.max(0, y)); + } + + play() { + if (this.frame) return; + this.frame = requestAnimationFrame(this.render); + } + + render(now) { + const gl = this.gl; + const pointer = this.pointer; + const options = this.options; + + pointer.x += (pointer.targetX - pointer.x) * 0.1; + pointer.y += (pointer.targetY - pointer.y) * 0.1; + pointer.displacement += ((pointer.x * 5) - pointer.displacement) * 0.1; + pointer.seed += (((pointer.y * 2) - 1) - pointer.seed) * 0.1; + + gl.clearColor(0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.useProgram(this.program); + + gl.uniform2f(this.uniforms.viewportSize, this.bounds.width, this.bounds.height); + gl.uniform3fv(this.uniforms.color1, options.color1); + gl.uniform3fv(this.uniforms.color2, options.color2); + gl.uniform3fv(this.uniforms.color3, options.color3); + gl.uniform3fv(this.uniforms.color4, options.color4); + gl.uniform1f(this.uniforms.colorSize, options.colorSize); + gl.uniform1f(this.uniforms.colorSpacing, options.colorSpacing); + gl.uniform1f(this.uniforms.colorRotation, options.colorRotation); + gl.uniform1f(this.uniforms.colorSpread, options.colorSpread); + gl.uniform2fv(this.uniforms.colorOffset, options.colorOffset); + gl.uniform1f(this.uniforms.displacement, pointer.displacement); + gl.uniform1f(this.uniforms.seed, pointer.seed); + gl.uniform2fv(this.uniforms.transformPosition, options.position); + gl.uniform1f(this.uniforms.zoom, options.zoom); + gl.uniform1f(this.uniforms.spacing, options.spacing); + gl.uniform1f(this.uniforms.time, (now - this.start) / 1000); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + this.frame = requestAnimationFrame(this.render); + } + } + + customElements.define("fluid-gradient", FluidGradient); +})();