{"record":{"id":"d382a14df5b4d2ab","repo":"heygen-com/hyperframes","slug":"color-curves-must-include-input-endpoints-0-and-1","errorCode":null,"errorMessage":"Color curves must include input endpoints 0 and 1","messagePattern":"Color curves must include input endpoints 0 and 1","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"packages/core/src/colorGradingCurves.ts","lineNumber":165,"sourceCode":"    samples[index] = Math.min(outputMax, Math.max(outputMin, output));\n  }\n  return samples;\n}\n\n/** Samples a shape-preserving cubic curve into a GPU-ready 1D lookup table. */\nexport function compileHfColorCurve(\n  points: readonly HfColorCurvePoint[],\n  size = HF_COLOR_CURVE_LUT_SIZE,\n): Float32Array {\n  validateCurvePoints(points, size);\n  if (points.length > HF_COLOR_CURVE_MAX_POINTS) {\n    throw new RangeError(`A color curve supports at most ${HF_COLOR_CURVE_MAX_POINTS} points`);\n  }\n  if (points.some(([input]) => input < 0 || input > 1)) {\n    throw new RangeError(\"Color curve inputs must be between 0 and 1\");\n  }\n  if (points[0]?.[0] !== 0 || points[points.length - 1]?.[0] !== 1) {\n    throw new RangeError(\"Color curves must include input endpoints 0 and 1\");\n  }\n  return compileCurveSamples(points, size, (index) => index / (size - 1), 0, 1);\n}\n\n/** Samples a periodic hue curve without duplicating the 0/360-degree texel. */\nexport function compileHfHueCurve(\n  points: readonly HfHueCurvePoint[],\n  outputMin: number,\n  outputMax: number,\n  size = HF_COLOR_CURVE_LUT_SIZE,\n): Float32Array {\n  if (points.length < 3) throw new RangeError(\"A hue curve requires at least three points\");\n  if (points.length > HF_COLOR_CURVE_MAX_POINTS) {\n    throw new RangeError(`A hue curve supports at most ${HF_COLOR_CURVE_MAX_POINTS} points`);\n  }\n  const sorted = [...points].sort((a, b) => a[0] - b[0]);\n  for (let index = 0; index < sorted.length; index += 1) {\n    const point = sorted[index];","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/core/src/colorGradingCurves.ts#L147-L183","documentation":"compileHfColorCurve compiles a shape-preserving cubic spline into a 1024-texel LUT that maps normalized input in [0,1] to output. The check at line 165 enforces that the first control point's input is exactly 0 and the last is exactly 1, so the curve spans the full tonal range (pure black to pure white). Without both anchors the sampler has no segment covering inputs below the first point or above the last, leaving LUT texels undefined at the extremes.","triggerScenarios":"Calling compileHfColorCurve(points) where points[0][0] !== 0 or points[points.length-1][0] !== 1, after validateCurvePoints has already accepted the array as finite and strictly increasing. Example: [[0.25, 0.18], [0.75, 0.82]] — no anchor at 0 or 1.","commonSituations":"Forgetting to add black/white anchors when hand-authoring a curve; loading a preset authored in an external editor that emits only interior control points; a UI that lets users drag interior handles but never synthesizes endpoint anchors before compile.","solutions":["Prepend [0, <output at black>] and append [1, <output at white>] so both endpoints are present.","If you want the curve to pass through the first user point's value, set the prepended [0, ...] output equal to that value (likewise for the appended [1, ...]).","Run the upstream validateCurve() from @hyperframes/parsers/color-grading-contract first — it reports missing endpoints and inferred-count overflow before you reach compileHfColorCurve.","Sort points ascending by input before adding anchors; validateCurvePoints requires strictly increasing inputs."],"exampleFix":"// before\ncompileHfColorCurve([[0.25, 0.18], [0.75, 0.82]]);\n\n// after\ncompileHfColorCurve([\n  [0, 0],\n  [0.25, 0.18],\n  [0.75, 0.82],\n  [1, 1],\n]);","handlingStrategy":"validation","validationCode":"function assertColorCurveEndpoints(points: readonly [number, number][]): void {\n  if (points.length === 0 || points[0][0] !== 0 || points[points.length - 1][0] !== 1) {\n    throw new Error('Color curve must start at input 0 and end at input 1');\n  }\n}\n\nassertColorCurveEndpoints(points);\ncompileHfColorCurve(points);","typeGuard":"function isAnchoredColorCurve(points: readonly unknown[]): points is [number, number][] {\n  return (\n    points.length >= 2 &&\n    Array.isArray(points[0]) && points[0][0] === 0 &&\n    Array.isArray(points[points.length - 1]) && points[points.length - 1][0] === 1\n  );\n}","tryCatchPattern":"try {\n  const lut = compileHfColorCurve(points);\n} catch (err) {\n  if (err instanceof RangeError && err.message === 'Color curves must include input endpoints 0 and 1') {\n    // synthesize anchors and retry, or surface a user-facing validation message\n  }\n  throw err;\n}","preventionTips":["Always author curves with explicit [0, y0] first and [1, y1] last.","Run validateCurve() from @hyperframes/parsers/color-grading-contract before compile.","In UI code, synthesize endpoint anchors from the nearest user point before calling compileHfColorCurve."],"tags":["color-grading","curves","endpoints","range-error"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}