Last active
February 27, 2025 05:33
-
-
Save xlab/1ae0d2f6aca1785b3b004ffc55435829 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Starship Shards</title> | |
<!-- Original code https://x.com/XorDev/status/1880344887033569682 --> | |
<!-- https://x.com/xlab_gg Asked Claude 3.5 to convert it to GLSL ES --> | |
<!-- Attribute original code to @XorDev if you reuse this code --> | |
<!-- Jan 17, 2025 --> | |
<style> | |
body { | |
margin: 0; | |
overflow: hidden; | |
background: #fff; | |
transition: background 0.3s; | |
} | |
canvas { | |
width: 100vw; | |
height: 100vh; | |
display: block; | |
} | |
#themeToggle { | |
position: fixed; | |
top: 20px; | |
right: 20px; | |
transform: none; | |
padding: 8px 16px; | |
border: none; | |
border-radius: 4px; | |
background: rgba(0, 0, 0, 0.1); | |
color: #000; | |
cursor: pointer; | |
font-family: system-ui, -apple-system, sans-serif; | |
backdrop-filter: blur(8px); | |
transition: all 0.3s; | |
} | |
#themeToggle:hover { | |
background: rgba(0, 0, 0, 0.2); | |
} | |
body.dark #themeToggle { | |
background: rgba(255, 255, 255, 0.1); | |
color: #fff; | |
} | |
body.dark #themeToggle:hover { | |
background: rgba(255, 255, 255, 0.2); | |
} | |
</style> | |
</head> | |
<body> | |
<button id="themeToggle">Switch Theme</button> | |
<canvas id="glCanvas"></canvas> | |
<script> | |
console.log("CREDITS:") | |
console.log("Original code https://x.com/XorDev/status/1880344887033569682") | |
console.log("https://x.com/xlab_gg Asked Claude 3.5 to convert it to GLSL ES") | |
console.log("Attribute original code to @XorDev if you reuse this code") | |
console.log("- Jan 17, 2025") | |
</script> | |
<script id="vertexShader" type="x-shader/x-vertex"> | |
attribute vec4 aVertexPosition; | |
void main() { | |
gl_Position = aVertexPosition; | |
} | |
</script> | |
<script id="fragmentShader" type="x-shader/x-fragment"> | |
precision highp float; | |
uniform vec2 r; | |
uniform float t; | |
uniform float alpha; | |
vec4 o; | |
// Simplex 2D noise | |
vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); } | |
float snoise2D(vec2 v) { | |
const vec4 C = vec4(0.211324865405187, | |
0.366025403784439, | |
-0.577350269189626, | |
0.024390243902439); | |
vec2 i = floor(v + dot(v, C.yy)); | |
vec2 x0 = v - i + dot(i, C.xx); | |
vec2 i1; | |
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); | |
vec4 x12 = x0.xyxy + C.xxzz; | |
x12.xy -= i1; | |
i = mod(i, 289.0); | |
vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) | |
+ i.x + vec3(0.0, i1.x, 1.0)); | |
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), | |
dot(x12.zw,x12.zw)), 0.0); | |
m = m*m; | |
m = m*m; | |
vec3 x = 2.0 * fract(p * C.www) - 1.0; | |
vec3 h = abs(x) - 0.5; | |
vec3 ox = floor(x + 0.5); | |
vec3 a0 = x - ox; | |
m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h); | |
vec3 g; | |
g.x = a0.x * x0.x + h.x * x0.y; | |
g.yz = a0.yz * x12.xz + h.yz * x12.yw; | |
return 130.0 * dot(m, g); | |
} | |
float tanh_float(float x) { | |
float exp2x = exp(2.0 * x); | |
return (exp2x - 1.0) / (exp2x + 1.0); | |
} | |
vec4 pow_vec4(vec4 v, float p) { | |
return vec4( | |
pow(v.x, p), | |
pow(v.y, p), | |
pow(v.z, p), | |
pow(v.w, p) | |
); | |
} | |
void main() { | |
vec2 FC = gl_FragCoord.xy; | |
vec2 p = (FC.xy-r*.5)/r.y*mat2(8,-6,6,8); | |
vec2 v; | |
o = vec4(0.0,0.0,0.0,alpha); | |
for(float i = 0.0; i < 50.0; i++) { | |
float f = 3.0 + snoise2D(p + vec2(t*7.0, 0.0)); | |
v = p + cos(i*i+(t+p.x*0.1)*0.03+i*vec2(11,9))*5.0; | |
o += (cos(sin(i)*vec4(1,2,3,1))+1.0)*exp(sin(i*i+t))/length(max(v,vec2(v.x*f*0.02,v.y))); | |
} | |
vec4 temp = pow_vec4(o/100.0, 1.5); | |
gl_FragColor = vec4( | |
tanh_float(temp.x), | |
tanh_float(temp.y), | |
tanh_float(temp.z), | |
tanh_float(temp.w) | |
); | |
} | |
</script> | |
<script> | |
let gl; | |
let program; | |
let canvas; | |
let rLocation; | |
let tLocation; | |
let alphaLocation; | |
let startTime; | |
let isDark = true; | |
document.body.classList.toggle('dark'); | |
function toggleTheme() { | |
isDark = !isDark; | |
document.body.classList.toggle('dark'); | |
if (gl) { | |
gl.clearColor(1.0, 1.0, 1.0, isDark ? 255.0 : 0.0); | |
} | |
} | |
function initGL() { | |
canvas = document.querySelector("#glCanvas"); | |
gl = canvas.getContext("webgl"); | |
gl.clearColor(1.0, 1.0, 1.0, 255.0); | |
if (!gl) { | |
alert("Unable to initialize WebGL."); | |
return; | |
} | |
const vsSource = document.querySelector("#vertexShader").text; | |
const fsSource = document.querySelector("#fragmentShader").text; | |
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vsSource); | |
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fsSource); | |
program = createProgram(gl, vertexShader, fragmentShader); | |
const positions = new Float32Array([ | |
-1.0, -1.0, | |
1.0, -1.0, | |
-1.0, 1.0, | |
1.0, 1.0, | |
]); | |
const positionBuffer = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); | |
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); | |
const aVertexPosition = gl.getAttribLocation(program, "aVertexPosition"); | |
gl.enableVertexAttribArray(aVertexPosition); | |
gl.vertexAttribPointer(aVertexPosition, 2, gl.FLOAT, false, 0, 0); | |
rLocation = gl.getUniformLocation(program, "r"); | |
tLocation = gl.getUniformLocation(program, "t"); | |
alphaLocation = gl.getUniformLocation(program, "alpha"); | |
startTime = Date.now(); | |
resize(); | |
window.addEventListener('resize', resize); | |
requestAnimationFrame(render); | |
} | |
function createShader(gl, type, source) { | |
const shader = gl.createShader(type); | |
gl.shaderSource(shader, source); | |
gl.compileShader(shader); | |
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { | |
console.error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)); | |
gl.deleteShader(shader); | |
return null; | |
} | |
return shader; | |
} | |
function createProgram(gl, vertexShader, fragmentShader) { | |
const program = gl.createProgram(); | |
gl.attachShader(program, vertexShader); | |
gl.attachShader(program, fragmentShader); | |
gl.linkProgram(program); | |
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { | |
console.error('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program)); | |
return null; | |
} | |
return program; | |
} | |
function resize() { | |
canvas.width = window.innerWidth; | |
canvas.height = window.innerHeight; | |
gl.viewport(0, 0, canvas.width, canvas.height); | |
} | |
function render() { | |
gl.clear(gl.COLOR_BUFFER_BIT); | |
gl.useProgram(program); | |
const time = (Date.now() - startTime) * 0.001; | |
gl.uniform1f(tLocation, time); | |
gl.uniform2f(rLocation, canvas.width, canvas.height); | |
gl.uniform1f(alphaLocation, isDark ? 255.0 : 0.0); | |
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); | |
requestAnimationFrame(render); | |
} | |
window.onload = function () { | |
initGL(); | |
document.getElementById('themeToggle').addEventListener('click', toggleTheme); | |
}; | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment