AI reference-match grading companion for DaVinci Resolve Studio (built against the Resolve 20/21 scripting API). Drop a reference image into a chat, and Claude — acting as a colorist — computes a deterministic color match, bakes it into a 3D LUT (+ a DCTL with a Strength slider), applies it to your clip's node graph, looks at a verification still of the actual result, and iterates with CDL trims until it matches.
Split brain: Claude makes the taste decisions (strategy, strengths, protection, critique). Deterministic math produces the pixels — Monge-Kantorovich / Reinhard / CDF matching computed in Oklab (perceptually uniform), with tone and color matched independently (match the palette without touching your contrast, or vice versa). Everything bakes into a LUT: temporally stable, render-safe.
How a turn works: Claude first renders 3-4 candidate recipes locally (free, no
Resolve round-trip) as a scored contact sheet, applies the winner to your MATCH
node, reads a verification still plus an objective match report (sliced-Wasserstein
score + per-zone Oklab deltas vs the reference), and lands residuals with one CDL
trim. Say "apply it to the whole timeline" and the match rolls out to every clip on
video track 1 with sample-clip verification stills. For exact duplication ("nail
this, 99% on target") the idt engine converges the full color distribution onto
the reference - measured at 99%+ through the baked LUT.
Also built in: multi-frame sampling (match statistics come from stills sampled
across the clip, so camera moves don't skew the match - sample_frames in config),
progressive results (plates land in the panel as each tool round completes),
A/B wipe (hover any verification plate to sweep it against the reference,
double-click to zoom), and taste memory (say "always keep wood warm" once -
Claude records it and applies it in every future session).
- DaVinci Resolve Studio (free version does not allow external scripting)
- macOS or Windows, Python 3.10+
- An Anthropic API key (
https://platform.claude.com)
The API key is stored in your system credential store (macOS Keychain / Windows Credential Manager). All Resolve paths (LUT folder, scripting modules, plugin folder) resolve per-platform automatically.
LLM gateways supported: set API base URL in Settings (any
Anthropic-compatible proxy, e.g. https://your-gateway.example/v1) and paste
the gateway token as the key — it is then sent as a bearer token instead of
an x-api-key. Leave the base URL empty for a standard sk-ant-… key
against api.anthropic.com.
-
Resolve preferences — Preferences → System → General → External scripting using → Local. Restart Resolve.
-
Node convention — REFGRADE owns the last two nodes of each clip it grades:
- second-to-last node → match LUT (label it
REFGRADE MATCH) - last node → CDL trim (label it
REFGRADE TRIM)
Set up ONE clip exactly how every clip should start (your CST + the two REFGRADE nodes), open it on the Color page, and hit Settings → Capture from current clip. That saves a
.drxnode template; from then on the agent'sprep clipand timeline rollout apply it automatically (note: applying the template REPLACES a clip's existing grade — that's its job). If a clip has only one node, REFGRADE uses it for both (works, but match + trim share a node). - second-to-last node → match LUT (label it
-
API key — launch the app, open Settings, paste your key (stored in macOS Keychain, never on disk in plaintext).
Run ./make-release.command to build dist/refgrade-vX.Y.Z.zip (code only, no
local state). Copy it over, unzip anywhere, then run the installer below from
inside the unzipped folder. Your API key/gateway settings are per-machine:
enter them once in the panel's Settings on the target.
macOS:
cd ~/Desktop/refgrade
./install-plugin.commandWindows (may need an elevated "Run as administrator" PowerShell to write into ProgramData):
cd path\to\refgrade
powershell -ExecutionPolicy Bypass -File .\install-plugin.ps1This registers REFGRADE as a Workflow Integration plugin: it copies
plugin/com.refgrade.panel/ into Resolve's plugins folder, pulls the
version-matched WorkflowIntegration.node from Resolve's own developer
samples, and points the plugin at this repo's Python engine. Then restart
Resolve and open Workspace > Workflow Integrations > REFGRADE. The panel
starts the grading engine automatically (log: engine.log in this folder)
and shuts it down when you close the panel. Re-run the installer after a
Resolve upgrade (to refresh WorkflowIntegration.node) or if you move this
folder or change the port.
cd ~/Desktop/refgrade
./run.command # macOS — first run creates .venv and installs deps
run.bat # Windows equivalentOpen http://127.0.0.1:8765. Either way, have Resolve running with your clip
open on the Color page, drop a reference image, and type what you want:
"Match this, but keep it subtle and don't let the wood go orange." "More like the reference's shadows, but protect the sofa fabric." "Less contrast, warmer highlights."
Each turn, REFGRADE grabs a still, computes, applies, grabs a verification still,
lets Claude critique it against the reference, and trims. Generated LUTs land in
…/DaVinci Resolve/LUT/REFGRADE/ — a .cube (applied automatically) plus a matching
.dctl you can apply manually instead if you want a live Strength slider on the node.
Your footage is linear EXR out of V-Ray/Vantage; a 3D LUT lives in a 0–1 box. Two modes (Settings → Grade domain):
display(default) — matching happens on display-referred stills (exactly what you see in the viewer), and the LUT expects display-referred input. The REFGRADE nodes must therefore be LAST in the node chain, after your CST / tone-mapping node. This is the robust choice for unmanaged timelines with a CST per clip, and the verification loop self-corrects small discrepancies.scene_di— matching happens in DaVinci Intermediate log; the generated DCTL wraps linear→DI→LUT→DI⁻¹→linear. For color-managed timelines where node input is scene-linear. Set Source colorimetry toacescgif your EXRs are ACEScg. In this mode, prefer applying the generated .dctl (it does the linearization); the raw .cube alone would clip super-white detail.
./.venv/bin/python -m tests.test_engine./.venv/bin/python -m refgrade.doctor # macOS
.venv\Scripts\python.exe -m refgrade.doctor # WindowsWith Resolve open and a clip on the Color page, it exercises every API call REFGRADE depends on (connection, stills, LUT apply, CDL, .drx capture, timeline enumeration, API key) and prints PASS/FAIL per step. Safe: its only writes are an identity LUT (visual no-op, restored where possible) and a neutral CDL.
- Anything misbehaving — run the doctor (above) first; send the FAIL lines.
- "Could not connect to DaVinci Resolve" — Resolve running? Studio? External scripting set to Local? A project open?
- "SetLUT failed" — the clip must be the current clip on the Color page, and the node index must exist (add the two REFGRADE nodes). LUTs are also listed under the LUT browser → REFGRADE if you want to check they registered.
- Result in the viewer doesn't match the verification still — grade domain mismatch; see the pipeline section above.
- Stills look double-graded — the REFGRADE nodes weren't bypassed during the "before" grab; make sure the node indices in config match where the nodes actually are.
refgrade/
├── refgrade/
│ ├── app.py Flask server + orchestration + tool executor
│ ├── colorist.py Claude agent (system prompt, tools, loop)
│ ├── resolve_bridge.py Resolve scripting API wrapper
│ ├── transfer.py Matching engines in Oklab (mkl / reinhard / hist / hybrid)
│ ├── metrics.py Sliced-Wasserstein match score + zone deltas
│ ├── lutbake.py .cube writer + trilinear sampler (tests)
│ ├── dctl.py DCTL generator (strength slider)
│ ├── imaging.py EXR/PNG IO, analysis domains, stats
│ ├── spaces.py DaVinci Intermediate log, sRGB, ACEScg, hue math
│ ├── config.py settings + Keychain key storage
│ └── ui/index.html chat panel
├── plugin/com.refgrade.panel/ Resolve Workflow Integration plugin
│ ├── manifest.xml plugin registration (id, name, entry point)
│ ├── main.js Resolve-hosted Electron main: engine supervisor + window
│ └── index.html boot splash / engine error screens
├── tests/ engine + stress tests (offline)
├── install-plugin.command installs the plugin into Resolve
└── run.command standalone launcher
How the plugin works: Resolve launches main.js with its bundled Electron.
The plugin health-checks the engine at 127.0.0.1:8765, spawns
.venv/bin/python -m refgrade.app if it isn't running (reusing an already
running engine otherwise), shows a boot splash, then loads the panel UI in
the plugin window. Closing the panel stops the engine only if the plugin
started it. WorkflowIntegration.node is used for plugin registration and
the ResolveQuit callback; grading traffic goes through the Python scripting
bridge, so the external-scripting preference must stay on Local.
Roadmap: image-model creative engine with LUT-fitting (v2) · IDT / true-OT match engines · multi-frame sampling · semantic-weighted matching.