Files
hyperframes/skills/music-to-video/references/templates/held-message-living-field/index.html
T

486 lines
17 KiB
HTML

<!doctype html>
<html
lang="en"
data-composition-variables='[
{"id":"bgColor", "type":"color", "label":"Page background", "default":"#050507"},
{"id":"colorLow", "type":"color", "label":"Flow — shadow tone", "default":"#0a1430"},
{"id":"colorMid", "type":"color", "label":"Flow — mid tone", "default":"#6a6f9e"},
{"id":"colorHigh", "type":"color", "label":"Flow — highlight tone", "default":"#e8f0ff"},
{"id":"accentColor","type":"color", "label":"Core / glow accent", "default":"#aee4ff"},
{"id":"markText", "type":"string", "label":"Monogram", "default":"HF"},
{"id":"titleText", "type":"string", "label":"Title", "default":"HYPERFRAMES"},
{"id":"tagText", "type":"string", "label":"Tagline", "default":"HTML · SHADERS · MP4"},
{"id":"flowSpeed", "type":"number", "label":"Flow speed", "default":1, "min":0.1, "max":3, "step":0.1}
]'
>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>WebGL Textures Playground — Template</title>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap"
rel="stylesheet"
/>
<script src="https://cdn.jsdelivr.net/npm/three@0.147.0/build/three.min.js"></script>
<script src="../../motion-primitives/assets/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* --bg / --mark-top / --mark-bottom / --line are set from variables in JS */
html,
body {
background: var(--bg, #050507);
overflow: hidden;
font-family: "Inter", system-ui, sans-serif;
}
#root {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: var(--bg, #050507);
}
.sf-stage {
position: absolute;
inset: 0;
}
.sf-canvas {
position: absolute;
inset: 0;
width: 1920px;
height: 1080px;
display: block;
}
.sf-overlay {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 28px;
color: #fff;
text-align: center;
mix-blend-mode: screen;
pointer-events: none;
}
.sf-mark {
font-size: 280px;
font-weight: 800;
letter-spacing: -18px;
line-height: 1;
display: flex;
gap: 0;
opacity: 0;
filter: drop-shadow(0 0 60px rgba(180, 220, 255, 0.4));
}
.sf-mark-letter {
display: inline-block;
background: linear-gradient(
180deg,
var(--mark-top, #ffffff) 0%,
var(--mark-bottom, #8aa9ff) 100%
);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
transform: translateY(0);
}
.sf-rule {
width: 0;
height: 2px;
background: linear-gradient(
90deg,
transparent 0%,
var(--line, rgba(255, 255, 255, 0.9)) 50%,
transparent 100%
);
opacity: 0;
}
.sf-title {
font-size: 64px;
font-weight: 700;
letter-spacing: 18px;
opacity: 0;
clip-path: inset(0 100% 0 0);
text-transform: uppercase;
}
.sf-tag {
font-size: 18px;
font-weight: 500;
letter-spacing: 8px;
color: rgba(255, 255, 255, 0.65);
opacity: 0;
text-transform: uppercase;
}
.sf-grain {
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0.06;
mix-blend-mode: overlay;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
}
</style>
</head>
<body>
<div
data-hf-id="hf-a0d4"
id="root"
data-composition-id="webgl-textures-playground"
data-width="1920"
data-height="1080"
data-start="0"
data-duration="12"
data-root="true"
>
<div data-hf-id="hf-clsq" class="sf-stage">
<canvas data-hf-id="hf-yk51" class="sf-canvas" width="1920" height="1080"></canvas>
<div data-hf-id="hf-7z0g" class="sf-overlay">
<div data-hf-id="hf-yo0z" class="sf-mark"><!-- letters injected from markText --></div>
<div data-hf-id="hf-nig2" class="sf-rule"></div>
<div data-hf-id="hf-gjx6" class="sf-title"></div>
<div data-hf-id="hf-e7y8" class="sf-tag"></div>
</div>
<div data-hf-id="hf-ku55" class="sf-grain"></div>
</div>
</div>
<script>
(function () {
// ── 1. Resolve variables (read ONCE during init) ──────────────────
var defaults = {
bgColor: "#050507",
colorLow: "#0a1430",
colorMid: "#6a6f9e",
colorHigh: "#e8f0ff",
accentColor: "#aee4ff",
markText: "HF",
titleText: "HYPERFRAMES",
tagText: "HTML · SHADERS · MP4",
flowSpeed: 1,
};
var vars = Object.assign(
{},
defaults,
window.__hyperframes && window.__hyperframes.getVariables
? window.__hyperframes.getVariables()
: {},
);
// sRGB hex -> linear-light vec3 (the shader works in linear space and
// gamma-encodes at the end, so converting here makes the picked color
// land close to what a color picker shows).
function hexToLinear(hex) {
var h = String(hex).replace("#", "").trim();
if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
var r = parseInt(h.substr(0, 2), 16) / 255;
var g = parseInt(h.substr(2, 2), 16) / 255;
var b = parseInt(h.substr(4, 2), 16) / 255;
var lin = function (c) {
return Math.pow(c, 2.2);
};
return new THREE.Vector3(lin(r), lin(g), lin(b));
}
var root = document.getElementById("root");
var canvas = root.querySelector(".sf-canvas");
var W = 1920;
var H = 1080;
// ── 2. Apply text + CSS-driven colors ─────────────────────────────
var docEl = document.documentElement;
docEl.style.setProperty("--bg", vars.bgColor);
docEl.style.setProperty("--mark-top", vars.colorHigh);
docEl.style.setProperty("--mark-bottom", vars.colorMid);
docEl.style.setProperty("--line", vars.colorHigh);
var markEl = root.querySelector(".sf-mark");
markEl.innerHTML = "";
String(vars.markText)
.split("")
.forEach(function (ch) {
var span = document.createElement("span");
span.className = "sf-mark-letter";
span.textContent = ch;
markEl.appendChild(span);
});
root.querySelector(".sf-title").textContent = vars.titleText;
root.querySelector(".sf-tag").textContent = vars.tagText;
// ── 3. WebGL flow field ───────────────────────────────────────────
var renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: false,
alpha: false,
preserveDrawingBuffer: true,
});
renderer.setPixelRatio(1);
renderer.setSize(W, H, false);
var scene = new THREE.Scene();
var camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
var uniforms = {
uTime: { value: 0.0 },
uPhase: { value: 0.0 },
uReveal: { value: 0.0 },
uChromAb: { value: 0.0 },
uVignette: { value: 0.0 },
uIntensity: { value: 0.0 },
uResolution: { value: new THREE.Vector2(W, H) },
uSpeed: { value: Number(vars.flowSpeed) || 1.0 },
// ── COLOR CONTROLS (driven by the composition variables) ──
uColLow: { value: hexToLinear(vars.colorLow) },
uColMid: { value: hexToLinear(vars.colorMid) },
uColHigh: { value: hexToLinear(vars.colorHigh) },
uAccent: { value: hexToLinear(vars.accentColor) },
};
var vert = [
"varying vec2 vUv;",
"void main(){",
" vUv = uv;",
" gl_Position = vec4(position.xy, 0.0, 1.0);",
"}",
].join("\n");
var frag = [
"precision highp float;",
"varying vec2 vUv;",
"uniform float uTime;",
"uniform float uPhase;",
"uniform float uReveal;",
"uniform float uChromAb;",
"uniform float uVignette;",
"uniform float uIntensity;",
"uniform float uSpeed;",
"uniform vec2 uResolution;",
"uniform vec3 uColLow;",
"uniform vec3 uColMid;",
"uniform vec3 uColHigh;",
"uniform vec3 uAccent;",
"float hash21(vec2 p){",
" p = fract(p * vec2(123.34, 456.21));",
" p += dot(p, p + 45.32);",
" return fract(p.x * p.y);",
"}",
"float noise2(vec2 p){",
" vec2 i = floor(p);",
" vec2 f = fract(p);",
" vec2 u = f * f * (3.0 - 2.0 * f);",
" float a = hash21(i);",
" float b = hash21(i + vec2(1.0, 0.0));",
" float c = hash21(i + vec2(0.0, 1.0));",
" float d = hash21(i + vec2(1.0, 1.0));",
" return mix(mix(a,b,u.x), mix(c,d,u.x), u.y);",
"}",
"float fbm(vec2 p){",
" float v = 0.0;",
" float a = 0.5;",
" for(int i=0;i<5;i++){",
" v += a * noise2(p);",
" p *= 2.02;",
" a *= 0.5;",
" }",
" return v;",
"}",
"vec2 curl(vec2 p, float t){",
" float e = 0.6;",
" float n1 = fbm(p + vec2(0.0, e) + t*0.18);",
" float n2 = fbm(p + vec2(0.0, -e) + t*0.18);",
" float n3 = fbm(p + vec2( e, 0.0) + t*0.18);",
" float n4 = fbm(p + vec2(-e, 0.0) + t*0.18);",
" return vec2(n1 - n2, -(n3 - n4));",
"}",
// 3-stop gradient palette built from the color variables.
// t≈0 -> shadow, t≈0.5 -> mid, t≈1 -> highlight.
"vec3 palette(float t){",
" t = clamp(t, 0.0, 1.0);",
" vec3 lowMid = mix(uColLow, uColMid, smoothstep(0.0, 0.5, t));",
" return mix(lowMid, uColHigh, smoothstep(0.5, 1.0, t));",
"}",
"vec3 sampleField(vec2 uv, float t, float energy){",
" vec2 p = (uv - 0.5) * vec2(uResolution.x/uResolution.y, 1.0) * 2.4;",
" vec2 c = curl(p * 0.9, t);",
" vec2 q = p + c * (0.45 + 0.55 * energy);",
" float flow = fbm(q * 1.6 + vec2(t * 0.11, -t * 0.07));",
" float bands = sin(flow * 14.0 + t * 1.1);",
" bands = smoothstep(0.0, 1.0, 0.5 + 0.5 * bands);",
" float r = length(p);",
" float core = smoothstep(1.5, 0.05, r) * (0.6 + 0.4 * energy);",
" float glow = exp(-r * 1.6) * (0.55 + 0.45 * energy);",
" float k = clamp(flow * 0.85 + 0.15 * bands + 0.25 * core + 0.6 * glow, 0.0, 1.4);",
" vec3 col = palette(k * 0.9 + 0.1 * sin(t * 0.4));",
" col *= 0.7 + 0.6 * energy;",
" col += uAccent * core * 0.55;",
" return col;",
"}",
"void main(){",
" vec2 uv = vUv;",
" float t = uTime * uSpeed;",
" float e = uIntensity;",
" vec2 dir = normalize(uv - 0.5 + 1e-6);",
" float ca = uChromAb * (0.004 + 0.012 * length(uv - 0.5));",
" vec3 cR = sampleField(uv + dir * ca, t, e);",
" vec3 cG = sampleField(uv, t, e);",
" vec3 cB = sampleField(uv - dir * ca, t, e);",
" vec3 col = vec3(cR.r, cG.g, cB.b);",
" float lum = dot(col, vec3(0.299, 0.587, 0.114));",
" col = mix(vec3(lum), col, 1.05);",
" float rev = uReveal;",
" float sweep = smoothstep(0.0, 0.6, rev) * (1.0 - smoothstep(0.6, 1.0, rev));",
" col += uColHigh * sweep * 0.35;",
" vec3 phaseTint = mix(vec3(0.85, 0.92, 1.05), vec3(1.05, 0.95, 0.85), uPhase);",
" col *= phaseTint;",
" float v = length(uv - 0.5);",
" col *= 1.0 - smoothstep(0.45, 0.95, v) * uVignette;",
" col = col / (col + 1.0);",
" col = pow(col, vec3(1.0/2.2));",
" gl_FragColor = vec4(col, 1.0);",
"}",
].join("\n");
var mat = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vert,
fragmentShader: frag,
});
var geo = new THREE.PlaneGeometry(2, 2);
var mesh = new THREE.Mesh(geo, mat);
scene.add(mesh);
function renderFrame() {
renderer.render(scene, camera);
}
renderFrame();
// Continuous loop: ensures the WebGL backbuffer always has fresh
// pixels when puppeteer takes a screenshot. All visible state comes
// from uniforms driven by the GSAP timeline, so output is fully
// determined by the timeline position (deterministic across renders).
function loop() {
renderFrame();
window.requestAnimationFrame(loop);
}
window.requestAnimationFrame(loop);
// ── 4. Deterministic timeline (unchanged motion design) ───────────
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({
paused: true,
onUpdate: renderFrame,
});
tl.to(uniforms.uTime, { value: 12.0, duration: 12, ease: "none" }, 0);
tl.to(uniforms.uIntensity, { value: 0.55, duration: 3.0, ease: "sine.inOut" }, 0);
tl.to(uniforms.uIntensity, { value: 1.0, duration: 1.2, ease: "power2.in" }, 3.0);
tl.to(uniforms.uIntensity, { value: 0.55, duration: 3.0, ease: "power2.out" }, 4.2);
tl.to(uniforms.uIntensity, { value: 0.3, duration: 4.8, ease: "sine.inOut" }, 7.2);
tl.fromTo(
uniforms.uChromAb,
{ value: 0.0 },
{ value: 0.4, duration: 3.0, ease: "sine.in" },
0,
);
tl.to(uniforms.uChromAb, { value: 1.6, duration: 1.0, ease: "power3.in" }, 3.0);
tl.to(uniforms.uChromAb, { value: 0.25, duration: 1.5, ease: "power2.out" }, 4.0);
tl.to(uniforms.uChromAb, { value: 0.18, duration: 6.5, ease: "sine.inOut" }, 5.5);
tl.fromTo(
uniforms.uReveal,
{ value: 0.0 },
{ value: 1.0, duration: 1.2, ease: "none" },
3.4,
);
tl.set(uniforms.uReveal, { value: 0.0 }, 4.6);
tl.to(uniforms.uPhase, { value: 0.6, duration: 5.0, ease: "sine.inOut" }, 1.5);
tl.to(uniforms.uPhase, { value: 0.15, duration: 5.5, ease: "sine.inOut" }, 6.5);
tl.to(uniforms.uVignette, { value: 0.85, duration: 3.0, ease: "power2.out" }, 4.2);
var mark = root.querySelector(".sf-mark");
var letters = root.querySelectorAll(".sf-mark-letter");
var rule = root.querySelector(".sf-rule");
var title = root.querySelector(".sf-title");
var tag = root.querySelector(".sf-tag");
tl.fromTo(
mark,
{
opacity: 0,
scale: 1.18,
filter: "blur(24px) drop-shadow(0 0 60px rgba(180,220,255,0.4))",
},
{
opacity: 1,
scale: 1.0,
filter: "blur(0px) drop-shadow(0 0 80px rgba(180,220,255,0.55))",
duration: 0.9,
ease: "power3.out",
},
3.6,
);
tl.fromTo(
letters,
{ y: 80 },
{ y: 0, duration: 1.0, ease: "expo.out", stagger: 0.08 },
3.6,
);
tl.fromTo(
rule,
{ width: 0, opacity: 0 },
{ width: 360, opacity: 0.85, duration: 0.8, ease: "power2.out" },
5.1,
);
tl.fromTo(
title,
{ opacity: 0, clipPath: "inset(0 100% 0 0)" },
{ opacity: 1, clipPath: "inset(0 0% 0 0)", duration: 0.95, ease: "power3.out" },
5.3,
);
tl.fromTo(
tag,
{ opacity: 0, y: 12 },
{ opacity: 0.7, y: 0, duration: 0.8, ease: "sine.out" },
6.0,
);
tl.to(mark, { scale: 1.04, duration: 5.0, ease: "sine.inOut" }, 7.0);
tl.to([mark, rule, title, tag], { opacity: 0, duration: 0.6, ease: "power1.in" }, 11.4);
window.__timelines["webgl-textures-playground"] = tl;
})();
</script>
</body>
</html>