AI-generated study image / before editing
DEVELOPMENT JOURNAL / 09
Photoshot
GITHUB × AI
Building a free Photoshop alternative

Beyond
black
& white.

Black & White did not require a new color-processing engine. We reused Photoshot’s HSL conversions and compositing, then connected color-specific gray calculations and sliders with AI. As reusable components accumulate, the scope of work needed for another editing tool becomes smaller.

PHOTOSHOT DEVELOPMENT JOURNAL / 09

AI-generated study photograph. Editor captures and outputs show Photoshot in use.

PhotoshotBuilding a free Photoshop alternative / 09FOUNDATION

From the source.

Existing color components, a different tool

Each new feature does not need its own external repository. Conversion, storage and compositing code already connected inside the application are reusable assets too. Black & White adds calculations and controls written with AI on top of those assets.

We did not find a record of importing a particular GitHub black-and-white filter for this feature. We reused the HSL conversion and shared compositor, then wrote the per-color settings and gray calculation with AI. GitHub is where that implementation can now be inspected.

Red, yellow, green, cyan, blue, and magenta are stored as color0 through color5. Their defaults are not all identical. The important feature is editable control over how each original color becomes gray.

Blending the two neighboring color controls

The code obtains the original hue h and multiplies it by six. The integer part i selects an interval; the fractional part f blends its two neighboring controls. A color between red and yellow responds to both sliders. Modulo arithmetic connects the final magenta interval back to red.

The resulting weight does not identify skin. It is a mixing coefficient based on the original color, so reddish skin and reddish clothing may respond in a similar direction.

Interpolation between six colors connects source color to slider settings. This AI-assisted code uses the existing HSL conversion and passes the gray result to the shared compositing path. Those boundaries show why the entire feature stack did not need rewriting.

src/adjustments.ts · v0.16.0
L149–160
Photoshot implementation ↗
blackwhite: {
  name: '흑백',
  description: '원본의 색상별 밝기를 조절하며 흑백으로 변환합니다.',
  controls: colorNames
    .slice(0, 6)
    .map((n, i) => control('color' + i, n, -100, 300, [40, 60, 40, 60, 20, 80][i]))
    .concat(
      control('tint', '색조 입히기', 0, 1),
      control('tintHue', '색조', 0, 360, 40),
      control('tintSaturation', '색조 채도', 0, 100, 25),
    ),
},
src/adjustments.ts · v0.16.0
L692–699
Photoshot implementation ↗
if (t === 'blackwhite') {
  const [h] = hsl(r, g, b, scratch),
    pos = h * 6,
    i = Math.floor(pos) % 6,
    f = pos - Math.floor(pos),
    weight = (v['color' + i] * (1 - f) + v['color' + ((i + 1) % 6)] * f) / 100;
  const gray = clamp(Math.min(r, g, b) + (Math.max(r, g, b) - Math.min(r, g, b)) * weight);
  out.fill(gray);
PhotoshotBuilding a free Photoshop alternative / 09IMPLEMENTATION

Inside the
calculation.

Keeping neutral gray while changing colored pixels

Gray is computed as the RGB minimum plus the difference between maximum and minimum multiplied by weight. For an already gray pixel, that difference is zero. With tint disabled, changing the color sliders therefore leaves the original neutral gray unchanged.

Pure red directly uses color0: a setting of 55 yields normalized gray 0.55, or about 140 in 8-bit output. Pure yellow at 60 yields 153; pure blue at 5 yields about 13. These are single-color examples of the formula, not whole-photo brightness measurements or Photoshop error figures.

Tint and masks work at different stages

out.fill(gray) assigns the same gray value to all three channels. If tint is enabled, the next step adds the chosen hue and saturation. Changing the black-and-white mix is thus separate from adding a tone such as sepia.

Masks and opacity are handled by the shared compositing stage. To change only a face’s black-and-white brightness, define that area with an adjustment-layer mask rather than expecting the color calculation to recognize a face.

PHOTOSHOT / SOURCE NOTES09

Read the code.

src/adjustments.ts · v0.16.0
L699–700
Photoshot implementation ↗
out.fill(gray);
if (v.tint) fromHsl(v.tintHue / 360, v.tintSaturation / 100, gray, out);
PhotoshotBuilding a free Photoshop alternative / 09IN PRACTICE

In the editor.

Actual Photoshot workspace. Settings and the image are shown together.
Actual Photoshot workspace. Settings and the image are shown together.

Recording all six example settings

The saved example uses Red 55, Yellow 60, Green 40, Cyan 60, Blue 5, and Magenta 80, with tint off. The original is AI-generated; the editor capture and exported result come from Photoshot.

AI’s value here was not originating the concept of grayscale conversion. It helped connect new mixing calculations and controls while retaining the responsibilities of existing code. A shared foundation makes each extension of the alternative editor more focused and manageable.

We record all six values so that the example is understandable without guessing omitted settings. This chapter documents a color-based black-and-white adjustment. The next chapter builds a color transform from a filter color and density.

PhotoshotBuilding a free Photoshop alternative / 09BEFORE / AFTER

The visible result.

AI-generated study image / before editing
01 / AI-generated study image / before editing
Photoshot output / edited result
02 / Photoshot output / edited result

AI-generated study photograph. Editor captures and outputs show Photoshot in use.