← Files Creative ClawARCHIVED FILE
skills/creativeclaw-render-html-video/assets/webgl-orbit.html
3.67 KB · Sep 30, 2026 · 23:19 UTC
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=640,height=360">
<title>Seekable WebGL orbit — four seconds</title>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
html,body{margin:0;overflow:hidden;background:#081021}
#root{position:relative;width:640px;height:360px;overflow:hidden}
#plate{position:absolute;inset:0;width:640px;height:360px;display:block}
h1{position:absolute;left:32px;top:24px;margin:0;color:#fff;
font:600 28px/1.2 system-ui,sans-serif;letter-spacing:-.5px}
</style>
</head>
<body>
<main id="root" data-composition-id="orbit" data-width="640"
data-height="360" data-duration="4">
<canvas id="plate" width="640" height="360"></canvas>
<h1>Motion, precisely timed.</h1>
</main>
<script>
// One document. No free-running loop; HyperFrames owns time.
const canvas = document.getElementById('plate');
const gl = canvas.getContext('webgl', {
alpha: false, antialias: false, preserveDrawingBuffer: true
});
if (!gl) throw new Error('WebGL context unavailable');
function compile(type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw new Error(gl.getShaderInfoLog(shader) || 'Shader compilation failed');
}
return shader;
}
const vertex = compile(gl.VERTEX_SHADER, `
attribute vec2 a_position;
void main() { gl_Position = vec4(a_position, 0.0, 1.0); }
`);
const fragment = compile(gl.FRAGMENT_SHADER, `
precision mediump float;
uniform vec2 u_resolution;
uniform float u_time;
void main() {
vec2 p = (2.0 * gl_FragCoord.xy - u_resolution) / u_resolution.y;
float angle = u_time * 1.57079632679;
vec2 center = vec2(cos(angle), sin(angle)) * 0.58;
float orb = 1.0 - smoothstep(0.10, 0.13, length(p - center));
float ring = 1.0 - smoothstep(0.005, 0.014, abs(length(p) - 0.58));
float glow = 0.018 / (0.03 + dot(p - center, p - center));
vec3 color = vec3(0.03, 0.06, 0.12)
+ ring * vec3(0.08, 0.18, 0.28)
+ (orb + glow) * vec3(0.12, 0.64, 0.86);
gl_FragColor = vec4(color, 1.0);
}
`);
const program = gl.createProgram();
gl.attachShader(program, vertex);
gl.attachShader(program, fragment);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw new Error(gl.getProgramInfoLog(program) || 'Shader link failed');
}
gl.useProgram(program);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER,
new Float32Array([-1,-1, 1,-1, -1,1, 1,1]), gl.STATIC_DRAW);
const position = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(position);
gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
const timeUniform = gl.getUniformLocation(program, 'u_time');
gl.uniform2f(gl.getUniformLocation(program, 'u_resolution'), 640, 360);
function renderAt(t) {
gl.viewport(0, 0, 640, 360);
gl.uniform1f(timeUniform, t);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
let time = 0;
const driver = {
get t() { return time; },
set t(value) { time = value; renderAt(value); }
};
const tl = gsap.timeline({paused: true});
tl.fromTo(driver, {t: 0},
{t: 4, duration: 4, ease: 'none', lazy: false}, 0);
renderAt(0);
window.__timelines = window.__timelines || {};
window.__timelines.orbit = tl;
</script>
</body>
</html>
SHA-256: fa8415c45c2d0a371bd88e901b282678b824e22bbbb9c006573f4ccaafa7ebcb