Skip to content

Innei/yoake

Yoake

Yoake

Develop the dawn. A local-only web darkroom that restores DJI D-Log M footage and exports Ultra HDR JPEG stills that bloom in macOS Photos and on iPhone.

Yoake (よあけ — "dawn") opens a DJI clip, applies the official D-Log M → Rec.709 .cube LUT, dials in exposure, and saves the current frame as an ISO 21496-1 Ultra HDR JPEG — entirely client-side, no upload step.

Features

  • Real HDR preview on HDR-capable displays via a WebGPU rgba16float canvas with extended tone mapping. What you see on screen is what the exported gainmap reconstructs.
  • Official DJI D-Log M restoration — forward OETF, 3D .cube LUT, sRGB output, all on the GPU.
  • Ultra HDR JPEG export — SDR base + per-pixel luminance gainmap in a hand-assembled ISO 21496-1 container (MPF/XMP/ICC) that round-trips through macOS Photos, iOS Photos, and Google Photos.
  • Video export via WebCodecs — direct stream copy when nothing needs baking, or a full GPU re-render that bakes grade, trims, speed ramps, and freeze frames into AVC/HEVC MP4.
  • SDR JPEG fallback auto-engages when WebGPU HDR is unavailable.
  • File System Access — pick a DJI clip directory and a LUT directory once, handles persist in IndexedDB.
  • Session restore — last clip, last LUT, exposure, HDR settings come back on reload.
  • Frame-stepping transportSpace, J/K/L, ← / →, Home / End. Frame export with ⌘S.

Requirements

Important

This tool targets macOS Chrome with WebGPU enabled on an HDR-capable display (MacBook Pro mini-LED XDR, Pro Display XDR, recent iMacs). The HDR preview path is gated on matchMedia('(dynamic-range: high)') and the WebGPU extended tone-mapping mode actually being honored by the browser.

Note

Safari, Firefox, Linux, and Windows are not supported. On SDR-only setups the app still runs, but Ultra HDR export is disabled with a tooltip explaining which capability is missing.

Source clips: HEVC Main 10 (yuv420p10le), as written by DJI Action / Osmo cameras in D-Log M mode (*_D.MP4).

Getting started

pnpm install
pnpm dev

Then open the printed localhost URL in macOS Chrome.

To produce a production bundle:

pnpm build
pnpm preview

Usage

  1. Pick a clip folder (⌘O) — the DJI directory containing *_D.MP4 files.
  2. Pick a LUT folder from the inspector — point it at DJI's official .cube LUTs (or any folder of .cube files).
  3. Select a clip from the list. The preview engages on an HDR canvas.
  4. Tune exposure and HDR peak nits (400 / 600 / 1000) in the inspector. Use [ / ] to flip between LUTs.
  5. Pause on the frame you want, then ⌘S to export.

Output filename: <clip>_<frameMs>_<lut>.jpg written to the chosen export directory.

Keyboard shortcuts

Section Keys Action
Playback Space Play / pause
← / → · Shift+← / → Step 1 / 10 frames
J / K / L Back / pause / forward
Home / End Jump to start / end
Clips & LUTs ↑ / ↓ Previous / next clip
[ / ] Previous / next LUT
⌘O Open clip folder
0 Reset exposure
Layout ⌘B · ? Toggle inspector · show shortcuts
Export ⌘S · ⌘C Export · copy frame to clipboard

How it works

Per-frame pipeline. The preview renders the reconstructed Ultra HDR result — the same image the exported JPEG reproduces on an HDR viewer.

<video> ─ rVFC ─► GPUExternalTexture
                          │
                          ▼
   Pass 1  sceneLinear   (rgba16float, scene-linear master)
                          │
              grading (exposure, …)
                          │
        ┌─────────────────┴──────────────────┐
        ▼                                    ▼
   Pass 2  lutSdrBase                  scene-linear extended
   D-Log M OETF → 3D LUT → sRGB rgba8
        │                                    │
        └─────────────────┬──────────────────┘
                          ▼
   Pass 3  hdrCompose
   gain = clamp(srcLuma / sdrLuma, 1.0, peakHeadroom)
   hdrLin = sdrLin * gain        (LUT owns hue/sat; gain lifts luma only)
                          │
                          ▼
                HDR canvas (preview)
                          │
                          ▼
   Pass 4  gainmap (export only)
   log2(hdrLin / sdrLin) → normalize → rgba8 grayscale
                          │
                          ▼
        ISO 21496-1 container assembly (MPF + APP2 + XMP)
                          │
                          ▼
                Ultra HDR JPEG file

The gainmap moves luminance only — hue and saturation come entirely from the LUT, which is what keeps the single-channel encoder faithful.

Stack

  • React 19 · TypeScript · Vite 8 · React Compiler
  • WebGPU for all per-frame work — scene-linear decode, LUT lookup, gainmap compute
  • WebCodecs + mediabunny for video export, hand-rolled ISO 21496-1 container assembly for Ultra HDR JPEG
  • Zustand + Jotai for state, IndexedDB (idb-keyval) for FileSystemDirectoryHandle persistence
  • TailwindCSS v4 + @pastel-palette/tailwindcss theming, next-themes for light/dark
  • Vitest + @testing-library/react for unit / component tests

Project layout

src/
├─ features/     Domain modules — clips, timeline, preview, grade, edit,
│                deliver, preferences, shortcuts, theme
├─ lib/          Core engines
│  ├─ gpu/       WebGPU lifecycle, capability detection, pipelines, WGSL shaders
│  ├─ decode/    <video> + requestVideoFrameCallback wrapper
│  ├─ color/     D-Log M OETF, .cube parser, 3D LUT textures, color math
│  ├─ fs/        File System Access — clip scanner, LUT loader, handle persistence
│  └─ export/    Offscreen render + Ultra HDR JPEG / SDR JPEG writers
├─ components/   Layout shell + UI primitives
├─ pages/        File-based routes (vite-plugin-route-builder)
├─ styles/       Tailwind + Pastel theme
└─ utils/        Shared helpers

tests/           Vitest setup, fixtures (DJI .cube LUT, OETF value pairs)

Scripts

Script Does
pnpm dev Start the dev server
pnpm build Typecheck + production build
pnpm preview Serve the production build
pnpm test · test:watch Run / watch Vitest unit suites
pnpm typecheck TypeScript only
pnpm lint · lint:fix ESLint
pnpm format Prettier

Non-goals

Note

Out of scope: batch export, multi-LUT compare, real-time scopes, custom curve editors, HEIC / AVIF / 16-bit PNG export, and any browser beyond macOS Chrome.

License

AGPL-3.0 © Innei

About

Local-only web tool for restoring DJI D-Log M footage with a LUT and exporting Ultra HDR JPEG stills — WebGPU, macOS Chrome.

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors