Files
hyperframes/docs/reference/color-grading.mdx
Vance IngallsandClaude Opus 5.5 09b1290474 feat(core): grade groups grade a whole lane from one hf-color-group (#6678)
An inert <hf-color-group> holds a lane's data-color-grading; <video>, <img>
and <canvas> join with data-color-group. A member's own grading overrides
its group's. The runtime discovers members, regrades them when the group's
grading or a member's data-color-group changes, and ungrades them when the
group element or its grading goes, drawing only members on screen.

Render keeps group-graded video at full size like directly graded video,
and lint accepts (and still validates) grading on hf-color-group.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 10:10:42 -07:00

153 lines
4.7 KiB
Plaintext

---
title: "Color grading implementation"
sidebarTitle: "Color grading"
description: "Persist, automate, animate, and spatially isolate media color grading."
---
Use this reference when browser editor controls are not enough: you need to write a
grade into HTML, apply it from automation, animate a supported property, or
limit an effect to a composited region. For visual correction and everyday
browser editor use, start with [Color grading](/guides/media-effects#fix-and-grade-the-color).
## Persist a grade
The browser editor and the CLI store the resolved grade in `data-color-grading`:
```html
<video
id="interview"
src="./assets/interview.mp4"
data-color-grading='{"preset":"skin-soft","intensity":0.7}'
></video>
```
Apply the same validated payload from the CLI:
```bash
npx hyperframes media-treatment \
--selector '#interview' \
--grading '{"preset":"skin-soft","intensity":0.7}' \
--apply
```
Add `--dry-run --json` to inspect the change without writing it. Use `--clear`
to remove the treatment.
## Grade a whole lane
A camera cut into hundreds of clips takes one grade, set once. Put the grade
on an `<hf-color-group>` and point each clip at it with `data-color-group`:
```html
<hf-color-group
id="tamer-cam"
data-label="Tamer"
data-color-grading='{"preset":"skin-soft","intensity":0.7}'
></hf-color-group>
<video id="tamer-01" data-color-group="tamer-cam" src="./assets/tamer.mp4" …></video>
<video id="tamer-02" data-color-group="tamer-cam" src="./assets/tamer.mp4" …></video>
<video
id="tamer-03"
data-color-group="tamer-cam"
data-color-grading='{"adjust":{"exposure":0.3}}'
src="./assets/tamer.mp4"
…
></video>
```
- `data-color-grading` on the group takes the same JSON as on a clip. Editing it
regrades every member, in preview and in render.
- `data-color-group` goes on the `<video>`, `<img>` or `<canvas>`. Membership is
held by the member, so a deleted clip simply leaves the group.
- **A clip's own `data-color-grading` wins.** It replaces the group's grade for
that clip, so `tamer-03` above gets only its exposure lift, not skin-soft.
- `data-color-group=""` is no group, and a group id with no `<hf-color-group>`
element leaves the clip ungraded. Removing the element, or its
`data-color-grading`, ungrades its members.
- Groups do not nest. Group ids share the element-id namespace.
- The element is inert: the runtime injects
`hf-color-group{display:contents!important}`. Close it with
`</hf-color-group>`, since HTML ignores `/>` on a custom element.
## Animate a supported property
Nine grading properties expose CSS custom properties that a paused timeline
can tween directly:
```text
--hf-color-grading-ascii
--hf-color-grading-bloom
--hf-color-grading-blur
--hf-color-grading-dither
--hf-color-grading-exposure
--hf-color-grading-intensity
--hf-color-grading-kuwahara
--hf-color-grading-pixelate
--hf-color-grading-lut-intensity
```
Once set, a property overrides the payload's value for that control, whether it
comes from the element's inline style, a stylesheet or a parent. Set it only on
media you animate, starting at the value the tween begins from; leave it unset
for a static grade. Then animate it on the registered timeline:
```js
tl.to(
"#plate",
{
"--hf-color-grading-pixelate": 0.5,
duration: 3,
ease: "power2.inOut",
},
0.3,
);
```
`--hf-color-grading-intensity` scales the primary correction, wheels, curves,
selections, and LUT. It is not a master control for detail and effect families
such as grain, halftone, bloom, tape, or CRT. Animate the specific exposed
property and verify that it moves before relying on it.
## Limit a grade to part of the frame
Grading selects pixels by value, not position. To treat one region, isolate it
as its own media layer:
| Result | Layer stack |
| --- | --- |
| Grade the subject only | Clean original plate, then a graded subject cutout |
| Grade the background only | Graded original plate, then a clean subject cutout |
| Grade one region of the subject | Clean cutout, then a second graded cutout clipped to the region |
```html
<video
src="media/room.mp4"
muted
data-start="0"
data-duration="4"
data-track-index="2"
></video>
<video
src="media/subject.webm"
muted
data-start="0"
data-duration="4"
data-track-index="3"
></video>
<video
src="media/subject.webm"
muted
data-start="0"
data-duration="4"
data-track-index="4"
style="clip-path: ellipse(9.8% 15.5% at 50.7% 14.5%)"
data-color-grading='{"intensity":1,"effects":{"pixelate":0.5}}'
></video>
```
Use the original clip as the background plate. A subject-removed plate leaves a
hole and can create a dark edge beneath a feathered cutout. A fixed `clip-path`
works only when the subject barely moves; real movement needs a tracked matte
produced outside HyperFrames.