Skip to main content
Glama

Lutra

In-browser color-grading LUT creator. Drop screenshots, camera RAW files or a video clip, grade them with real colorist tools, export .cube / .3dl / Unreal / Unity / ReShade / HALD LUTs. No installs, no accounts — everything runs locally on your GPU via WebGL2. Images never leave your machine.

Live at https://lutra.gamebench.dev

Why

Making a color LUT usually means installing gigabytes of video software. Lutra is a ~70 KB (gzipped) web app built around one architectural rule: every operation is a pure per-pixel color transform, so the entire grade bakes into a 3D LUT exactly. Spatial effects (bloom, halation, grain, vignette) exist only as a clearly-labelled preview layer and export as an engine recipe rather than pixels, because they can't live in a LUT — what you see in the grade is what the LUT does.

Related MCP server: mcp-da-vinci

Features

Grading

  • Contact-sheet grading — load many shots across lighting conditions; every thumbnail follows the grade live, because a LUT is global and has to hold up everywhere.

  • Per-shot development — normalize each source by ±4 EV before judging the shared look. Developed originals, scopes, color picking, and Auto match all see the same input; the offset stays out of the exported LUT by design.

  • Colorist toolset — white balance (+ gray eyedropper), exposure, contrast with pivot, highlights/shadows/whites/blacks, lift/gamma/gain/offset wheels, master and per-channel R/G/B curves, 8-band HSL mixer (Oklab hues), continuous Sat-vs-Sat and Luma-vs-Sat curves, saturation/vibrance with lightness-keyed shadow/highlight control, split toning, output levels, and a master look-strength blend.

  • Creative LUT layer — drop in any .cube you already own and keep grading on top of it with a strength slider. It stacks in right after the tone curves, so everything below trims the look rather than feeding it — and both still bake into one exported LUT.

  • Film response, honestly — bloom, halation, grain and vignette preview as a layer the UI labels not in the LUT in as many words. They can't bake (bloom reads neighbouring pixels; grain and vignette depend on position in frame), so they're hidden from thumbnails, comparisons and scopes, and export as an engine recipe naming the Unreal/Unity/ReShade settings that reproduce them.

  • Compare — before/after (hold Space), A/B wipe, per-shot thumbnails. Undo/redo and keyboard shortcuts throughout.

Input

  • Camera RAW.ARW, .CR2, .CR3, .NEF, .DNG, .RAF, .ORF, .RW2 and the rest via LibRaw compiled to WebAssembly. The camera's own white balance and color matrix are applied, then the frame enters the pipeline as scene-linear light at exactly the point EXR and PQ AVIF do — so a photo and a game capture grade identically. Exposure is a fixed, documented headroom constant, not per-image auto-brightness, so a night shot and a snow field don't develop to different brightnesses. The 1.5 MB decoder is behind a dynamic import() and never weighs on anyone else's load.

  • HDR & high bit depth — OpenEXR (half/float, NONE/RLE/ZIP), Radiance .hdr, and 10/12-bit AVIF (PQ, HLG, high-bit SDR) at full precision via zero-dependency decoders + WebCodecs. HDR develops to display sRGB with the same ACES fit the PQ shaper export assumes, so preview and in-engine result agree. Browsers without WebCodecs ImageDecoder (Safari, Firefox) get pre-developed SDR companions derived from the encoded PQ data, so pixels match — and a clear warning when a user's HDR AVIF must be browser-tonemapped.

  • Live video grading — MP4/MOV/WebM clips sit in the same filmstrip as stills. Select one, play, scrub or step frames in the normal viewport, and keep turning every grading knob while it runs. Presented frames feed the shared WebGL grade directly from the browser/OS decoder; hardware decode is used when that codec/browser/device combination supports it. Original clip bytes and the playhead persist locally in browser storage and restore into the filmstrip after a restart; they are never uploaded. Copies are chunked and cancellable, quota is checked first, and each clip is labelled LOCAL or SESSION so storage failure is visible rather than destructive.

Matching & QA

  • Auto match — drop a reference image and a solver fits the broad-stroke knobs so the whole contact sheet's Oklab statistics match it. The result is ordinary knob positions: blend with a strength slider, refine by hand, undo in one step.

  • .cube import — the same solver fits the knob stack to any 3D .cube (typically within ΔE ≈ 0.1), turning an opaque LUT back into an editable grade.

  • LUT Lab — open any 3D .cube as float data, inspect its declared input domain, output excursions and folded/singular cells, resize it, blend it from 0–200%, compose it before or after a second LUT, then download, stack, or reverse-fit the result. Custom domains and values outside 0–1 survive the round trip instead of being silently clipped.

  • Color locks — pin colors that must survive the grade (UI colors, team reds, skin tones). Live ΔE badges warn when the grade moves them, and Auto match treats them as constraints.

  • Harmonize assets — mask a palette to keep and an asset to adjust; Harmonize solves only the HSL mixer so the asset matches under your look. Export texture fix instead solves a standalone B→A transform and downloads it as .cube + HALD for batch-applying to textures.

  • Scopes — RGB histogram, parade, waveform, vectorscope with skin-tone line, clipping zebras, exact in→out pixel probe, and color-vision-deficiency simulation for gameplay-readability checks.

  • Honest baked preview — toggle between the float pipeline and the actual quantized LUT (16³/32³/65³, trilinear or tetrahedral) to see banding before export instead of in-engine.

