fix(registry): clamp filmed-screen's curve and vignette (#5501)

* fix(registry): clamp filmed-screen's curve and vignette

Post-merge review of #5499:

- curve is clamped at 0: a negative value samples outside the frame and
  folds over below about -0.32.
- the vignette factor is clamped at 0: above vignette ~1.11 it went
  negative in the corners.
- README: a "previous look" line with the pre-#5499 values, noting that
  the LED fade-in threshold and the trace of fringing on the focused
  element are hard-coded.

The demo renders the same: at the defaults neither clamp is reached.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(registry): point filmed-screen's catalog page at its recorded preview

The catalog builds this item's payload as unsupported (it needs
canvas-draw-element), and without preview.video the page fell back to the
flag notice: "Needs chrome://flags/#canvas-draw-element to render live"
under a "Recorded preview" caption, with no video, whatever the visitor's
flags. The manifest now names the CDN video and poster the way the
liquid-glass blocks do; the regenerated page carries video= and poster=
instead of needsFlag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vance Ingalls
2026-10-12 20:52:18 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 86ff462d21
commit 37a79e9371
5 changed files with 30 additions and 6 deletions
@@ -90,6 +90,13 @@ HFFilmedScreen.mount({
| fadeOut | true | Fades to black over the last 0.4 s of the owning composition. |
| zIndex | 10 | z-index of the lens output canvas. |
**Previous look.** Before these defaults the lens used `blur: 0.9`, `rack: 0.7`, `vignette: 0.42`,
`aberration: 1`, `led: 0.9` and `curve: 0`; pass those to get close to it. Two changes have no
option and can't be restored: the LED grid now fades in only once a cell spans about 4 output
pixels, and a quarter of the fringing stays on the focused element.
`curve` is clamped at 0, and the vignette never darkens past black, so very large values are safe.
Pass `document.querySelectorAll(...)` from your own snippet rather than a selector string. The
library initialises once per page, so its own `document` belongs to whichever copy loaded first;
your query is scoped to your composition, which is what keeps two instances (or the same
+3 -2
View File
@@ -343,7 +343,7 @@ void main() {
col *= mix(vec3(1.0), led * 1.3, ledAmt * uLed * (1.0 - 0.7 * sharp));
col = col * (1.0 + uCut * 1.1) + uCut * 0.06;
vec2 v = s0 * vec2(0.85, 1.15);
col *= 1.0 - uVig * dot(v, v);
col *= max(0.0, 1.0 - uVig * dot(v, v));
col *= 1.0 - (0.035 - 0.035 * sin(coord.y * 0.9 - uTime * 55.0));
col += (hash(coord + frameSeed * 1.31) - 0.5) * 0.06;
col *= 1.0 - uOut;
@@ -781,7 +781,8 @@ void main() {
gl.uniform1f(loc.uVig, opts.vignette == null ? 1.0 : opts.vignette);
gl.uniform1f(loc.uCA, opts.aberration == null ? 2.2 : opts.aberration);
gl.uniform1f(loc.uLed, opts.led == null ? 0.45 : opts.led);
gl.uniform1f(loc.uCurve, opts.curve == null ? 0.15 : opts.curve);
// A negative curve would sample outside the frame and fold over.
gl.uniform1f(loc.uCurve, Math.max(0, opts.curve == null ? 0.15 : opts.curve));
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
fs = null;
return true;
+3 -2
View File
@@ -191,7 +191,7 @@ void main() {
col *= mix(vec3(1.0), led * 1.3, ledAmt * uLed * (1.0 - 0.7 * sharp));
col = col * (1.0 + uCut * 1.1) + uCut * 0.06;
vec2 v = s0 * vec2(0.85, 1.15);
col *= 1.0 - uVig * dot(v, v);
col *= max(0.0, 1.0 - uVig * dot(v, v));
col *= 1.0 - (0.035 - 0.035 * sin(coord.y * 0.9 - uTime * 55.0));
col += (hash(coord + frameSeed * 1.31) - 0.5) * 0.06;
col *= 1.0 - uOut;
@@ -628,7 +628,8 @@ void main() {
gl.uniform1f(loc.uVig, opts.vignette == null ? 1.0 : opts.vignette);
gl.uniform1f(loc.uCA, opts.aberration == null ? 2.2 : opts.aberration);
gl.uniform1f(loc.uLed, opts.led == null ? 0.45 : opts.led);
gl.uniform1f(loc.uCurve, opts.curve == null ? 0.15 : opts.curve);
// A negative curve would sample outside the frame and fold over.
gl.uniform1f(loc.uCurve, Math.max(0, opts.curve == null ? 0.15 : opts.curve));
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
fs = null;
return true;
@@ -10,12 +10,15 @@ const library = html.match(/<script>([\s\S]*?)<\/script>/i)[1];
// enough canvas/WebGL surface for mount() to build a lens. Draws are counted per lens.
function page() {
const draws = [];
const uniforms = {};
const gl = (id) =>
new Proxy(
{},
{
get: (_, key) => {
if (key === "getShaderParameter") return () => true;
if (key === "getUniformLocation") return (_, name) => name;
if (key === "uniform1f") return (name, v) => (uniforms[name] = v);
if (key === "drawArrays") return () => draws.push(id);
if (key === "getExtension") return () => null;
if (typeof key === "string" && /^[A-Z0-9_]+$/.test(key)) return 1;
@@ -80,7 +83,7 @@ function page() {
setAttribute: (k, v) => attrs.set(k, v),
};
};
return { sandbox, draws, armVfx, canvas, captures: () => vfxCaptures };
return { sandbox, draws, uniforms, armVfx, canvas, captures: () => vfxCaptures };
}
const shots = [{ until: 10, palette: null }];
@@ -166,3 +169,11 @@ test("a draw before the scene's first paint waits for the paint instead of loggi
assert.deepEqual(errors, []);
assert.equal(p.draws.length, 1, "redrawn once the paint landed");
});
test("a negative curve is clamped to flat instead of folding the frame", () => {
const p = page();
p.sandbox.HFFilmedScreen.mount({ canvas: [p.canvas()], shots, curve: -1 });
p.sandbox.__hf.seek(0.5);
p.sandbox.__hf_page_composite_resolve();
assert.equal(p.uniforms.uCurve, 0);
});
@@ -23,5 +23,9 @@
"target": "compositions/components/filmed-screen.html",
"type": "hyperframes:snippet"
}
]
],
"preview": {
"video": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/filmed-screen.mp4",
"poster": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/filmed-screen.png"
}
}