From db6b79edbfca3ab7049af2492acd567b099559f5 Mon Sep 17 00:00:00 2001 From: John Bargman Date: Wed, 15 Apr 2026 08:23:09 +0000 Subject: agentic ai; is so; fucking cool; omg --- js/webgl-bg.js | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 js/webgl-bg.js (limited to 'js/webgl-bg.js') diff --git a/js/webgl-bg.js b/js/webgl-bg.js new file mode 100644 index 0000000..081b525 --- /dev/null +++ b/js/webgl-bg.js @@ -0,0 +1,190 @@ +// WebGL background effect for Nixtaml website +// Particle system with subtle animations fitting "crash-over-burn" theme + +(function() { + // Check for reduced motion preference + const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; + + // Check if mobile (simplify or disable for performance) + const isMobile = window.innerWidth < 768; + + // If reduced motion or mobile, skip WebGL + if (prefersReducedMotion || isMobile) { + console.log('WebGL background disabled: reduced motion or mobile'); + return; + } + + // Check WebGL support + const canvas = document.createElement('canvas'); + const gl = canvas.getContext('webgl2') || canvas.getContext('webgl'); + if (!gl) { + console.log('WebGL not supported, using fallback'); + return; + } + + // Append canvas to body + canvas.id = 'webgl-bg'; + document.body.insertBefore(canvas, document.body.firstChild); + + // Set canvas size + function resizeCanvas() { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + gl.viewport(0, 0, canvas.width, canvas.height); + } + resizeCanvas(); + window.addEventListener('resize', resizeCanvas); + + // Load shader + function loadShader(gl, type, source) { + const shader = gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + console.error('Shader compile error:', gl.getShaderInfoLog(shader)); + gl.deleteShader(shader); + return null; + } + return shader; + } + + // Create program + const vertexShaderSource = ` + attribute vec2 a_position; + attribute float a_size; + attribute vec3 a_color; + + uniform mat4 u_matrix; + uniform float u_time; + + varying vec3 v_color; + + void main() { + vec4 position = vec4(a_position, 0.0, 1.0); + position.x += sin(u_time * 0.001 + a_position.y * 0.01) * 0.05; + position.y += cos(u_time * 0.0005 + a_position.x * 0.01) * 0.02; + + gl_Position = u_matrix * position; + gl_PointSize = a_size; + + v_color = a_color; + } + `; + + const fragmentShaderSource = ` + precision mediump float; + + varying vec3 v_color; + + void main() { + vec2 center = gl_PointCoord - vec2(0.5); + float dist = length(center); + + if (dist > 0.5) { + discard; + } + + float alpha = 1.0 - smoothstep(0.0, 0.5, dist); + gl_FragColor = vec4(v_color, alpha * 0.2); + } + `; + + const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vertexShaderSource); + const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource); + + const program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + gl.linkProgram(program); + + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + console.error('Program link error:', gl.getProgramInfoLog(program)); + return; + } + + gl.useProgram(program); + + // Get attribute locations + const positionLocation = gl.getAttribLocation(program, 'a_position'); + const sizeLocation = gl.getAttribLocation(program, 'a_size'); + const colorLocation = gl.getAttribLocation(program, 'a_color'); + + // Get uniform locations + const matrixLocation = gl.getUniformLocation(program, 'u_matrix'); + const timeLocation = gl.getUniformLocation(program, 'u_time'); + + // Create particles + const numParticles = 200; + const positions = new Float32Array(numParticles * 2); + const sizes = new Float32Array(numParticles); + const colors = new Float32Array(numParticles * 3); + + for (let i = 0; i < numParticles; i++) { + positions[i * 2] = (Math.random() - 0.5) * 4; // Spread across screen + positions[i * 2 + 1] = (Math.random() - 0.5) * 4; + sizes[i] = Math.random() * 5 + 2; // Size 2-7 + // Orange/red colors + colors[i * 3] = Math.random() * 0.5 + 0.5; // R + colors[i * 3 + 1] = Math.random() * 0.3 + 0.2; // G + colors[i * 3 + 2] = Math.random() * 0.1; // B + } + + // Create buffers + const positionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + + const sizeBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, sizeBuffer); + gl.bufferData(gl.ARRAY_BUFFER, sizes, gl.STATIC_DRAW); + + const colorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW); + + // Enable blending + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + + // Render loop + let startTime = Date.now(); + function render() { + const time = Date.now() - startTime; + + // Clear + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + + // Set uniforms + const matrix = new Float32Array([ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]); + gl.uniformMatrix4fv(matrixLocation, false, matrix); + gl.uniform1f(timeLocation, time); + + // Bind position buffer + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.enableVertexAttribArray(positionLocation); + gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); + + // Bind size buffer + gl.bindBuffer(gl.ARRAY_BUFFER, sizeBuffer); + gl.enableVertexAttribArray(sizeLocation); + gl.vertexAttribPointer(sizeLocation, 1, gl.FLOAT, false, 0, 0); + + // Bind color buffer + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.enableVertexAttribArray(colorLocation); + gl.vertexAttribPointer(colorLocation, 3, gl.FLOAT, false, 0, 0); + + // Draw + gl.drawArrays(gl.POINTS, 0, numParticles); + + requestAnimationFrame(render); + } + + render(); +})(); \ No newline at end of file -- cgit v1.2.3