feat/vision-camera-v5
The five changes recipes-web landed on 2026-09-29 (34f8601,c0aaa67,bd57dd7,3b92e4e) only put two new fields into the shareable payload, but four of them change the look a given recipe produces — so both halves of the story belong in one note, next to doc 8 which was written against6814b05. What the note says: - The slider/store split. Web now reads EXPOSURE as stops (-5..+5, two decimals) and every develop knob as -100..+100, but paramDefs translates both ways and the stored units did not move: exposure is still +-10 units of 0.25 EV, every other knob is still +-10. Android can keep its +-10 sliders and still be byte-compatible, so the doc says that instead of asking for a UI port. - The one place a unit really does differ: masks[].exposure is the EV itself (MASK_EXPOSURE_MAX = 5) because the shader reads pow(2.0, a.x). Getting that wrong is a factor of four, so it gets its own row and its own acceptance test. - The recipe format did not change. The shareable object is still {name, baseFilter, adjustments, frameId, useGeotag}, adjustments still travels whole, and Android already keeps unknown fields through import -> Look -> export because only hslBands is sanitized. So masks survive web -> Android -> web today; that is worth an explicit round-trip test rather than being relied on as an accident. - The two new mask fields: temperature (2500..10000 K, NEUTRAL_K = 5500) and tint (+-10), resolved in readMasks so a mask stored before them carries a gain of exactly (1,1,1), plus the extra float4 wb[count] uniform and the one line c = clamp(c * wb, 0, 1) right after EXPOSURE. Gain is hoisted to JS on purpose: the Kelvin fit is a curve and a second copy of it in SkSL is a second answer. - whiteBalanceGain is the same arithmetic Android already has inline in getSkiaColorMatrix, so the port is a lift-and-shift with a bit-identical matrix as its acceptance test. One divergence is called out rather than papered over: gradientMask.ts still runs the old cg = clamp(lifted / max(l, 0.0004), 0.55, 1.35) for the tone knobs inside a mask, and its comment still claims that is TONE_SKSL's own gain — which stopped being true whenc0aaa67gave the frame-wide shader the hue-preserving knee and the five-knot ramp. A mask's HIGHLIGHT is therefore not the frame-wide HIGHLIGHT on a smaller area, and the note says which side of that the Android port should follow until the project owner decides. Verified: every file:line, commit hash and unit in the note was read back out of origin/recipes-web at3b92e4e(toneShader.ts, colorUtils.ts, paramDefs.ts, gradientMask.ts, types/index.ts, ToolRail.tsx, App.tsx, recipeShare.ts) and out of this branch (src/utils/colorUtils.ts:384-394, src/types/index.ts:81-105, src/utils/recipeShare.ts:59-110), including grep runs showing Android has no masks field at all. The measured WB numbers quoted (1.250 -> 1.724 inside the mask, 0.994 -> 0.994 outside) are the ones the studio's own mask-wb check produced against the live build. ponytail: no code touched, one file. The mask-tone alignment is left as an open question with both candidate behaviours written down, because choosing for the owner would bake a look decision into two codebases at once. Co-authored-by: PenguinHarness <noreply@penguin.local>
The file is empty.
Description
Languages
TypeScript
92.9%
Kotlin
5.7%
JavaScript
1.3%