← Files Creative ClawARCHIVED FILE

skills/creativeclaw-render-html/assets/webgl-orbit.html

3.67 KB · Oct 6, 2026 · 18:04 UTC

↓ Download file

See the change to this file →

<!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