Output & persistence

  • Source-resolution stills — export the active shot as a PNG at its original dimensions, including per-shot development and either the float grade or the currently selected quantized-LUT preview. A separate complete-look export renders tuned bloom, halation, grain and vignette into the pixels.

  • Exports.cube (16/32/65), .3dl, Unreal 256×16 strip, Unity LDR 1024×32 strip, ReShade lut.png, HALD CLUT 512×512, and a PQ shaper .cube for engines grading in scene-referred HDR before the tonemapper. Multiple formats bundle into one zip. See docs/exports.md for per-engine setup and the PQ shaper's caveats.

  • Share links — the Share button copies a URL with the whole look gzipped into the fragment. No server, nothing uploaded.

  • Sessions survive reload — shots and mask scribbles persist to OPFS, the grade to localStorage. Close the tab, come back, keep grading.

  • Works on a phone — below 860px the image pins to the top while controls scroll under it, hit targets grow, and two fingers pinch-zoom the viewport.

Connect AI

One button in the toolbar. Lutra exposes its tools over the Model Context Protocol, so Claude, ChatGPT or any MCP client can drive the app you already have open. The point isn't text-to-LUT: the assistant can look. It renders a shot, reads the scopes, nudges one named axis in plain English, renders again, checks your locked colors — the loop a blind generator can't close. Global grade changes remain ordinary undo steps; shot development and film response stay separate so an undo cannot silently cross pipeline boundaries.

Two transports, picked for you. WebMCP, where the browser implements it (Edge ships it; Chrome runs an origin trial from 149), hands the tools to the in-browser agent with no pairing at all. Everywhere else you get a connector link relayed through a Durable Object that stores nothing:

Client

What you get

Claude Code

claude mcp add --transport http lutra <url>, to run in a terminal

Codex

an [mcp_servers.lutra] block for ~/.codex/config.toml

Claude app

the bare URL, for Settings → Connectors → Add custom connector

Tools: get_look · list_shots · render_shot (returns the actual image) · get_scopes (original or graded) · nudge · set_look · set_shot_development · get_film_response · set_film_response · suggest_looks · check_locks · undo · get_share_link. The grade schema includes the wheels and expert curves, plus a compact toe/mid-gamma/shoulder tone shape. Declared once in shared/mcpTools.ts, implemented in src/mcp/tools.ts; worker/mcp.ts is the protocol as a pure function.

Security. There is no API key and no OAuth — the code in the URL is the credential, so treat it like a password and use New link to revoke one you've pasted somewhere public. It works only while the Lutra tab is open. Lutra holds no API key and calls no model: the intelligence is billed to the subscription you already pay for, which is why this costs nothing to run and needs no account. A localhost link is unreachable from the Claude app, which connects from Anthropic's servers; Claude Code and Codex run locally, so they can use one.

Develop

npm install
npm run dev        # http://localhost:5173
npm run build      # type-check + production build → dist/
npm test           # vitest

One runtime dependency (libraw-wasm, dynamically imported and code-split so it only loads for RAW files); dev deps are Vite + TypeScript only.

Deploy

Production is served as static assets from a Cloudflare Worker — no server code, just dist/:

npm run deploy       # build + wrangler deploy
npm run deploy:dry   # validate without deploying

The custom domain is a route in wrangler.jsonc, so DNS is kept in sync by the deploy. The app is fully static with relative asset paths, so it also hosts on Netlify, GitHub Pages or any file server — grading, export and every feature above work with no backend at all. The only server-side piece is the MCP pairing relay (worker/), and WebMCP doesn't even need that.

Docs

  • docs/exports.md — using the exported LUTs in each engine, and what the PQ shaper does and doesn't guarantee.

  • docs/architecture.md — the single-grade()-function design, the CPU mirror, the solver, and roadmap ideas.

  • docs/professional-parity.md — competitor-backed capability gaps, browser feasibility decisions, and acceptance gates.

  • docs/cloud-architecture.md — the local-first server boundary and the fail-closed Cloudflare budget capped at $100/year.

License

Proprietary — all rights reserved. See LICENSE.

The app is free to use at https://lutra.gamebench.dev; the source is not licensed for reuse. This may change: releasing under an open-source licence later is easy, and un-releasing is impossible, so the source stays closed until that decision is made deliberately.

A built deployment still redistributes LibRaw (LGPL-2.1 or CDDL-1.0, via libraw-wasm), and the bundled samples are CC0 from RawDB. Those obligations follow the deployed app rather than the source, so they apply regardless of this repository's visibility — see NOTICE, including which RawDB licences are safe to bundle if you add a sample.

F
license - not found
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Create and manage cinematic AI video renders through the Future Video Studio Agent API.

  • Manage AI filmmaking projects, scenes, assets, and generations in Martini.

  • Build and run visual creative-production workflows from your AI agent.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/withaspoon/nyans'

If you have feedback or need assistance with the MCP directory API, please join our Discord server