Specifications
Everything Halidra does, in detail — system requirements, camera support, the colour pipeline, every develop control, and a full functional-requirements table.
On this page
01 System requirements
Halidra runs as a Windows desktop app with a WebGL2 develop pipeline and a pure-JavaScript RAW reader. No native modules, no Adobe dependency.
- Operating system
- Windows 10 or 11, 64-bit (x64)
- GPU
- WebGL2-capable — the whole develop pipeline runs on the GPU
- Download size
- ~95 MB (portable single .exe or installer)
- Disk
- Download size, plus a thumbnail cache
- macOS
- Coming soon
- Internet
- Not required to edit
Note: Minimum RAM and CPU figures have not been published yet. Tier 2 sensor decode is the heaviest task and runs on a single thread.
02 Distribution
Two Windows x64 executables, both built from the same source.
| File | What it is |
|---|---|
| Halidra-1.0.0-portable.exe | Single file, ~95 MB. Double-click and it runs — no install, nothing left behind in Program Files. |
| Halidra-1.0.0-setup.exe | Installer, ~95 MB. Lets you choose the location and adds Start Menu and desktop shortcuts. |
Packaged builds are checked with a self-test before being handed over: it boots the app, confirms WebGL initialises, confirms the decode worker loads as an ES module from inside the packaged app, and confirms a real image renders to non-black pixels. Builds are signed with Microsoft Artifact Signing (Azure Trusted Signing), and the signature is checked by asking Windows directly — not by reading the build log.
03 Camera & file support
Every RAW file is read in two tiers, and which tier you get decides how much latitude you have — not whether the file opens.
Tier 1 — the embedded preview
Every RAW the camera wrote a JPEG preview into opens, culls, rates and exports. That is effectively every camera: it's the camera's own full-resolution rendering, which is also what makes culling quick.
Tier 2 — the sensor data
Demosaicing the actual sensor plane gives real highlight headroom and the full tonal range. Press Ctrl+Alt+R to switch a photo to it.
| Format | Tier 1 | Tier 2 |
|---|---|---|
| Canon CR2 | yes | yes |
| DNG (lossless JPEG or uncompressed) | yes | yes |
| Any uncompressed RAW | yes | yes |
| Sony ARW (ARW2) | yes | yes |
| Nikon NEF (compressed) | yes | yes |
| Canon CR3 (lossless) | yes | yes |
| Canon CR3 (C-RAW) | yes | yes |
| Panasonic RW2 (RawFormat 4, 5, 6) | yes | yes |
| Panasonic RW2 (RawFormat 7) | yes | yes |
| Panasonic RW2 (RawFormat 8) | yes | yes |
| Fujifilm RAF (Bayer, uncompressed) | yes | yes |
| Fujifilm RAF (compressed X-Trans, lossless) | yes | yes |
| Fujifilm RAF (compressed X-Trans, lossy) | yes | yes |
| Fujifilm RAF (compressed Bayer) | yes | yes |
Containers & other input
Containers parsed directly: CR2, NEF, ARW, DNG, ORF, RW2, PEF, SRW, IIQ, 3FR (TIFF/IFD), CR3 (ISO-BMFF) and RAF (Fujifilm header). Anything with an embedded preview opens via a brute-force scan even if the container itself is unrecognised, so unknown formats still open at Tier 1. JPEG, PNG and TIFF files can also be imported directly alongside RAW.
Tier 2 is opt-in — Ctrl+Alt+R — pure JavaScript, and takes about 13 seconds for a 26 MP frame on one thread, which is why it isn't the default. If a body ever looks wrong, the same shortcut returns you to the embedded preview.
04 Colour pipeline
Colour needs no per-camera table. Instead, the camera-RGB → sRGB transform is fitted per-photo against the camera's own embedded preview of that same frame.
- Matrix fitting
- Full 3×3 matrix fitted against the embedded preview
- Row constraint
- Each row sums to 1, which keeps neutrals neutral
- Fallback
- Per-channel gains when the scene can't constrain a full matrix
- Fallback rate
- 3 of 12 reference files take the fallback
- Black level
- Measured from the optically shielded sensor border
- White balance
- As-shot, read from the camera's MakerNote
A full matrix needs six free parameters, which needs a scene with colour in it; per-channel gains need only two, so they work on a near-monochrome scene. Two of the twelve reference files are near-monochrome, and one has ample blue-green variation but almost no red-green, leaving its red row unconstrained — all three take the gains fallback. Gains fix the colour cast, which is what makes an uncorrected render obviously wrong, but leave saturation where the sensor put it. Which tier a file lands in is reported in the sensor-decode line and asserted by the test suite.
Canon files state none of the DNG metadata a decoder needs. The MakerNote is read for the sensor borders (SensorInfo) and the as-shot white balance (ColorData, whose layout is identified by record length); the white level comes from the real bit depth of the compressed stream rather than an assumption.
05 Develop controls
100 controls across 10 groups, plus Auto Tone, Auto White Balance, a before/after split view and clipping warnings. Every control takes the mouse wheel — Shift for fine steps, Ctrl for coarse — and this works identically on a favourited copy of a control.
| Group | Controls |
|---|---|
| Profile | Profile, Amount, Black & White |
| Basic | Temp, Tint, Exposure, Contrast, Highlights, Shadows, Whites, Blacks, Texture, Clarity, Dehaze, Vibrance, Saturation |
| Tone Curve | Point curve (RGB + per-channel) over a live histogram, plus parametric Highlights / Lights / Darks / Shadows |
| Color Mixer | Hue / Saturation / Luminance for 8 colour bands |
| Color Grading | Shadows / Midtones / Highlights / Global wheels, Blending, Balance |
| Detail | Sharpening amount · radius · detail · masking, luminance & colour noise reduction |
| Optics | Distortion, lens vignetting, purple/green defringe, chromatic aberration |
| Geometry | Rotation (90° steps), Straighten, perspective V/H, aspect, scale, offset, flips |
| Effects | Post-crop vignette (amount · midpoint · roundness · feather · highlights), grain |
| Calibration | Shadow tint, R/G/B primary hue & saturation |
06 Masking spec
Add a mask and every control in the app — tone, curves, colour mixer, colour grading, detail, effects, calibration — applies inside it and nowhere else.
| Mask | What it selects |
|---|---|
| Radial | An ellipse you move, resize and rotate — vignettes, drawing the eye |
| Linear Gradient | A straight falloff — skies, foregrounds, raking light |
| Brush | Painted by hand; Alt-drag erases, wheel resizes |
| Color Range | Everything matching a colour you pick off the photo |
| Luminance Range | Everything inside a brightness band |
- Per-mask controls
- Feather, opacity, invert, show/hide
- Stack limit
- Up to 8 masks per photo
- Coordinates
- Source-image coordinates — stay glued through crop, straighten, 90° rotation, perspective and lens distortion
- Rendering
- Each mask gets a complete pipeline pass of its own, composited over the global result by coverage
Select a mask and the develop panels turn blue and start writing into it; deselect and they go back to the whole photo. A mask is seeded from the photo's settings at the moment it's created, so its sliders start where the picture already is — later changes to the global settings do not flow into an existing mask.
The test suite asserts confinement exactly: at the centre of a mask the result equals the fully-adjusted value, and a sample outside it is identical to the untouched original — not close, identical.
07 Healing spec
Click a blemish and it is repaired; drag to choose where the repair comes from. Q opens the tool, Shift+Q toggles Heal ↔ Clone.
- Heal
- Blend with background — keeps the source's texture, replaces colour and brightness with the destination's
- Clone
- Replace with similar — the source area is copied verbatim
- Formula
- source − blur(source) + blur(destination)
- Auto source
- Ring matching around the spot against candidate positions, rejecting overlaps with the spot, other spots, or the frame edge
- Spot limit
- Up to 32 spots per photo, all applied on the GPU, all fully reversible
- Coordinates
- Source-image coordinates — stay stuck through crop, straighten, 90° rotation, perspective and lens distortion
The difference between Heal and Clone is easy to see on a gradient: healing from a much darker area onto a bright background reads 196 against a 198 background (invisible), while cloning the same source reads 42 — an obvious dark blotch. That comparison is an assertion in the test suite.
Brush size follows the mouse wheel or [ / ]; feather and opacity are set per spot; Del removes the selected spot.
08 Auto adjust
The Auto ▾ button in the toolbar, or its shortcuts, offer three different solves.
| Mode | Shortcut | What it does |
|---|---|---|
| Whole photo | Ctrl+U | Balances tone across the frame and neutralises any colour cast |
| From a subject / area… | Ctrl+Alt+U | Drag a box around your subject; exposure and highlight/shadow recovery come from that selection's own distribution |
| From a white / neutral area… | Ctrl+Shift+W | Drag a box over something neutral; white balance is solved directly, then exposure set so the reference sits just below clipping |
The neutral-area solve is a true solve, not a guess: on a blue-cast test frame it moves the blue-minus-red difference from +59 to −4.
09 Styles
29 built-in looks in the Styles panel, grouped into seven categories.
| Group | Named examples |
|---|---|
| Essentials | Punch, Soft Light, Crisp Detail, Flat / Log, HDR Natural |
| Film | Kodachrome, Portra 400, Faded Film, Ektar Vivid, Tungsten Night |
| Cinematic | Teal & Orange, Bleach Bypass, Golden Hour |
| Portrait | Clean Skin |
| Landscape | Vivid Landscape |
| Black & White | Red Filter B&W |
| Creative | Cross Process |
A Strength slider scales the whole look: numeric settings interpolate from their defaults toward the style's values, so 50% really is half the look rather than a different one. Switching styles cleans up whatever the previous one touched, and Clear returns those controls to their defaults — a style is never a trap. Styles are separate from presets: presets capture your photo's exact settings, styles are reusable starting points.
10 Library & culling
- Import a folder (optionally including subfolders) of RAW and JPEG/PNG/TIFF files
- Grid and filmstrip with lazily-decoded, disk-cached thumbnails
- Rate 0–5 stars, flag pick/reject, colour-label red→purple
- Auto-advance mode — a rating keystroke moves straight to the next frame
- Filter by minimum rating, flag, label, text; sort by name/date/rating/flag/size
- RAW+JPEG pairs shot together are detected and grouped
11 Crop, geometry & zoom
Crop — C
Drag the rectangle or its eight handles, lock to an aspect ratio (1:1, 4:5, 3:2, 16:9, 21:9, original, free), straighten with the angle slider, rotate in 90° steps, flip orientation, and choose an overlay (thirds, golden ratio, grid, diagonal, centre cross). Enter applies, Esc cancels. The crop travels with the photo through rotation, copy/paste and export.
Zoom
Fit / Fill / 1:1 / 2:1 / 4:1 buttons, + / - stepping, Z for 100%, Space to toggle fit↔100%, cursor-anchored scroll-wheel zoom, drag to pan, and a navigator thumbnail showing (and letting you move) the visible region. The renderer only processes the visible region at screen resolution, so zooming to 1:1 costs the same as fit-to-window.
12 Display calibration
Ctrl+Shift+K. A view transform, never an edit.
Set your panel's white point (or dial R/G/B gains by eye against neutral strips), correct any shadow cast with black-point lifts, and match gamma against a striped reference. Halidra then compensates the preview so what you judge as neutral on your screen is neutral in the actual file.
Exported pixels are never compensated — asserted in the test suite, which renders the same photo through both paths and checks the export is bit-identical with calibration on and off while the preview measurably changes. This is calibration by eye, not measurement; if you own a colorimeter, profile the display with that and leave this switched off.
13 Copy / paste / sync / presets / history
| Action | Shortcut |
|---|---|
| Copy Settings | Ctrl+Shift+C |
| Paste | Ctrl+Shift+V |
| Paste Settings (choose…) — checklist of just white balance, tone, etc. | Ctrl+Alt+V |
| Paste From Previous Photo | Ctrl+Alt+P |
| Sync to selected (same checklist as Paste choose) | Ctrl+Shift+S |
Presets save the whole look for reuse; a per-photo history list lets you step back through edits. Favourites: drag any control's label out of a develop panel into a Favourites box in the right panel — reorder within a box, drag between boxes, add and rename boxes. A favourited slider is a live copy, in sync with the original, and the layout persists between sessions.
14 Keyboard
Ctrl+K opens the shortcut editor: 576 bindable commands, searchable and filtered by category. Every control is assignable — each slider gets Increase / Decrease / Reset / Focus / Add to Favorites; toggles get Toggle; dropdowns get Next/Previous Option. Record a combination with + Add. Conflicts show in red and the new binding takes the key. Multiple keys per command are allowed, and the keymap survives upgrades.
| Ctrl+O / Ctrl+Shift+O | Import folder / with subfolders |
| G E D | Grid · Loupe · Develop |
| 0–5 | Set rating · , . decrease/increase |
| P X U | Pick · Reject · Unflag |
| 6–9, Shift+9 | Colour labels |
| ← → | Previous / next photo |
| Z / Shift+Z / Space | 100% · 200% · toggle fit↔100% |
| Ctrl+0 / Ctrl+Shift+0 | Fit · Fill |
| C | Crop tool (Enter apply, Esc cancel) |
| Q / Shift+Q | Heal tool · toggle Heal↔Clone |
| M / Shift+M / Alt+M | Masks panel · add radial · add brush |
| O | Toggle mask overlay |
| Ctrl+U / Ctrl+Alt+U / Ctrl+Shift+W | Auto: whole photo · from subject · from white |
| [ ] / Del | Brush smaller · larger · delete selected spot |
| Ctrl+Shift+K | Calibrate display |
| Y / Alt+Y | Before/After split (side-by-side / top-bottom) |
| J | Clipping warnings |
| Ctrl+Shift+C / Ctrl+Shift+V / Ctrl+Alt+V | Copy · Paste · Paste (choose) |
| Ctrl+Alt+P / Ctrl+Shift+S | Paste from previous · Sync to selected |
| Ctrl+U / Ctrl+Shift+U | Auto Tone · Auto White Balance |
| Ctrl+Z / Ctrl+Y | Undo · Redo |
| Alt+1…Alt+9 | Jump to a develop panel |
| F7 F8 F9 F6 | Toggle left · right · favourites · filmstrip |
| Ctrl+E | Export |
| Ctrl+K | Shortcut editor |
15 Export formats
Ctrl+E exports the selected photos, rendered through the same GPU pipeline at full resolution, with quality and long-edge resize. The originals are untouched in every case.
| Format | Notes |
|---|---|
| JPEG | 8-bit, adjustable quality |
| PNG | 8-bit lossless |
| WebP | 8-bit, adjustable quality |
| TIFF 8-bit | Uncompressed RGB, universally accepted |
| TIFF 16-bit | Uncompressed RGB, read straight off the float render target — for print or further retouching |
16 Data & storage
- Catalog
- .halidra/catalog.json
- Location
- Beside the photos
- Originals
- Opened read-only, never written to
- Legacy catalogs
- .aperture/
Ratings, flags, labels, crops, healing spots and every develop setting live in the catalog and are baked into pixels only when you export. Catalogs written by earlier versions under .aperture/ are still read, and favourites, shortcuts, presets and calibration are migrated across on first launch, so nothing is lost in the rename.
17 Quality assurance
76 end-to-end checks boot the real renderer headlessly and cover WebGL initialisation, folder import, thumbnail and full-image decode, that exposure / white balance / saturation / tone curve / clarity / sharpening / vignette / grain actually change rendered pixels, rating and catalog round-trips, keyboard dispatch and rebinding, favourites drag targets and cross-copy sync, crop geometry, 90° rotation with crop remapping, copy/paste, undo/redo, zoom and pan clamping, image orientation, mouse-wheel control of sliders/toggles/dropdowns, healing, styles and strength scaling, display calibration reaching the preview but never the export, TIFF structure and bit depth, masking confinement, all three auto-adjust modes, and export in all five formats. Screenshots are written to test/screenshots/ on every run.
Per-vendor RAW suite
A separate suite runs against ~500 MB of CC0 RAW samples from raw.pixls.us — the same database darktable and RawTherapee regression-test against. Opening, reading metadata and rendering the preview are pass/fail; sensor decode for vendors not yet covered is reported as a named gap, not a failure, and a gap becomes a pass once the codec lands.
LibRaw reference comparison
LibRaw's unprocessed_raw writes the mosaiced sensor plane with nothing else applied, giving an exact pixel-level answer to compare against — catching errors a preview-correlation check alone would miss (this is what caught a Sony plane sitting two bits low, which still correlated at 0.98 against the preview).
Correctness without reference renders
A new decoder is correlated against the camera's own preview — a different rendering of the same frame — over a 24×16 grid, deliberately coarse enough to be fair (a correct decoder scores 0.98) but fine enough to catch structural errors (a decoder with its rows shifted by one byte scores 0.79 at this grid, versus 0.96 at a too-coarse 4×4).
Packaged executables are verified with a self-test before release, and their signatures are verified rather than assumed.
18 Functional requirements
A representative slice of the product requirements, with an acceptance criterion the README states is asserted or observable for each.
| ID | Requirement | Acceptance criterion |
|---|---|---|
| FR-LIB-01 | Import a folder of RAW/JPEG/PNG/TIFF, optionally with subfolders | Imported files appear in Grid with cached thumbnails |
| FR-LIB-02 | Rate, flag and colour-label photos | 0–5 stars, pick/reject flags, red→purple labels persist to the catalog |
| FR-LIB-03 | Auto-advance after rating | A rating keystroke moves selection to the next frame |
| FR-LIB-04 | Filter and sort the library | Filter by rating/flag/label/text; sort by name/date/rating/flag/size |
| FR-LIB-05 | Detect RAW+JPEG pairs | Pairs shot together are grouped in the grid |
| FR-DEC-01 | Open Tier 1 (embedded preview) for any supported container | Preview located via container parsing, or a brute-force JPEG scan as fallback |
| FR-DEC-02 | Open Tier 2 (sensor decode) on demand | Ctrl+Alt+R switches the active photo to a 16-bit linear sensor render |
| FR-DEC-03 | Tier 2 decode correctness | Sensor render correlates against the camera's own preview over a 24×16 grid, and matches LibRaw's unprocessed_raw pixel-for-pixel where available |
| FR-CLR-01 | Fit a camera-RGB→sRGB matrix per photo | 3×3 matrix with each row summing to 1, fitted against the embedded preview |
| FR-CLR-02 | Fall back to per-channel gains | Gains used when the scene can't constrain a full matrix; reported in the sensor-decode line |
| FR-CLR-03 | Measure black level | Read from the optically shielded sensor border rather than assumed |
| FR-DEV-01 | Provide 100 develop controls across 10 groups | Profile, Basic, Tone Curve, Color Mixer, Color Grading, Detail, Optics, Geometry, Effects, Calibration panels all present |
| FR-DEV-02 | Every slider responds to the mouse wheel | Wheel adjusts the hovered control; Shift = fine step, Ctrl = coarse step |
| FR-DEV-03 | Before/After split view | Y / Alt+Y toggle side-by-side and top-bottom split |
| FR-DEV-04 | Clipping warnings | J toggles highlight/shadow clipping overlay |
| FR-MSK-01 | Five mask types available | Radial, Linear Gradient, Brush, Color Range, Luminance Range all selectable from the Masks panel |
| FR-MSK-02 | Every develop control works inside a mask | Selecting a mask redirects the whole develop panel, not a restricted subset |
| FR-MSK-03 | Mask confinement | Centre-of-mask sample equals the fully-adjusted value; outside-mask sample is identical to the untouched original |
| FR-MSK-04 | Stack up to 8 masks | Eight masks stack and composite on one photo |
| FR-MSK-05 | Masks survive geometry changes | Mask position is correct after crop, straighten, 90° rotation, perspective and lens distortion |
| FR-HEAL-01 | Heal and Clone modes | Heal keeps source texture and adopts destination colour/brightness; Clone copies the source verbatim |
| FR-HEAL-02 | Heal vs Clone gradient test | Heal from a darker source onto a 198 background reads 196; Clone of the same source reads 42 |
| FR-HEAL-03 | Automatic source selection | Ring-matching picks a continuation of the surroundings, rejecting overlap with the spot, other spots, or the frame edge |
| FR-HEAL-04 | Spot limit and reversibility | Up to 32 spots per photo, each independently deletable via Del |
| FR-AUTO-01 | Auto Tone (whole photo) | Ctrl+U balances tone and neutralises colour cast across the frame |
| FR-AUTO-02 | Auto from subject/area | Ctrl+Alt+U exposes for a dragged selection's own distribution |
| FR-AUTO-03 | Auto from neutral area (grey-card solve) | On a blue-cast test frame, blue-minus-red moves from +59 to −4 |
| FR-STY-01 | 29 built-in styles across 7 groups | Styles panel lists all 29, grouped and searchable |
| FR-STY-02 | Strength slider scales the look | 50% Strength interpolates every numeric setting halfway from default to the style's value |
| FR-STY-03 | Clear resets touched controls | Clear returns only the controls the style touched to their defaults |
| FR-CAL-01 | Display calibration affects preview only | Export output is bit-identical whether calibration is on or off, while the preview visibly changes |
| FR-CPY-01 | Copy/paste develop settings | Ctrl+Shift+C / Ctrl+Shift+V copy and paste the full setting set onto one or many photos |
| FR-CPY-02 | Selective paste and sync | Ctrl+Alt+V opens a checklist; Sync applies the same checklist to every other selected photo |
| FR-CROP-01 | Crop with aspect lock and overlays | 1:1, 4:5, 3:2, 16:9, 21:9, original and free ratios; thirds/golden ratio/grid/diagonal/centre-cross overlays |
| FR-CROP-02 | Crop travels with the photo | Crop region remains correct through rotation, copy/paste and export |
| FR-ZOOM-01 | Zoom levels and navigation | Fit / Fill / 1:1 / 2:1 / 4:1, Z, Space toggle, cursor-anchored wheel zoom, navigator thumbnail |
| FR-EXP-01 | Export to five formats | JPEG, PNG, WebP, TIFF 8-bit and TIFF 16-bit all produced from the GPU render target |
| FR-EXP-02 | Originals never modified by export | Source file bytes are unchanged after any export operation |
| FR-KEY-01 | 576 bindable commands | Shortcut editor (Ctrl+K) lists and allows rebinding of all 576 commands |
| FR-KEY-02 | Every control is assignable | Sliders expose Increase/Decrease/Reset/Focus/Add-to-Favorites; toggles expose Toggle; dropdowns expose Next/Previous Option |
| FR-KEY-03 | Keymap persistence across upgrades | User rebindings survive an upgrade; newly added commands keep factory defaults |
| NFR-DATA-01 | Originals are never written to | RAW and JPEG files are opened read-only for the lifetime of the app |
| NFR-DATA-02 | Legacy catalog migration | .aperture/ catalogs, favourites, shortcuts, presets and calibration are read and migrated on first launch |
| NFR-PERF-01 | Zoom cost is resolution-independent | Rendering 1:1 costs the same as fit-to-window — only the visible region is processed |
| NFR-PERF-02 | Tier 2 decode time is bounded and disclosed | A 26 MP frame takes about 13 seconds on one thread; the badge discloses which tier is active |
| NFR-QA-01 | End-to-end regression coverage | 76 checks run headlessly against the real renderer on every change |
| NFR-QA-02 | Per-vendor RAW regression coverage | Sony, Nikon, Panasonic and Fujifilm held to the Tier 1 guarantee; Tier 2 gaps reported by name, not silently failed |
| NFR-QA-03 | Packaged-build verification | Self-test boots the packaged .exe, checks WebGL init and non-black render output before release |
| NFR-SEC-01 | Release builds are code-signed | npm run verify:sign exits non-zero if any shipped executable is unsigned |