From 9512744c2eb95542e4c3727ef419ba52a6d348c4 Mon Sep 17 00:00:00 2001 From: Vance Ingalls Date: Wed, 22 Apr 2026 17:02:42 -0700 Subject: [PATCH] refactor(shader-transitions): extract DEFAULT_DURATION and DEFAULT_EASE constants (#367) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Extract `DEFAULT_DURATION = 0.7` and `DEFAULT_EASE = "power2.inOut"` as shared constants in `hyper-shader.ts` and apply them at all three fallback sites (metadata write, browser/render mode, engine mode). ## Why `Chunk 2` of `plans/hdr-followups.md`. The three fallback sites had drifted apart: the metadata path used `1s` / `"none"` while the actual rendering used `0.7s` / `"power2.inOut"`. A transition that omitted `duration`/`ease` would render at 0.7 s but tell the producer it was 1 s, throwing off the producer's compositing window planning and producing a visible ~0.3 s brightness dropout. This is a small, high-value correctness fix that runs before the larger Chunk 1 / Chunk 4 work. ## What changed - New module-level `DEFAULT_DURATION` and `DEFAULT_EASE` constants in `packages/shader-transitions/src/hyper-shader.ts`. - All three fallback call sites (metadata, browser, engine) now use the constants. - Explicit `ease: "none"` on the timeline-length anchor tweens elsewhere in the file is intentional (those are linear interpolators driving the shader's progress uniform) and is left unchanged. ## Test plan - [x] Render a composition with a transition that omits `duration` and `ease` — no brightness dip in the last ~0.3 s of the transition. - [x] Preview (browser mode) and render (engine mode) produce matching blending curves. - [x] Render with explicit `duration: 1.5` still works (constants are fallbacks only). ## Stack Chunk 2 of `plans/hdr-followups.md`. Lands ahead of Chunk 1 (opacity) per the suggested merge order. --- .../shader-transitions/src/hyper-shader.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/shader-transitions/src/hyper-shader.ts b/packages/shader-transitions/src/hyper-shader.ts index c393d4c04..0561d4e57 100644 --- a/packages/shader-transitions/src/hyper-shader.ts +++ b/packages/shader-transitions/src/hyper-shader.ts @@ -61,6 +61,14 @@ interface TransState { progress: number; } +// Defaults for transition duration/ease. Used by every fallback site in this +// file — meta-write, browser/render mode, and engine mode — so a transition +// without explicit `duration`/`ease` plays the same length and curve in +// preview, the engine's deterministic seek path, and the metadata the +// producer reads to plan compositing. +const DEFAULT_DURATION = 0.7; +const DEFAULT_EASE = "power2.inOut"; + function parseHex(hex: string): [number, number, number] { const h = hex.replace("#", ""); if (h.length < 6) return [0.5, 0.5, 0.5]; @@ -130,9 +138,9 @@ export function init(config: HyperShaderConfig): GsapTimeline { if (hfWin.__hf) { hfWin.__hf.transitions = transitions.map((t: TransitionConfig, i: number) => ({ time: t.time, - duration: t.duration ?? 1, + duration: t.duration ?? DEFAULT_DURATION, shader: t.shader, - ease: t.ease ?? "none", + ease: t.ease ?? DEFAULT_EASE, fromScene: scenes[i] ?? "", toScene: scenes[i + 1] ?? "", })); @@ -238,8 +246,8 @@ export function init(config: HyperShaderConfig): GsapTimeline { const prog = programs.get(t.shader); if (!prog) continue; - const dur = t.duration ?? 0.7; - const ease = t.ease ?? "power2.inOut"; + const dur = t.duration ?? DEFAULT_DURATION; + const ease = t.ease ?? DEFAULT_EASE; const T = t.time; // Pause timeline during async capture to prevent the progress tween @@ -361,7 +369,7 @@ function initEngineMode( const toId = scenes[i + 1]; if (!fromId || !toId) continue; - const dur = t.duration ?? 0.7; + const dur = t.duration ?? DEFAULT_DURATION; const T = t.time; // During the transition both scenes need to be visible so the engine